设计模式是软件开发中的最佳实践,帮助开发者在面对复杂设计问题时提供有效的解决方案。
面向对象设计原则为支持可维护性复用而诞生,这些原则蕴含在很多设计模式中,它们是从许多设计方案中总结出的指导性原则。
创建型模式关注于对象的创建,提供了更灵活的对象创建方式。
单例模式指一个类只有一个实例,并提供一个全局访问点。下面是 Java 中的一些实现方式:
优点:这种写法比较简单,就是在类装载的时候就完成实例化,避免了线程同步问题。
缺点:在类装载的时候就完成实例化,没有达到 Lazy Loading 的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
x1public class Singleton {
2
3 // 在类加载时就创建实例
4 private static final Singleton instance = new Singleton();
5
6 // 私有构造函数,防止外部实例化
7 private Singleton() {}
8
9 // 提供全局访问点
10 public static Singleton getInstance() {
11 return instance;
12 }
13}
14
这种方式和上面的方式类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候执行静态代码块中的代码,初始化类的实例。优缺点和上面一样。
181public class Singleton {
2 // 在类加载时创建实例
3 private static final Singleton instance;
4
5 // 静态代码块,在类加载时执行
6 static {
7 instance = new Singleton();
8 }
9
10 // 私有构造函数,防止外部实例化
11 private Singleton() {}
12
13 // 提供全局访问点
14 public static Singleton getInstance() {
15 return instance;
16 }
17}
18
这种写法起到了 Lazy Loading 的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了 if (instance == null) 判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
161public class Singleton {
2 // 类加载时不创建实例
3 private static Singleton instance;
4
5 // 私有构造函数,防止外部实例化
6 private Singleton() {}
7
8 // 提供全局访问点
9 public static Singleton getInstance() {
10 if (instance == null) {
11 instance = new Singleton();
12 }
13 return instance;
14 }
15}
16
解决上面线程不安全问题的方式是对 getInstance() 方法进行同步。但这种方式效率太低,每个线程在想获得类的实例时,执行getInstance() 方法都要进行同步。其实这个方法只需执行一次实例化代码,后面直接 return 实例化对象即可。
161public class Singleton {
2 // 类加载时不创建实例
3 private static Singleton instance;
4
5 // 私有构造函数,防止外部实例化
6 private Singleton() {}
7
8 // 提供全局访问点,使用同步方法保证线程安全
9 public static synchronized Singleton getInstance() {
10 if (instance == null) {
11 instance = new Singleton();
12 }
13 return instance;
14 }
15}
16
这种方式尝试使用同步代码块来提高效率,但仍无法保证线程安全。如果一个线程进入了 if (instance == null) 判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。
201public class Singleton {
2 // 类加载时不创建实例
3 private static Singleton instance;
4
5 // 私有构造函数,防止外部实例化
6 private Singleton() {}
7
8 // 提供全局访问点,尝试使用同步代码块
9 public static Singleton getInstance() {
10 if (instance == null) {
11 synchronized (Singleton.class) {
12 if (instance == null) {
13 instance = new Singleton();
14 }
15 }
16 }
17 return instance;
18 }
19}
20
Double-Check 概念对于多线程开发者来说不会陌生,进行了两次 if (instance == null) 检查,这样就可以保证线程安全。实例化代码只需执行一次,后面再次访问时,判断 if (instance == null),直接 return 实例化对象。
优点:线程安全;延迟加载;效率较高。
211public class Singleton {
2
3 // 类加载时不创建实例
4 private static volatile Singleton instance; // 【注意】和上面的区别是加了 volatile 关键字
5
6 // 私有构造函数,防止外部实例化
7 private Singleton() {}
8
9 // 提供全局访问点,使用双重检查锁定机制保证线程安全
10 public static Singleton getInstance() {
11 if (instance == null) {
12 synchronized (Singleton.class) {
13 if (instance == null) {
14 instance = new Singleton();
15 }
16 }
17 }
18 return instance;
19 }
20}
21
这种方式与饿汉式方式类似,都是采用类装载机制来保证实例化时只有一个线程。但不同的是,饿汉式在类装载时就实例化,没有 Lazy-Loading 效果,而静态内部类方式在需要实例化时才装载静态内部类,从而实例化 Singleton。类的静态属性只会在第一次加载类时初始化,所以这里JVM帮助保证了线程的安全性。
优点:避免了线程不安全,延迟加载,效率高。
151public class Singleton {
2 // 私有构造函数,防止外部实例化
3 private Singleton() {}
4
5 // 静态内部类,负责持有单例实例
6 private static class SingletonHolder {
7 private static final Singleton INSTANCE = new Singleton();
8 }
9
10 // 提供全局访问点,通过静态内部类实现延迟加载和线程安全
11 public static Singleton getInstance() {
12 return SingletonHolder.INSTANCE;
13 }
14}
15
创建枚举默认就是线程安全的,不需要担心 double checked locking,并且还能防止反序列化导致重新创建新的对象。枚举让JVM保证线程安全和单一实例问题,是 JDK1.5 版本后最适合用于创建单例设计模式的方法,是唯一一种不会被反射破坏单例状态的模式。
231// 使用枚举实现单例模式
2public enum Singleton {
3 // 定义一个枚举元素,即为单例实例
4 INSTANCE;
5
6 // 可以定义其他方法
7 public void someMethod() {
8 // 实现方法逻辑
9 }
10}
11
12// 测试枚举单例
13public class TestSingleton {
14 public static void main(String[] args) {
15
16 // 获取单例实例
17 Singleton singleton = Singleton.INSTANCE;
18
19 // 调用单例的方法
20 singleton.someMethod();
21 }
22}
23
定义:指通过复制现有的实例来创建新实例,而不是通过构造函数。
原理:实现 Cloneable 接口并重写 clone() 方法来复制对象。
优点:
减少创建新对象的开销:通过复制现有对象创建新对象。
动态配置对象:可以在运行时配置对象状态。
451// 原型接口
2public interface Prototype extends Cloneable {
3 Prototype clone();
4}
5
6// 具体原型类
7public class ConcretePrototype implements Prototype {
8 private String data;
9
10 public ConcretePrototype(String data) {
11 this.data = data;
12 }
13
14 // 使用浅拷贝
15
16 public Prototype clone() {
17 try {
18 return (Prototype) super.clone();
19 } catch (CloneNotSupportedException e) {
20 // 处理克隆不支持异常
21 return null;
22 }
23 }
24
25 public String getData() {
26 return data;
27 }
28
29 public void setData(String data) {
30 this.data = data;
31 }
32}
33
34// 客户端代码
35public class Client {
36 public static void main(String[] args) {
37 ConcretePrototype prototype = new ConcretePrototype("Prototype Data");
38 ConcretePrototype clonedPrototype = (ConcretePrototype) prototype.clone();
39 System.out.println(clonedPrototype.getData()); // 输出: Prototype Data
40 clonedPrototype.setData("Modified Data");
41 System.out.println(prototype.getData()); // 输出: Prototype Data
42 System.out.println(clonedPrototype.getData()); // 输出: Modified Data
43 }
44}
45
定义:使用多个简单的对象一步一步构建一个复杂的对象。
原理:定义一个建造者接口和具体建造者类,通过建造者类来创建复杂对象。
优点:
解耦:将复杂对象的构建与表示解耦。
灵活性:可以根据需要创建不同表示的复杂对象。
701// 产品类
2public class Product {
3 private String partA;
4 private String partB;
5
6 // 构造函数
7 public Product(String partA, String partB) {
8 this.partA = partA;
9 this.partB = partB;
10 }
11
12
13 public String toString() {
14 return "Product [partA=" + partA + ", partB=" + partB + "]";
15 }
16}
17
18// 建造者接口
19public interface Builder {
20 void buildPartA();
21 void buildPartB();
22 Product getResult();
23}
24
25// 具体建造者类
26public class ConcreteBuilder implements Builder {
27 private String partA;
28 private String partB;
29
30
31 public void buildPartA() {
32 partA = "Part A";
33 }
34
35
36 public void buildPartB() {
37 partB = "Part B";
38 }
39
40
41 public Product getResult() {
42 return new Product(partA, partB);
43 }
44}
45
46// 指导者类
47public class Director {
48 private Builder builder;
49
50 public Director(Builder builder) {
51 this.builder = builder;
52 }
53
54 public void construct() {
55 builder.buildPartA();
56 builder.buildPartB();
57 }
58}
59
60// 客户端代码
61public class Client {
62 public static void main(String[] args) {
63 Builder builder = new ConcreteBuilder();
64 Director director = new Director(builder);
65 director.construct();
66 Product product = builder.getResult();
67 System.out.println(product);
68 }
69}
70
定义:通过一个静态方法,根据传入的参数,返回不同类型的对象。
优点:客户端不需要知道具体产品类的类名,只需要知道一个参数即可创建产品实例,降低了客户端与具体产品类的耦合。
缺点:
增加新的产品时需要修改工厂类,违反了开放-关闭原则(OCP)。
简单工厂模式中的工厂类集中了所有产品的创建逻辑,职责过重,不利于代码维护。
使用场景:
当工厂类负责创建的对象比较少时。
客户端只需知道传入工厂类的参数即可创建对象时。
491// 产品接口
2public interface Product {
3 void use();
4}
5
6// 具体产品A
7public class ProductA implements Product {
8
9 public void use() {
10 System.out.println("Using Product A");
11 }
12}
13
14// 具体产品B
15public class ProductB implements Product {
16
17 public void use() {
18 System.out.println("Using Product B");
19 }
20}
21
22// 工厂类
23public class SimpleFactory {
24 // 静态方法,根据传入的参数创建不同的产品对象
25 public static Product createProduct(String type) {
26 switch (type) {
27 case "A":
28 return new ProductA();
29 case "B":
30 return new ProductB();
31 default:
32 throw new IllegalArgumentException("Unknown product type");
33 }
34 }
35}
36
37// 使用示例
38public class Main {
39 public static void main(String[] args) {
40 // 创建产品A
41 Product productA = SimpleFactory.createProduct("A");
42 productA.use();
43
44 // 创建产品B
45 Product productB = SimpleFactory.createProduct("B");
46 productB.use();
47 }
48}
49
定义:定义一个创建对象的接口,但由子类决定实例化哪个类。
原理:将对象的创建逻辑放在子类中,而不是在客户端代码中。
优点:
遵循开放-关闭原则,新增产品时只需增加相应的具体产品类和具体工厂类。
遵循单一职责原则,每个具体工厂类只负责创建对应的产品。
缺点:
增加了系统的复杂性,增加了类的数量。
使用场景:
一个类不知道它所需要的对象的类时。
一个类通过其子类来指定创建哪个对象时。
将创建对象的职责委托给多个工厂子类中的某一个,客户端在使用时通过具体工厂类来创建对象。
381// 工厂接口
2public interface Factory {
3 Product createProduct();
4}
5
6// 具体工厂A
7public class FactoryA implements Factory {
8
9 public Product createProduct() {
10 return new ProductA();
11 }
12}
13
14// 具体工厂B
15public class FactoryB implements Factory {
16
17 public Product createProduct() {
18 return new ProductB();
19 }
20}
21
22// 使用示例
23public class Main {
24 public static void main(String[] args) {
25 // 创建具体工厂A
26 Factory factoryA = new FactoryA();
27 // 通过工厂A创建产品A
28 Product productA = factoryA.createProduct();
29 productA.use();
30
31 // 创建具体工厂B
32 Factory factoryB = new FactoryB();
33 // 通过工厂B创建产品B
34 Product productB = factoryB.createProduct();
35 productB.use();
36 }
37}
38
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
原理:通过定义多个工厂接口,每个接口负责创建一组相关的对象。
优点:
分离了具体类的生成,符合开放-关闭原则。
可以在不修改已有代码的情况下增加新的产品系列。
通过一个工厂创建多个相关的产品对象,保证产品对象的一致性。
缺点:
增加了系统的复杂性,增加了类的数量。
使用场景:
一个系统需要独立于其产品创建、组合和表示时。
一个系统需要配置多个产品系列中的一个时。
需要强调一系列相关的产品对象一起使用时。
981// 抽象产品1
2public interface Product1 {
3 void use();
4}
5
6// 抽象产品2
7public interface Product2 {
8 void use();
9}
10
11// 具体产品A1
12public class ProductA1 implements Product1 {
13
14 public void use() {
15 System.out.println("Using Product A1");
16 }
17}
18
19// 具体产品A2
20public class ProductA2 implements Product2 {
21
22 public void use() {
23 System.out.println("Using Product A2");
24 }
25}
26
27// 具体产品B1
28public class ProductB1 implements Product1 {
29
30 public void use() {
31 System.out.println("Using Product B1");
32 }
33}
34
35// 具体产品B2
36public class ProductB2 implements Product2 {
37
38 public void use() {
39 System.out.println("Using Product B2");
40 }
41}
42
43// 抽象工厂
44public interface AbstractFactory {
45 Product1 createProduct1();
46 Product2 createProduct2();
47}
48
49// 具体工厂A
50public class FactoryA implements AbstractFactory {
51
52 public Product1 createProduct1() {
53 return new ProductA1();
54 }
55
56
57 public Product2 createProduct2() {
58 return new ProductA2();
59 }
60}
61
62// 具体工厂B
63public class FactoryB implements AbstractFactory {
64
65 public Product1 createProduct1() {
66 return new ProductB1();
67 }
68
69
70 public Product2 createProduct2() {
71 return new ProductB2();
72 }
73}
74
75// 使用示例
76public class Main {
77 public static void main(String[] args) {
78 // 创建具体工厂A
79 AbstractFactory factoryA = new FactoryA();
80
81 // 通过工厂A创建产品A1和A2
82 Product1 productA1 = factoryA.createProduct1();
83 Product2 productA2 = factoryA.createProduct2();
84 productA1.use();
85 productA2.use();
86
87 // 创建具体工厂B
88 AbstractFactory factoryB = new FactoryB();
89
90 // 通过工厂B创建产品B1和B2
91 Product1 productB1 = factoryB.createProduct1();
92 Product2 productB2 = factoryB.createProduct2();
93 productB1.use();
94 productB2.use();
95 }
96}
97
98
结构型模式关注如何将类或对象组合成更大的结构,以便更好地实现功能。
定义:将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本接口不兼容的类可以合作。
原理:通过引入一个适配器类,将目标接口转换为适配者接口。
优点:
接口兼容:使得接口不兼容的类可以协作。
复用性:可以复用现有的类。
351// 目标接口
2public interface Target {
3 void request();
4}
5
6// 适配者类
7public class Adaptee {
8 public void specificRequest() {
9 System.out.println("SpecificRequest");
10 }
11}
12
13// 适配器类
14public class Adapter implements Target {
15 private Adaptee adaptee;
16
17 public Adapter(Adaptee adaptee) {
18 this.adaptee = adaptee;
19 }
20
21
22 public void request() {
23 adaptee.specificRequest(); // 适配方法
24 }
25}
26
27// 客户端代码
28public class Client {
29 public static void main(String[] args) {
30 Adaptee adaptee = new Adaptee();
31 Target target = new Adapter(adaptee);
32 target.request(); // 通过适配器调用
33 }
34}
35
定义:将抽象部分与实现部分分离,使它们可以独立地变化。
原理:通过定义抽象类和实现类,将它们的变化解耦。
优点:
解耦:抽象和实现的解耦,使得它们可以独立变化。
灵活性:可以独立地扩展抽象和实现。
571// 实现接口
2public interface Implementor {
3 void operation();
4}
5
6// 具体实现A
7public class ConcreteImplementorA implements Implementor {
8
9 public void operation() {
10 System.out.println("ConcreteImplementorA operation");
11 }
12}
13
14// 具体实现B
15public class ConcreteImplementorB implements Implementor {
16
17 public void operation() {
18 System.out.println("ConcreteImplementorB operation");
19 }
20}
21
22// 抽象类
23public abstract class Abstraction {
24 protected Implementor implementor;
25
26 public Abstraction(Implementor implementor) {
27 this.implementor = implementor;
28 }
29
30 public abstract void operation();
31}
32
33// 扩展抽象类
34public class RefinedAbstraction extends Abstraction {
35 public RefinedAbstraction(Implementor implementor) {
36 super(implementor);
37 }
38
39
40 public void operation() {
41 implementor.operation(); // 委托给实现类
42 }
43}
44
45// 客户端代码
46public class Client {
47 public static void main(String[] args) {
48 Implementor implementorA = new ConcreteImplementorA();
49 Abstraction abstraction = new RefinedAbstraction(implementorA);
50 abstraction.operation();
51
52 Implementor implementorB = new ConcreteImplementorB();
53 abstraction = new RefinedAbstraction(implementorB);
54 abstraction.operation();
55 }
56}
57
定义:将对象组合成树形结构以表示部分-整体层次结构,使得客户端对单个对象和组合对象的使用具有一致性。
原理:通过定义一个组件接口,将叶子节点和容器节点统一处理。
优点:
一致性:对单个对象和组合对象的一致性操作。
简化客户端代码:客户端代码可以统一处理叶子节点和容器节点。
501import java.util.ArrayList;
2import java.util.List;
3
4// 组件接口
5public interface Component {
6 void operation();
7}
8
9// 叶子节点
10public class Leaf implements Component {
11
12 public void operation() {
13 System.out.println("Leaf operation");
14 }
15}
16
17// 容器节点
18public class Composite implements Component {
19 private List<Component> children = new ArrayList<>();
20
21 public void add(Component component) {
22 children.add(component);
23 }
24
25 public void remove(Component component) {
26 children.remove(component);
27 }
28
29
30 public void operation() {
31 for (Component child : children) {
32 child.operation();
33 }
34 }
35}
36
37// 客户端代码
38public class Client {
39 public static void main(String[] args) {
40 Composite root = new Composite();
41 Component leaf1 = new Leaf();
42 Component leaf2 = new Leaf();
43
44 root.add(leaf1);
45 root.add(leaf2);
46
47 root.operation(); // 统一调用
48 }
49}
50
定义:动态地给一个对象添加一些额外的职责。装饰器模式提供了比继承更灵活的扩展功能的方式。
原理:通过定义装饰器类来扩展被装饰对象的功能。
优点:
灵活性:可以动态地扩展对象的功能。
避免子类爆炸:通过装饰器而不是继承来扩展功能。
531// 组件接口
2public interface Component {
3 void operation();
4}
5
6// 具体组件
7public class ConcreteComponent implements Component {
8
9 public void operation() {
10 System.out.println("ConcreteComponent operation");
11 }
12}
13
14// 装饰器抽象类
15public abstract class Decorator implements Component {
16 protected Component component;
17
18 public Decorator(Component component) {
19 this.component = component;
20 }
21
22
23 public void operation() {
24 component.operation();
25 }
26}
27
28// 具体装饰器
29public class ConcreteDecorator extends Decorator {
30 public ConcreteDecorator(Component component) {
31 super(component);
32 }
33
34
35 public void operation() {
36 super.operation();
37 addedBehavior();
38 }
39
40 private void addedBehavior() {
41 System.out.println("ConcreteDecorator addedBehavior");
42 }
43}
44
45// 客户端代码
46public class Client {
47 public static void main(String[] args) {
48 Component component = new ConcreteComponent();
49 Component decorator = new ConcreteDecorator(component);
50 decorator.operation(); // 执行装饰后的操作
51 }
52}
53
定义:为子系统中的一组接口提供一个一致的界面,使得子系统更容易使用。
原理:通过定义一个外观类来封装子系统的复杂性,提供简化的接口。
优点:
简化使用:提供简单的接口来访问复杂的子系统。
解耦:将客户端与子系统解耦。
381// 子系统类A
2public class SubsystemA {
3 public void operationA() {
4 System.out.println("SubsystemA operationA");
5 }
6}
7
8// 子系统类B
9public class SubsystemB {
10 public void operationB() {
11 System.out.println("SubsystemB operationB");
12 }
13}
14
15// 外观类
16public class Facade {
17 private SubsystemA subsystemA;
18 private SubsystemB subsystemB;
19
20 public Facade() {
21 subsystemA = new SubsystemA();
22 subsystemB = new SubsystemB();
23 }
24
25 public void operation() {
26 subsystemA.operationA();
27 subsystemB.operationB();
28 }
29}
30
31// 客户端代码
32public class Client {
33 public static void main(String[] args) {
34 Facade facade = new Facade();
35 facade.operation(); // 通过外观类调用子系统
36 }
37}
38
定义:运用共享技术有效地支持大量细粒度的对象。
原理:通过将对象的共享部分与独享部分分开,将共享部分提取出来。
优点:
节省内存:通过共享来减少内存使用。
提高性能:减少对象创建和管理的开销。
471import java.util.HashMap;
2import java.util.Map;
3
4// 享元接口
5public interface Flyweight {
6 void operation(String extrinsicState);
7}
8
9// 具体享元类
10public class ConcreteFlyweight implements Flyweight {
11 private String intrinsicState;
12
13 public ConcreteFlyweight(String intrinsicState) {
14 this.intrinsicState = intrinsicState;
15 }
16
17
18 public void operation(String extrinsicState) {
19 System.out.println("Intrinsic State: " + intrinsicState + ", Extrinsic State: " + extrinsicState);
20 }
21}
22
23// 享元工厂
24public class FlyweightFactory {
25 private Map<String, Flyweight> flyweights = new HashMap<>();
26
27 public Flyweight getFlyweight(String key) {
28 Flyweight flyweight = flyweights.get(key);
29 if (flyweight == null) {
30 flyweight = new ConcreteFlyweight(key);
31 flyweights.put(key, flyweight);
32 }
33 return flyweight;
34 }
35}
36
37// 客户端代码
38public class Client {
39 public static void main(String[] args) {
40 FlyweightFactory factory = new FlyweightFactory();
41 Flyweight flyweight1 = factory.getFlyweight("A");
42 Flyweight flyweight2 = factory.getFlyweight("B");
43 flyweight1.operation("1");
44 flyweight2.operation("2");
45 }
46}
47
定义:为其他对象提供一种代理以控制对这个对象的访问。
原理:通过定义代理类来控制对真实对象的访问。
优点:
控制访问:可以在代理中实现对真实对象的控制。
增强功能:可以在代理中增加额外的功能,如延迟加载。
分类:
静态代理:为每一个真实角色编写对应的代理类,会导致类的数量倍增,维护成本提高。
动态代理:通过在内存生成虚拟类创建代理对象,可维护性好。
561/**
2 * 抽象对象
3 */
4public interface UserDao {
5 void save();
6}
7
8/**
9 * 真实对象(目标对象)
10 */
11public class UserDaoImpl implements UserDao {
12
13 public void save() {
14 System.out.println("----保存数据----");
15 }
16}
17
18/**
19 * 代理对象(静态代理)
20 */
21public class UserDaoProxy implements UserDao {
22 //接收保存目标对象
23 private UserDao target;
24
25 public UserDaoProxy(UserDao target) {
26 this.target = target;
27 }
28
29
30 public void save() {
31 System.out.println("开始事务...");
32 target.save();
33 System.out.println("提交事务...");
34 }
35}
36
37/**
38 * 测试类
39 */
40public class StaticProxyTest {
41 public static void main(String[] args) {
42 //目标对象
43 UserDaoImpl target = new UserDaoImpl();
44
45 //代理对象,把目标对象传给代理对象,建立代理关系
46 UserDaoProxy proxy = new UserDaoProxy(target);
47
48 // 通过代理对象执行方法
49 proxy.save();
50 }
51}
52
53开始事务...
54----已经保存数据!----
55提交事务...
56
231public class JdkProxyTest {
2
3 public static void main(String[] args) {
4 // 目标对象
5 UserDao target = new UserDaoImpl();
6
7 // 给目标对象创建代理对象(com.sun.proxy.$Proxy0类型)
8 UserDao proxy = (UserDao) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
9
10 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
11 System.out.println("开始事务...");
12 Object returnValue = method.invoke(target, args);
13 System.out.println("提交事务...");
14
15 return returnValue;
16 }
17 });
18
19 // 通过代理对象执行方法
20 proxy.save();
21 }
22}
23
221public class CGLibProxyTest {
2
3 public static void main(String[] args) {
4 // 目标对象
5 UserDao target = new UserDaoImpl();
6
7 // 给目标对象创建代理对象(UserDaoImpl@35bbe5e8类型)
8 UserDao proxy = (UserDao) Enhancer.create(target.getClass(), new InvocationHandler() {
9
10 public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
11 System.out.println("开始事务...");
12 Object returnValue = method.invoke(target, args);
13 System.out.println("提交事务...");
14
15 return returnValue;
16 }
17 });
18
19 // 通过代理对象执行方法
20 proxy.save();
21 }
22}
行为型模式关注对象之间的沟通和职责分配。
定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
原理:通过定义处理请求的链,并逐步将请求传递给链中的各个对象,直到找到合适的处理者。
优点:
解耦:发送者和接收者解耦。
灵活性:可以动态地添加或修改处理者。
481// 处理者接口
2public abstract class Handler {
3 protected Handler nextHandler;
4
5 public void setNextHandler(Handler nextHandler) {
6 this.nextHandler = nextHandler;
7 }
8
9 public abstract void handleRequest(String request);
10}
11
12// 具体处理者A
13public class ConcreteHandlerA extends Handler {
14
15 public void handleRequest(String request) {
16 if (request.equals("A")) {
17 System.out.println("Handler A handling request A");
18 } else if (nextHandler != null) {
19 nextHandler.handleRequest(request);
20 }
21 }
22}
23
24// 具体处理者B
25public class ConcreteHandlerB extends Handler {
26
27 public void handleRequest(String request) {
28 if (request.equals("B")) {
29 System.out.println("Handler B handling request B");
30 } else if (nextHandler != null) {
31 nextHandler.handleRequest(request);
32 }
33 }
34}
35
36// 客户端代码
37public class Client {
38 public static void main(String[] args) {
39 Handler handlerA = new ConcreteHandlerA();
40 Handler handlerB = new ConcreteHandlerB();
41 handlerA.setNextHandler(handlerB);
42
43 handlerA.handleRequest("A"); // 处理请求A
44 handlerA.handleRequest("B"); // 处理请求B
45 handlerA.handleRequest("C"); // 无处理者
46 }
47}
48
定义:将请求封装成一个对象,从而使你能够用不同的请求对客户进行参数化、队列化请求、以及支持可撤销操作。
原理:通过定义命令接口和具体命令类,将请求封装为对象,并将其传递给调用者。
优点:
解耦:发送者和接收者解耦。
灵活性:可以动态地创建、撤销请求。
501// 命令接口
2public interface Command {
3 void execute();
4}
5
6// 具体命令类
7public class ConcreteCommand implements Command {
8 private Receiver receiver;
9
10 public ConcreteCommand(Receiver receiver) {
11 this.receiver = receiver;
12 }
13
14
15 public void execute() {
16 receiver.action(); // 将请求委托给接收者
17 }
18}
19
20// 接收者类
21public class Receiver {
22 public void action() {
23 System.out.println("Receiver action");
24 }
25}
26
27// 调用者类
28public class Invoker {
29 private Command command;
30
31 public void setCommand(Command command) {
32 this.command = command;
33 }
34
35 public void invoke() {
36 command.execute();
37 }
38}
39
40// 客户端代码
41public class Client {
42 public static void main(String[] args) {
43 Receiver receiver = new Receiver();
44 Command command = new ConcreteCommand(receiver);
45 Invoker invoker = new Invoker();
46 invoker.setCommand(command);
47 invoker.invoke(); // 执行命令
48 }
49}
50
定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部表示。
原理:通过定义迭代器接口和具体迭代器类来遍历集合对象中的元素。
优点:
简化访问:提供统一的访问方式。
解耦:容器和迭代器解耦。
561import java.util.ArrayList;
2import java.util.List;
3
4// 迭代器接口
5public interface Iterator {
6 boolean hasNext();
7 Object next();
8}
9
10// 具体迭代器类
11public class ConcreteIterator implements Iterator {
12 private List<Object> items;
13 private int position;
14
15 public ConcreteIterator(List<Object> items) {
16 this.items = items;
17 }
18
19
20 public boolean hasNext() {
21 return position < items.size();
22 }
23
24
25 public Object next() {
26 return items.get(position++);
27 }
28}
29
30// 聚合类
31public class Aggregate {
32 private List<Object> items = new ArrayList<>();
33
34 public void add(Object item) {
35 items.add(item);
36 }
37
38 public Iterator iterator() {
39 return new ConcreteIterator(items);
40 }
41}
42
43// 客户端代码
44public class Client {
45 public static void main(String[] args) {
46 Aggregate aggregate = new Aggregate();
47 aggregate.add("Item 1");
48 aggregate.add("Item 2");
49
50 Iterator iterator = aggregate.iterator();
51 while (iterator.hasNext()) {
52 System.out.println(iterator.next());
53 }
54 }
55}
56
定义:定义一个对象来封装一组对象之间的交互,使得对象之间的耦合松散,从而使得它们可以独立地改变。
原理:通过定义中介者接口和具体中介者类来协调对象之间的交互。
优点:
降低耦合:将对象间的交互集中在中介者中。
易于维护:中介者可以集中处理复杂的交互逻辑。
831// 中介者接口
2public interface Mediator {
3 void notify(Component sender, String event);
4}
5
6// 具体中介者类
7public class ConcreteMediator implements Mediator {
8 private ComponentA componentA;
9 private ComponentB componentB;
10
11 public void setComponentA(ComponentA componentA) {
12 this.componentA = componentA;
13 }
14
15 public void setComponentB(ComponentB componentB) {
16 this.componentB = componentB;
17 }
18
19
20 public void notify(Component sender, String event) {
21 if (sender == componentA) {
22 componentB.handleEvent(event);
23 } else if (sender == componentB) {
24 componentA.handleEvent(event);
25 }
26 }
27}
28
29// 组件接口
30public abstract class Component {
31 protected Mediator mediator;
32
33 public Component(Mediator mediator) {
34 this.mediator = mediator;
35 }
36
37 public abstract void handleEvent(String event);
38}
39
40// 具体组件A
41public class ComponentA extends Component {
42 public ComponentA(Mediator mediator) {
43 super(mediator);
44 }
45
46
47 public void handleEvent(String event) {
48 System.out.println("ComponentA handling event: "
49
50 + event);
51 }
52
53 public void triggerEvent(String event) {
54 mediator.notify(this, event);
55 }
56}
57
58// 具体组件B
59public class ComponentB extends Component {
60 public ComponentB(Mediator mediator) {
61 super(mediator);
62 }
63
64
65 public void handleEvent(String event) {
66 System.out.println("ComponentB handling event: " + event);
67 }
68}
69
70// 客户端代码
71public class Client {
72 public static void main(String[] args) {
73 ConcreteMediator mediator = new ConcreteMediator();
74 ComponentA componentA = new ComponentA(mediator);
75 ComponentB componentB = new ComponentB(mediator);
76 mediator.setComponentA(componentA);
77 mediator.setComponentB(componentB);
78
79 componentA.triggerEvent("Event A");
80 componentB.handleEvent("Event B");
81 }
82}
83
定义:在不暴露对象内部状态的情况下,捕获一个对象的内部状态,并在该对象外部保存这个状态。可以在以后将对象恢复到保存的状态。
原理:通过定义备忘录类来保存对象的状态,并通过发起人类和恢复者类来实现状态的恢复。
优点:
状态恢复:可以在需要的时候恢复对象的状态。
封装性:不暴露对象的内部状态。
641// 备忘录类
2public class Memento {
3 private String state;
4
5 public Memento(String state) {
6 this.state = state;
7 }
8
9 public String getState() {
10 return state;
11 }
12}
13
14// 发起人类
15public class Originator {
16 private String state;
17
18 public void setState(String state) {
19 this.state = state;
20 }
21
22 public String getState() {
23 return state;
24 }
25
26 public Memento saveStateToMemento() {
27 return new Memento(state);
28 }
29
30 public void getStateFromMemento(Memento memento) {
31 state = memento.getState();
32 }
33}
34
35// 管理者类
36public class Caretaker {
37 private Memento memento;
38
39 public void saveMemento(Memento memento) {
40 this.memento = memento;
41 }
42
43 public Memento retrieveMemento() {
44 return memento;
45 }
46}
47
48// 客户端代码
49public class Client {
50 public static void main(String[] args) {
51 Originator originator = new Originator();
52 Caretaker caretaker = new Caretaker();
53
54 originator.setState("State1");
55 caretaker.saveMemento(originator.saveStateToMemento());
56
57 originator.setState("State2");
58 System.out.println("Current State: " + originator.getState());
59
60 originator.getStateFromMemento(caretaker.retrieveMemento());
61 System.out.println("Restored State: " + originator.getState());
62 }
63}
64
定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
原理:通过定义解释器类和表达式类,将文法规则和解释逻辑分开。
优点:
易于扩展:可以通过增加新的终结符和非终结符来扩展语法。
灵活性:可以定义复杂的语言规则。
481import java.util.HashMap;
2import java.util.Map;
3
4// 表达式接口
5public interface Expression {
6 int interpret(Map<String, Integer> context);
7}
8
9// 终结符表达式
10public class NumberExpression implements Expression {
11 private int number;
12
13 public NumberExpression(int number) {
14 this.number = number;
15 }
16
17
18 public int interpret(Map<String, Integer> context) {
19 return number;
20 }
21}
22
23// 非终结符表达式
24public class PlusExpression implements Expression {
25 private Expression left;
26 private Expression right;
27
28 public PlusExpression(Expression left, Expression right) {
29 this.left = left;
30 this.right = right;
31 }
32
33
34 public int interpret(Map<String, Integer> context) {
35 return left.interpret(context) + right.interpret(context);
36 }
37}
38
39// 客户端代码
40public class Client {
41 public static void main(String[] args) {
42 Expression expression = new PlusExpression(new NumberExpression(5), new NumberExpression(3));
43 Map<String, Integer> context = new HashMap<>();
44 int result = expression.interpret(context);
45 System.out.println("Result: " + result); // 输出结果 8
46 }
47}
48
定义:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
原理:通过定义状态接口和具体状态类,将对象的状态和行为分开,使得状态改变时可以改变行为。
优点:
状态独立:每个状态都有自己的行为。
易于扩展:可以增加新的状态而不改变现有代码。
491// 状态接口
2public interface State {
3 void handle(Context context);
4}
5
6// 具体状态A
7public class ConcreteStateA implements State {
8
9 public void handle(Context context) {
10 System.out.println("Handling state A");
11 context.setState(new ConcreteStateB()); // 切换到状态B
12 }
13}
14
15// 具体状态B
16public class ConcreteStateB implements State {
17
18 public void handle(Context context) {
19 System.out.println("Handling state B");
20 context.setState(new ConcreteStateA()); // 切换到状态A
21 }
22}
23
24// 上下文类
25public class Context {
26 private State state;
27
28 public Context(State state) {
29 this.state = state;
30 }
31
32 public void setState(State state) {
33 this.state = state;
34 }
35
36 public void request() {
37 state.handle(this);
38 }
39}
40
41// 客户端代码
42public class Client {
43 public static void main(String[] args) {
44 Context context = new Context(new ConcreteStateA());
45 context.request(); // 处理状态A
46 context.request(); // 处理状态B
47 }
48}
49
定义:定义一系列算法,将每一个算法封装起来,并使它们可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
原理:通过定义策略接口和具体策略类,将算法封装为对象,并在运行时选择使用。
优点:
灵活性:可以动态选择算法。
易于扩展:可以新增策略而不影响现有代码。
461// 策略接口
2public interface Strategy {
3 void execute();
4}
5
6// 具体策略A
7public class ConcreteStrategyA implements Strategy {
8
9 public void execute() {
10 System.out.println("Executing strategy A");
11 }
12}
13
14// 具体策略B
15public class ConcreteStrategyB implements Strategy {
16
17 public void execute() {
18 System.out.println("Executing strategy B");
19 }
20}
21
22// 上下文类
23public class Context {
24 private Strategy strategy;
25
26 public void setStrategy(Strategy strategy) {
27 this.strategy = strategy;
28 }
29
30 public void executeStrategy() {
31 strategy.execute();
32 }
33}
34
35// 客户端代码
36public class Client {
37 public static void main(String[] args) {
38 Context context = new Context();
39 context.setStrategy(new ConcreteStrategyA());
40 context.executeStrategy(); // 执行策略A
41
42 context.setStrategy(new ConcreteStrategyB());
43 context.executeStrategy(); // 执行策略B
44 }
45}
46
定义:定义一个操作中的算法的骨架,将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下重新定义算法中的某些步骤。
原理:通过定义模板方法在父类中,并将一些步骤的实现延迟到子类中。
优点:
复用性:将公共算法逻辑放在父类中。
灵活性:子类可以改变某些步骤的实现而不改变算法结构。
501// 抽象类
2public abstract class AbstractClass {
3 // 模板方法
4 public final void templateMethod() {
5 step1();
6 step2();
7 step3();
8 }
9
10 // 具体步骤1
11 private void step1() {
12 System.out.println("Step 1");
13 }
14
15 // 具体步骤2,留给子类实现
16 protected abstract void step2();
17
18 // 具体步骤3
19 private void step3() {
20 System.out.println("Step 3");
21 }
22}
23
24// 具体类A
25public class ConcreteClassA extends AbstractClass {
26
27 protected void step2() {
28 System.out.println("ConcreteClassA Step 2");
29 }
30}
31
32// 具体类B
33public class ConcreteClassB extends AbstractClass {
34
35 protected void step2() {
36 System.out.println("ConcreteClassB Step 2");
37 }
38}
39
40// 客户端代码
41public class Client {
42 public static void main(String[] args) {
43 AbstractClass concreteClassA = new ConcreteClassA();
44 concreteClassA.templateMethod(); // 执行具体类A的模板方法
45
46 AbstractClass concreteClassB = new ConcreteClassB();
47 concreteClassB.templateMethod(); // 执行具体类B的模板方法
48 }
49}
50
定义:表示一个作用于某对象结构中的各元素的操作,它可以在不改变元素类的前提下定义作用于这些元素的新操作。
原理:通过定义访问者接口和具体访问者类,将操作和对象结构分离,使得可以在不改变对象结构的情况下增加新的操作。
优点:
扩展性:可以在不改变对象结构的情况下增加新的操作。
操作集中:操作被集中在访问者中,使得相关操作更易于维护。
861import java.util.ArrayList;
2import java.util.List;
3
4// 访问者接口
5public interface Visitor {
6 void visit(ConcreteElementA elementA);
7 void visit(ConcreteElementB elementB);
8}
9
10// 具体访问者A
11public class ConcreteVisitorA implements Visitor {
12
13 public void visit(ConcreteElementA elementA) {
14 System.out.println("Visitor A visiting Element A");
15 }
16
17
18 public void visit(ConcreteElementB elementB) {
19 System.out.println("Visitor A visiting Element B");
20 }
21}
22
23// 具体访问者B
24public class ConcreteVisitorB implements Visitor {
25
26 public void visit(ConcreteElementA elementA) {
27 System.out.println("Visitor B visiting Element A");
28 }
29
30
31 public void visit(ConcreteElementB elementB) {
32 System.out.println("Visitor B visiting Element B");
33 }
34}
35
36// 元素接口
37public interface Element {
38 void accept(Visitor visitor);
39}
40
41// 具体元素A
42public class ConcreteElementA implements Element {
43
44 public void accept(Visitor visitor) {
45 visitor.visit(this);
46 }
47}
48
49// 具体元素B
50public class ConcreteElementB implements Element {
51
52 public void accept(Visitor visitor) {
53 visitor.visit(this);
54 }
55}
56
57// 对象结构
58public class ObjectStructure {
59 private List<Element> elements = new ArrayList<>();
60
61 public void addElement(Element element) {
62 elements.add(element);
63 }
64
65 public void accept(Visitor visitor) {
66 for (Element element : elements) {
67 element.accept(visitor);
68 }
69 }
70}
71
72// 客户端代码
73public class Client {
74 public static void main(String[] args) {
75 ObjectStructure structure = new ObjectStructure();
76 structure.addElement(new ConcreteElementA());
77 structure.addElement(new ConcreteElementB());
78
79 Visitor visitorA = new ConcreteVisitorA();
80 structure.accept(visitorA); // Visitor A visiting elements
81
82 Visitor visitorB = new ConcreteVisitorB();
83 structure.accept(visitorB); // Visitor B visiting elements
84 }
85}
86
定义:定义对象之间的一对多依赖,使得当一个对象改变状态时,所有依赖于它的对象都得到通知并被自动更新。
原理:通过定义观察者接口和被观察者类来实现一对多的通知机制。
优点:
解耦:观察者和被观察者之间的解耦。
动态更新:自动更新所有观察者。
661import java.util.ArrayList;
2import
3
4 java.util.List;
5
6// 观察者接口
7public interface Observer {
8 void update(String message);
9}
10
11// 被观察者接口
12public interface Subject {
13 void addObserver(Observer observer);
14 void removeObserver(Observer observer);
15 void notifyObservers(String message);
16}
17
18// 具体被观察者
19public class ConcreteSubject implements Subject {
20 private List<Observer> observers = new ArrayList<>();
21
22
23 public void addObserver(Observer observer) {
24 observers.add(observer);
25 }
26
27
28 public void removeObserver(Observer observer) {
29 observers.remove(observer);
30 }
31
32
33 public void notifyObservers(String message) {
34 for (Observer observer : observers) {
35 observer.update(message);
36 }
37 }
38}
39
40// 具体观察者
41public class ConcreteObserver implements Observer {
42 private String name;
43
44 public ConcreteObserver(String name) {
45 this.name = name;
46 }
47
48
49 public void update(String message) {
50 System.out.println(name + " received: " + message);
51 }
52}
53
54// 客户端代码
55public class Client {
56 public static void main(String[] args) {
57 ConcreteSubject subject = new ConcreteSubject();
58 Observer observer1 = new ConcreteObserver("Observer1");
59 Observer observer2 = new ConcreteObserver("Observer2");
60 subject.addObserver(observer1);
61 subject.addObserver(observer2);
62
63 subject.notifyObservers("Hello Observers!");
64 }
65}
66
UML 是 Unified Model Language 的缩写,中文是统一建模语言,是由一整套图表组成的标准化建模语言。分类如下:
类图是面向对象系统建模中最常用和最重要的图,是定义其它图的基础,主要用来表示类、接口以及它们之间的静态结构和关系。
在类图中,常见的有以下几种关系:
泛化关系是一种继承关系(is A),表示子类继承父类的所有特征和行为。
箭头指向:带三角箭头的实线,箭头指向父类。
实现关系是一种类与接口的实现关系(is A),表示类是接口所有特征和行为的实现。
箭头指向:带三角箭头的虚线表示,箭头指向接口。
组合关系是一种整体与部分的关系,且部分不能离开整体而单独存在。组合关系是关联关系的一种,是比聚合关系还要强的关系。
代码体现:成员变量。
箭头指向:带实心菱形和普通箭头的实线,实心菱形指向整体。
鸟是整体,翅膀是部分。鸟死了,翅膀也就不能飞了。所以是组合。
聚合关系是一种整体与部分的关系(has A),但部分可以离开整体而单独存在。聚合关系是关联关系的一种,是较强的关联关系;
代码体现:成员变量。
箭头指向:带空心菱形的实线,空心菱形指向整体。
电脑有键盘才能输入信息,电脑是整体,键盘是部分,键盘也可以离开电脑,单纯的拿去敲,所以是聚合。
关联关系是一种拥有关系(has A),它使得一个类知道另一个类的属性和方法。
代码体现:成员变量。
箭头指向:带普通箭头的实线,指向被拥有者。双向的关联可以有两个箭头,或者没有箭头。单向的关联有一个箭头。
车是车,人是人,没有整体与部分的关系。
依赖关系是一种使用关系(use A),即一个类的实现需要另一个类的协助。
代码体现:方法参数
箭头指向:带普通箭头的虚线,普通箭头指向被使用者。
老司机只管开车,车是谁的不重要,给什么车开什么车。
【概念】描绘了系统中组件提供的、需要的接口、端口等,以及它们之间的关系。
【目的】用来展示各个组件之间的依赖关系。
订单系统组件依赖于客户资源库和库存系统组件。中间的虚线箭头表示依赖关系。另外两个符号,表示组件连接器,一个提供接口,一个需要接口。
【概念】描述了系统内部的软件如何分布在不同的节点上。
【目的】用来表示软件和硬件的映射关系。
图中简单的表示,不同机器上面部署的不同软件。
【概念】对象图是类图的一个实例,是系统在某个时间点的详细状态的快照。
【目的】用来表示两个或者多个对象之间在某一时刻之间的关系。
图中就是描述的,某时间点 bat
这个公司有一个研发部,一个销售部,两个部门只有一个人iisheng
。
【概念】描绘了系统在包层面上的结构设计。
【目的】用来表示包和包之间的依赖关系。
“Use”关系:表示使用依赖,Web Shopping
依赖Payment
“Merge”关系:表示合并,Web Shopping
合并了Shopping Cart
就拥有了Shopping Cart
的功能
“Access”关系:表示私有引入,比如代码中的指定包名类名
“Import”关系:表示公共引入,比如Java中的import
之后,就可以直接使用import
包中的类了。
【概念】描述了一个"组合结构"的内部结构,以及他们之间的关系。这个"组合结构"可以是系统的一部分,或者一个整体。
【目的】用来表示系统中逻辑上的"组合结构"。
图中描述了Car
是由车轴连接着的两个前面轮子、两个后面轮子,和引擎组合的。
【概念】轮廓图提供了一种通用的扩展机制,用于为特定域和平台定制UML模型。
【目的】用于在特定领域中构建UML模型。
图中我们定义了一个简易的EJB
的概要图。Bean
是从Component
扩展来的。Entity Bean
和Session Bean
继承了Bean
。EJB
拥有Remote
和Home
接口,和JAR
包。
【概念】用例图是指由参与者、用例,边界以及它们之间的关系构成的用于描述系统功能的视图。
【目的】用来描述整个系统的功能。
用例图中包含以下三种关系:
包含关系使用符号include,想要查看订单列表,前提是需要先登录。
扩展关系使用符号extend,基于查询订单列表的功能,可以增加一个导出数据的功能
泛化关系,子用例继承父用例所有结构、行为和关系。
【概念】描述了具体业务用例的实现流程。
【目的】用来表示用例实现的工作流程。
图中简单描述了,从开始到登录到查看订单列表,或者登录失败直接结束。
【概念】状态机图对一个单独对象的行为建模,指明对象在它的整个生命周期里,响应不同事件时,执行相关事件的顺序。
【目的】用来表示指定对象,在整个生命周期,响应不同事件的不同状态。
图中描述了,门在其生命周期内所经历的状态。
【概念】序列图根据时间序列展示对象如何进行协作。它展示了在用例的特定场景中,对象如何与其他对象交互。
【目的】通过描述对象之间发送消息的时间顺序显示多个对象之间的动态协作。
图中展示的是支付宝条码支付场景的序列图。其中,loop
是循环,alt
是选择,序列图的其他关系这里就不介绍了。
【概念】描述了收发消息的对象的组织关系,强调对象之间的合作关系而不是时间顺序。
【目的】用来显示不同对象的关系。
图中展示了一个线上书店的通讯图,方框和小人表示生命线,不同生命线之间可以传递消息,消息前面的数字可以表达序列顺序。
【概念】交互概览图与活动图类似,但是它的节点是交互图。
【目的】提供了控制流的概述。
图中表示一个调度系统的交互概览图,跟活动图很像。其中sd
的框代表具体的交互流程,ref
框代表使用交互。
【概念】时序图用来显示随时间变化的一个或多个元素值或状态的更改。也显示时控事件之间的交互和管理它们的时间和期限约束。
【目的】用来表示元素状态或者值随时间的变化而变化的视图。
图中展示了老年痴呆病人随着时间的变化病情的变化。