目录
设计模式是一种解决特定问题的经验总结,是一种被反复使用的、经过验证的、可用于解决特定问题的代码设计经验的总结。设计模式不是一种具体的代码实现,而是一种通用的解决方案,可以在不同的场景中被应用。
设计模式可以分为三类:
- 创建型模式:用于创建对象的模式,包括工厂模式、抽象工厂模式、单例模式、建造者模式和原型模式。
- 结构型模式:用于描述如何将类或对象按某种布局组成更大的结构,包括适配器模式、桥接模式、组合模式、装饰器模式、外观模式、享元模式和代理模式。
- 行为型模式:用于描述对象之间的通信方式和协作方式,包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。
设计模式的优点包括:
- 提高代码的可重用性和可维护性。
- 提高代码的灵活性和扩展性。
- 降低代码的耦合度,使代码更易于理解和修改。
- 提高代码的可读性和可靠性。
- 促进代码的标准化和规范化。
1.单例模式
单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。单例模式的核心思想是将类的实例化操作封装在一个方法中,该方法会检查是否已经创建了实例,如果已经创建了实例,则直接返回该实例,否则创建一个新的实例并返回。
单例模式在实际应用中非常广泛,例如Spring框架中的Bean容器就是基于单例模式实现的。
单例模式的实现方式有多种,其中最常见的方式是使用静态变量和静态方法。具体实现方式如下:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
单例模式的优点包括:
- 保证一个类只有一个实例,避免了多个实例之间的冲突和资源浪费。
- 提供一个全局访问点,方便了对单例对象的访问和管理。
- 延迟加载,只有在需要时才会创建单例对象,提高了系统的性能和效率。
单例模式的缺点包括:
- 单例对象的职责过重,可能会导致单例对象的代码量过大,从而影响系统的性能和可维护性。
- 单例模式对扩展不友好,如果需要扩展单例对象的功能,可能需要修改现有的代码,从而影响系统的稳定性和可维护性。
2. 工厂模式
工厂模式是一种创建型设计模式,它提供了一种创建对象的方式,而无需指定具体的类。工厂模式将对象的创建过程封装在一个工厂类中,客户端只需要调用工厂类的方法即可创建对象,而无需关心对象的具体实现。
工厂模式的实现方式有多种,其中最常见的方式是使用静态工厂方法。具体实现方式如下:
public interface Product {
void use();
}
public class ProductA implements Product {
@Override
public void use() {
System.out.println("Product A is used.");
}
}
public class ProductB implements Product {
@Override
public void use() {
System.out.println("Product B is used.");
}
}
public class ProductFactory {
public static Product createProduct(String type) {
if ("A".equals(type)) {
return new ProductA();
} else if ("B".equals(type)) {
return new ProductB();
} else {
throw new IllegalArgumentException("Invalid product type.");
}
}
}
在上面的代码中,Product 是产品接口,ProductA 和 ProductB 是具体的产品实现类,ProductFactory 是工厂类,它提供了一个静态方法 createProduct(),根据传入的参数 type 创建不同的产品实例。
工厂模式的优点包括:
- 将对象的创建过程封装在工厂类中,客户端无需关心对象的具体实现,降低了客户端的耦合度。
- 可以通过工厂类控制对象的创建过程,实现对象的复用和统一管理。
- 可以通过工厂类实现对象的延迟加载和懒加载,提高了系统的性能和效率。
工厂模式的缺点包括:
- 工厂模式会增加代码的复杂度,需要额外编写工厂类。
- 工厂模式可能会导致系统中类的数量增加,增加了系统的复杂度和维护成本。
- 工厂模式不适用于需要频繁添加新产品的场景,需要修改工厂类的代码。
3.抽象工厂模式
抽象工厂模式是一种创建型设计模式,它提供了一种创建一系列相关或相互依赖对象的方式,而无需指定具体的类。抽象工厂模式将对象的创建过程封装在一个抽象工厂类中,客户端只需要调用抽象工厂类的方法即可创建一系列相关或相互依赖的对象,而无需关心对象的具体实现。
抽象工厂模式的实现方式有多种,其中最常见的方式是使用接口和实现类。具体实现方式如下:
public interface ProductA {
void use();
}
public interface ProductB {
void eat();
}
public class ProductA1 implements ProductA {
@Override
public void use() {
System.out.println("Product A1 is used.");
}
}
public class ProductA2 implements ProductA {
@Override
public void use() {
System.out.println("Product A2 is used.");
}
}
public class ProductB1 implements ProductB {
@Override
public void eat() {
System.out.println("Product B1 is eaten.");
}
}
public class ProductB2 implements ProductB {
@Override
public void eat() {
System.out.println("Product B2 is eaten.");
}
}
public interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
public class ConcreteFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ProductA1();
}
@Override
public ProductB createProductB() {
return new ProductB1();
}
}
public class ConcreteFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ProductA2();
}
@Override
public ProductB createProductB() {
return new ProductB2();
}
}
在上面的代码中,ProductA 和 ProductB 是产品接口,ProductA1、ProductA2、ProductB1 和 ProductB2 是具体的产品实现类,AbstractFactory 是抽象工厂接口,ConcreteFactory1 和 ConcreteFactory2 是具体的工厂实现类,它们分别实现了 AbstractFactory 接口,用于创建一系列相关或相互依赖的对象。
抽象工厂模式的优点包括:
- 将对象的创建过程封装在抽象工厂类中,客户端无需关心对象的具体实现,降低了客户端的耦合度。
- 可以通过抽象工厂类控制对象的创建过程,实现对象的复用和统一管理。
- 可以通过抽象工厂类实现对象的延迟加载和懒加载,提高了系统的性能和效率。
- 可以通过抽象工厂类实现一系列相关或相互依赖的对象的创建,保证了对象之间的一致性和完整性。
抽象工厂模式的缺点包括:
- 抽象工厂模式会增加代码的复杂度,需要额外编写抽象工厂类和具体工厂类。
- 抽象工厂模式可能会导致系统中类的数量增加,增加了系统的复杂度和维护成本。
- 抽象工厂模式不适用于需要频繁添加新产品的场景,需要修改抽象工厂类和具体工厂类的代码。
4.创造者模式
建造者模式是一种创建型设计模式,它将一个复杂对象的构建过程分解为多个简单对象的构建过程,然后将这些简单对象组合起来构建出复杂对象。建造者模式将对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式的实现方式有多种,其中最常见的方式是使用建造者接口和具体建造者类。具体实现方式如下:
public class Product {
private String partA;
private String partB;
private String partC;
public void setPartA(String partA) {
this.partA = partA;
}
public void setPartB(String partB) {
this.partB = partB;
}
public void setPartC(String partC) {
this.partC = partC;
}
public void show() {
System.out.println("Product: " + partA + ", " + partB + ", " + partC);
}
}
public interface Builder {
void buildPartA();
void buildPartB();
void buildPartC();
Product getResult();
}
public class ConcreteBuilder implements Builder {
private Product product = new Product();
@Override
public void buildPartA() {
product.setPartA("Part A");
}
@Override
public void buildPartB() {
product.setPartB("Part B");
}
@Override
public void buildPartC() {
product.setPartC("Part C");
}
@Override
public Product getResult() {
return product;
}
}
public class Director {
public void construct(Builder builder) {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
}
}
在上面的代码中,Product 是产品类,Builder 是建造者接口,ConcreteBuilder 是具体建造者类,Director 是指挥者类,它负责调用建造者接口的方法构建产品。
建造者模式的优点包括:
- 将对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
- 可以通过建造者接口控制对象的构建过程,实现对象的复用和统一管理。
- 可以通过建造者接口实现对象的延迟加载和懒加载,提高了系统的性能和效率。
建造者模式的缺点包括:
- 建造者模式会增加代码的复杂度,需要额外编写建造者接口和具体建造者类。
- 建造者模式可能会导致系统中类的数量增加,增加了系统的复杂度和维护成本。
- 建造者模式不适用于对象的构建过程比较简单的场景。
5.原型模式
原型模式是一种创建型设计模式,它通过复制现有对象来创建新对象,而无需再次调用构造函数。原型模式将对象的创建过程与其表示分离,使得同样的创建过程可以创建不同的表示。
原型模式的实现方式有多种,其中最常见的方式是使用 clone() 方法。具体实现方式如下:
public class Product implements Cloneable {
private String name;
public Product(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public Product clone() {
try {
return (Product) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
在上面的代码中,Product 是原型类,它实现了 Cloneable 接口,并重写了 clone() 方法,用于复制对象。
原型模式的优点包括:
- 可以通过复制现有对象来创建新对象,避免了重复调用构造函数的开销。
- 可以通过原型类控制对象的创建过程,实现对象的复用和统一管理。
- 可以通过原型类实现对象的延迟加载和懒加载,提高了系统的性能和效率。
原型模式的缺点包括:
- 原型模式需要实现 Cloneable 接口,并重写 clone() 方法,增加了代码的复杂度。
- 原型模式可能会导致对象的深拷贝和浅拷贝问题,需要注意对象的引用关系。
- 原型模式不适用于对象的构建过程比较复杂的场景。
6.适配器模式
适配器模式是一种结构型设计模式,它将一个类的接口转换成客户端所期望的另一个接口,从而使得原本不兼容的类可以协同工作。适配器模式通过封装、继承或对象组合等方式实现接口的转换。
适配器模式的实现方式有多种,其中最常见的方式是使用类适配器和对象适配器。具体实现方式如下:
public interface Target {
void request();
}
public class Adaptee {
public void specificRequest() {
System.out.println("Adaptee specific request.");
}
}
public class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest();
}
}
public class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
在上面的代码中,Target 是目标接口,Adaptee 是被适配的类,ClassAdapter 是类适配器,它继承了 Adaptee 类并实现了 Target 接口,ObjectAdapter 是对象适配器,它通过对象组合的方式持有 Adaptee 对象,并实现了 Target 接口。
适配器模式的优点包括:
- 可以将不兼容的类转换成客户端所期望的接口,提高了代码的复用性和灵活性。
- 可以通过适配器类控制被适配类的访问权限,实现对象的封装和保护。
- 可以通过适配器类实现对象的透明化,客户端无需关心对象的具体实现。
适配器模式的缺点包括:
- 适配器模式会增加代码的复杂度,需要额外编写适配器类。
- 适配器模式可能会导致系统中类的数量增加。
7.桥接模式
桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立地变化。桥接模式通过对象组合的方式将抽象部分和实现部分连接起来,从而实现了抽象部分和实现部分的解耦。
桥接模式的实现方式有多种,其中最常见的方式是使用抽象类和实现类。具体实现方式如下:
public interface Implementor {
void operationImpl();
}
public class ConcreteImplementorA implements Implementor {
@Override
public void operationImpl() {
System.out.println("Concrete Implementor A operation.");
}
}
public class ConcreteImplementorB implements Implementor {
@Override
public void operationImpl() {
System.out.println("Concrete Implementor B operation.");
}
}
public abstract class Abstraction {
protected Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public abstract void operation();
}
public class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
implementor.operationImpl();
}
}
在上面的代码中,Implementor 是实现部分接口,ConcreteImplementorA 和 ConcreteImplementorB 是具体的实现类,Abstraction 是抽象部分类,它持有一个 Implementor 对象,RefinedAbstraction 是具体的抽象部分类,它继承了 Abstraction 类并实现了 operation() 方法。
桥接模式的优点包括:
-
- 将抽象部分和实现部分分离,使它们可以独立地变化,提高了代码的灵活性和可扩展性。
- 可以通过抽象部分和实现部分的组合实现不同的功能,增加了系统的功能性和可用性。
- 可以通过抽象部分和实现部分的组合实现对象的复用,减少了代码的重复性和冗余性。
桥接模式的缺点包括:
-
- 桥接模式会增加代码的复杂度,需要额外编写抽象部分和实现部分的类和接口。
- 桥接模式可能会导致系统中类的数量增加,增加了系统的复杂度和维护成本。
- 桥接模式不适用于抽象部分和实现部分的变化频率不同的场景,因为这会导致抽象部分和实现部分的耦合度增加。
8.组合模式
组合模式是一种结构型设计模式,它将对象组合成树形结构,以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性,从而使得用户无需关心对象的具体类型,而只需要关心对象的结构和行为。
组合模式的实现方式有多种,其中最常见的方式是使用抽象类和实现类。具体实现方式如下:
public abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
public abstract void add(Component component);
public abstract void remove(Component component);
public abstract void display(int depth);
}
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public void add(Component component) {
System.out.println("Cannot add to a leaf.");
}
@Override
public void remove(Component component) {
System.out.println("Cannot remove from a leaf.");
}
@Override
public void display(int depth) {
System.out.println(String.format("%s%s", "-".repeat(depth), name));
}
}
public class Composite extends Component {
private List<Component> children = new ArrayList<>();
public Composite(String name) {
super(name);
}
@Override
public void add(Component component) {
children.add(component);
}
@Override
public void remove(Component component) {
children.remove(component);
}
@Override
public void display(int depth) {
System.out.println(String.format("%s%s", "-".repeat(depth), name));
for (Component component : children) {
component.display(depth + 2);
}
}
}
在上面的代码中,Component 是抽象类,它定义了对象的基本行为,Leaf 是叶子节点类,它实现了 Component 类的抽象方法,Composite 是组合节点类,它持有一个 Component 对象的列表,并实现了 Component 类的抽象方法。
组合模式的优点包括:
- 可以通过组合对象的方式实现对象的递归组合,提高了代码的复用性和可扩展性。
- 可以通过组合对象的方式实现对象的透明化,客户端无需关心对象的具体类型。
- 可以通过组合对象的方式实现对象的统一管理,减少了代码的重复性和冗余性。
组合模式的缺点包括:
- 组合模式会增加代码的复杂度,需要额外编写抽象类和实现类。
- 组合模式可能会导致系统中类的数量增加,增加了系统的复杂度和维护成本。
- 组合模式不适用于对象的行为差异较大的场景,因为这会导致对象的结构和行为的耦合度增加。
9.装饰器模式
装饰器模式是一种结构型设计模式,它允许在不改变对象自身的基础上,动态地扩展对象的功能。装饰器模式通过对象组合的方式,将装饰器对象嵌套在被装饰对象中,从而实现了对被装饰对象的功能扩展。
装饰器模式的实现方式有多种,其中最常见的方式是使用抽象类和实现类。具体实现方式如下:
public interface Component {
void operation();
}
public class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("Concrete Component operation.");
}
}
public abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("Concrete Decorator A operation.");
}
}
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("Concrete Decorator B operation.");
}
}
在上面的代码中,Component 是抽象组件类,ConcreteComponent 是具体组件类,Decorator 是抽象装饰器类,它持有一个 Component 对象,ConcreteDecoratorA 和 ConcreteDecoratorB 是具体装饰器类,它们继承了 Decorator 类并实现了 operation() 方法。
装饰器模式的优点包括:
- 可以动态地扩展对象的功能,提高了代码的灵活性和可扩展性。
- 可以通过装饰器对象的嵌套实现多个功能的组合,增加了系统的功能性和可用性。
- 可以通过装饰器对象的嵌套实现对象的透明化,客户端无需关心对象的具体实现。
装饰器模式的缺点包括:
- 装饰器模式会增加代码的复杂度,需要额外编写装饰器类。
- 装饰器模式可能会导致系统中类的数量增加,增加了系统的复杂度和维护成本。
- 装饰器模式不适用于对对象的行为进行大量修改的场景,因为这会导致装饰器对象的嵌套层数增加。
10.外观模式
外观模式是一种结构型设计模式,它提供了一个简单的接口,隐藏了系统的复杂性,使得客户端可以更加方便地使用系统。外观模式通过封装系统的复杂性,将系统的接口简化,从而提高了系统的可用性和可维护性。
外观模式的实现方式有多种,其中最常见的方式是使用一个外观类来封装系统的复杂性。具体实现方式如下:
public class SubsystemA {
public void operationA() {
System.out.println("Subsystem A operation.");
}
}
public class SubsystemB {
public void operationB() {
System.out.println("Subsystem B operation.");
}
}
public class SubsystemC {
public void operationC() {
System.out.println("Subsystem C operation.");
}
}
public class Facade {
private SubsystemA subsystemA;
private SubsystemB subsystemB;
private SubsystemC subsystemC;
public Facade() {
subsystemA = new SubsystemA();
subsystemB = new SubsystemB();
subsystemC = new SubsystemC();
}
public void operation() {
subsystemA.operationA();
subsystemB.operationB();
subsystemC.operationC();
}
}
在上面的代码中,SubsystemA、SubsystemB 和 SubsystemC 是子系统类,Facade 是外观类,它持有一个或多个子系统对象,并提供了一个简单的接口 operation(),隐藏了子系统的复杂性。
外观模式的优点包括:
- 可以隐藏系统的复杂性,提高了系统的可用性和可维护性。
- 可以通过外观类的封装实现系统的解耦,减少了系统的耦合度。
- 可以通过外观类的封装实现系统的统一管理,提高了系统的可管理性和可维护性。
外观模式的缺点包括:
- 外观模式可能会导致系统的功能不够灵活,因为外观类只提供了一个简单的接口。
- 外观模式可能会导致系统的性能下降,因为外观类需要处理大量的请求。
11.享元模式
享元模式是一种结构型设计模式,它通过共享对象来减少系统中对象的数量,从而提高系统的性能和可扩展性。享元模式将对象分为内部状态和外部状态,内部状态是可以共享的,而外部状态是不可以共享的。通过将内部状态共享,可以减少系统中对象的数量,从而提高系统的性能和可扩展性。
享元模式的实现方式有多种,其中最常见的方式是使用工厂类来管理共享对象。具体实现方式如下:
public interface Flyweight {
void operation(String extrinsicState);
}
public class ConcreteFlyweight implements Flyweight {
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation(String extrinsicState) {
System.out.println(String.format("Concrete Flyweight operation: %s, %s", intrinsicState, extrinsicState));
}
}
public class FlyweightFactory {
private Map<String, Flyweight> flyweights = new HashMap<>();
public Flyweight getFlyweight(String key) {
if (!flyweights.containsKey(key)) {
flyweights.put(key, new ConcreteFlyweight(key));
}
return flyweights.get(key);
}
}
在上面的代码中,Flyweight 是享元接口,ConcreteFlyweight 是具体享元类,它持有一个内部状态 intrinsicState,FlyweightFactory 是享元工厂类,它持有一个 Flyweight 对象的列表,并提供了一个 getFlyweight() 方法来获取共享对象。
享元模式的优点包括:
- 可以减少系统中对象的数量,提高了系统的性能和可扩展性。
- 可以通过共享对象的方式实现对象的复用,减少了代码的重复性和冗余性。
- 可以通过共享对象的方式实现对象的透明化,客户端无需关心对象的具体实现。
享元模式的缺点包括:
- 享元模式会增加代码的复杂度,需要额外编写工厂类和共享对象类。
- 享元模式可能会导致系统的功能不够灵活,因为共享对象的内部状态是不可变的。
- 享元模式不适用于对象的内部状态变化频繁的场景,因为这会导致共享对象的数量增加。
12.代理模式
代理模式是一种结构型设计模式,它允许通过代理对象来控制对另一个对象的访问。代理模式可以在不改变原始对象的情况下,增加一些额外的功能,例如权限控制、缓存、延迟加载等。
代理模式的实现方式有多种,其中最常见的方式是使用一个代理类来封装原始对象。具体实现方式如下:
public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("Real Subject request.");
}
}
public class Proxy implements Subject {
private RealSubject realSubject;
public Proxy() {
realSubject = new RealSubject();
}
@Override
public void request() {
System.out.println("Proxy request.");
realSubject.request();
}
}
在上面的代码中,Subject 是抽象主题类,RealSubject 是具体主题类,Proxy 是代理类,它持有一个 RealSubject 对象,并实现了 Subject 接口的方法。
代理模式的优点包括:
- 可以通过代理对象来控制对另一个对象的访问,提高了系统的安全性和可靠性。
- 可以通过代理对象来增加一些额外的功能,例如权限控制、缓存、延迟加载等。
- 可以通过代理对象来实现对象的透明化,客户端无需关心对象的具体实现。
代理模式的缺点包括:
- 代理模式会增加代码的复杂度,需要额外编写代理类。
- 代理模式可能会导致系统的性能下降,因为代理对象需要处理大量的请求。
- 代理模式不适用于对象的行为差异较大的场景,因为这会导致代理对象的代码复杂度增加。
13.责任链模式
责任链模式是一种行为型设计模式,它允许多个对象来处理同一个请求,从而避免了请求发送者和接收者之间的耦合关系。责任链模式将多个对象组成一条链,每个对象都可以处理请求,如果一个对象不能处理请求,则将请求传递给下一个对象,直到请求被处理为止。
责任链模式的实现方式有多种,其中最常见的方式是使用一个抽象处理器类和多个具体处理器类来实现责任链。具体实现方式如下:
public abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void handleRequest(int request);
}
public class ConcreteHandlerA extends Handler {
@Override
public void handleRequest(int request) {
if (request >= 0 && request < 10) {
System.out.println(String.format("%s handles request %d", getClass().getSimpleName(), request));
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
public class ConcreteHandlerB extends Handler {
@Override
public void handleRequest(int request) {
if (request >= 10 && request < 20) {
System.out.println(String.format("%s handles request %d", getClass().getSimpleName(), request));
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
public class ConcreteHandlerC extends Handler {
@Override
public void handleRequest(int request) {
if (request >= 20 && request < 30) {
System.out.println(String.format("%s handles request %d", getClass().getSimpleName(), request));
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
在上面的代码中,Handler 是抽象处理器类,ConcreteHandlerA、ConcreteHandlerB 和 ConcreteHandlerC 是具体处理器类,它们继承了 Handler 类并实现了 handleRequest() 方法。
责任链模式的优点包括:
- 可以将请求发送者和接收者解耦,提高了系统的灵活性和可扩展性。
- 可以通过责任链的方式实现请求的分发和处理,提高了系统的可维护性和可扩展性。
- 可以动态地添加或删除处理器,提高了系统的灵活性和可扩展性。
责任链模式的缺点包括:
- 可能会导致请求的处理时间过长,因为请求需要依次经过多个处理器。
- 可能会导致系统的性能下降,因为每个处理器都需要处理请求。
- 可能会导致系统的复杂度增加,因为需要额外编写处理器类和管理责任链。
14.命令模式
命令模式是一种行为设计模式,它将请求封装成一个对象,从而使您可以使用不同的请求、队列或日志来参数化其他对象。命令模式还支持可撤销操作。
在命令模式中,有四个主要的角色:
- 命令(Command):定义了执行操作的接口,包括一个执行方法和一个撤销方法。
- 具体命令(ConcreteCommand):实现了命令接口,包含了执行操作的具体实现。
- 调用者(Invoker):负责调用命令对象执行请求。
- 接收者(Receiver):执行命令所需的操作。
一个简单的例子是一个遥控器,它可以控制电视机、音响等设备。在这个例子中,遥控器就是调用者,电视机、音响等设备就是接收者,而遥控器上的按钮就是命令。
具体实现可以这样:
1.定义一个命令接口,包含执行方法和撤销方法。
public interface Command {
void execute();
void undo();
}
2.实现具体的命令,比如控制电视机的命令。
public class TVOnCommand implements Command {
private TV tv;
public TVOnCommand(TV tv) {
this.tv = tv;
}
public void execute() {
tv.on();
}
public void undo() {
tv.off();
}
}
3.定义调用者,比如遥控器。
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
public void pressUndo() {
command.undo();
}
}
4.定义接收者,比如电视机。
public class TV {
public void on() {
System.out.println("TV is on");
}
public void off() {
System.out.println("TV is off");
}
}
使用命令模式控制电视机。
TV tv = new TV();
TVOnCommand tvOnCommand = new TVOnCommand(tv);
RemoteControl remoteControl = new RemoteControl();
remoteControl.setCommand(tvOnCommand);
remoteControl.pressButton(); // 打开电视机
remoteControl.pressUndo(); // 关闭电视机
在上面代码中,遥控器上的按钮就是命令,遥控器就是调用者,电视机就是接收者。通过命令模式,我们可以将调用者和接收者解耦,遥控器只需要知道如何调用命令,而不需要知道命令的具体实现。同时,命令模式还支持撤销操作,可以回滚到之前的状态。
命令模式的优点包括:
- 降低系统的耦合度,将请求者和接收者解耦。
- 可以方便地添加新的命令,而无需修改现有的代码。
- 支持撤销操作,可以回滚到之前的状态。
命令模式的缺点包括:
- 命令的数量可能会增加,导致系统变得复杂。
- 命令模式可能会增加系统的开销,因为需要创建额外的对象。
- 如果命令的执行时间过长,可能会影响系统的性能。
15.解释器模式
解释器模式是一种行为设计模式,它定义了一种语言,用于表示特定的领域知识,并提供了一种解释器来解释这种语言中的语句。解释器模式通常用于编写编译器、解释器和正则表达式引擎等应用程序。
在解释器模式中,有两个主要的角色:
- 抽象表达式(AbstractExpression):定义了解释器的接口,包括一个解释方法。
- 具体表达式(ConcreteExpression):实现了抽象表达式接口,包含了解释方法的具体实现。
一个简单的例子是一个计算器,它可以解释简单的数学表达式。在这个例子中,表达式就是语言,解释器就是计算器。
具体实现可以这样:
定义一个抽象表达式接口,包含一个解释方法。
public interface Expression {
int interpret();
}
实现具体的表达式,比如加法表达式。
public class AddExpression implements Expression {
private Expression left;
private Expression right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
public int interpret() {
return left.interpret() + right.interpret();
}
}
定义终结符表达式,比如数字表达式。
public class NumberExpression implements Expression {
private int number;
public NumberExpression(int number) {
this.number = number;
}
public int interpret() {
return number;
}
}
定义解释器,比如计算器。
public class Calculator {
private Expression expression;
public Calculator(Expression expression) {
this.expression = expression;
}
public int calculate() {
return expression.interpret();
}
}
使用解释器模式计算表达式。
Expression expression = new AddExpression(new NumberExpression(1), new NumberExpression(2));
Calculator calculator = new Calculator(expression);
int result = calculator.calculate(); // 计算 1 + 2
在上面代码中,表达式就是语言,解释器就是计算器。通过解释器模式,我们可以扩展语言的语法,增加新的解释器,同时也可以实现解释器,每个解释器对象只需要实现一个解释方法即可。
解释器模式的优点包括:
- 可以扩展语言的语法,增加新的解释器。
- 易于实现文法,每个文法规则都可以表示为一个解释器对象。
- 易于实现解释器,每个解释器对象只需要实现一个解释方法即可。
解释器模式的缺点包括:
- 可能会导致类的数量增加,因为每个文法规则都需要一个解释器对象。
- 可能会导致性能问题,因为解释器需要递归地调用自己来解释语句。
- 可能会导致代码复杂度增加,因为需要实现多个解释器对象来表示不同的文法规则。
16.迭代器模式
迭代器模式是一种行为型设计模式,它允许客户端通过迭代器逐个访问聚合对象中的元素,而不必暴露聚合对象的内部表示。迭代器模式将迭代算法与聚合对象分离,使得它们可以独立地变化。
在迭代器模式中,聚合对象提供一个迭代器接口,该接口定义了访问聚合对象中元素的方法。迭代器对象实现了迭代器接口,并包含了迭代算法。客户端通过调用迭代器接口中的方法来访问聚合对象中的元素,而不必了解聚合对象的内部表示。
迭代器模式在Java中的应用非常广泛,例如Java中的集合框架就是基于迭代器模式实现的。
以下是一个简单的迭代器模式的例子:
假设我们有一个名为Book的类,它包含书的名称和作者。我们还有一个名为BookShelf的类,它表示一个书架,可以添加和删除书籍。我们希望能够遍历BookShelf中的所有书籍,但是我们不想暴露BookShelf的内部表示。因此,我们可以使用迭代器模式来实现这个功能。
首先,我们定义一个Iterator接口,它包含两个方法:hasNext()和next()。hasNext()方法用于检查是否还有下一个元素,next()方法用于返回下一个元素。
public interface Iterator {
boolean hasNext();
Object next();
}
然后,我们实现BookShelf类,并在其中定义一个内部类BookShelfIterator,它实现了Iterator接口。BookShelfIterator类包含一个指向当前书籍的索引,以及一个对BookShelf对象的引用。hasNext()方法检查是否还有下一本书,next()方法返回下一本书,并将索引递增。
public class BookShelf {
private Book[] books;
private int last = 0;
public BookShelf(int maxsize) {
this.books = new Book[maxsize];
}
public Book getBookAt(int index) {
return books[index];
}
public void appendBook(Book book) {
this.books[last] = book;
last++;
}
public int getLength() {
return last;
}
public Iterator iterator() {
return new BookShelfIterator(this);
}
private class BookShelfIterator implements Iterator {
private int index;
private BookShelf bookShelf;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
public boolean hasNext() {
if (index < bookShelf.getLength()) {
return true;
} else {
return false;
}
}
public Object next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
}
最后,我们可以使用BookShelf和Iterator来遍历书架中的所有书籍。
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("Design Patterns", "Erich Gamma"));
bookShelf.appendBook(new Book("Head First Java", "Kathy Sierra"));
bookShelf.appendBook(new Book("Effective Java", "Joshua Bloch"));
bookShelf.appendBook(new Book("Clean Code", "Robert C. Martin"));
Iterator iterator = bookShelf.iterator();
while (iterator.hasNext()) {
Book book = (Book) iterator.next();
System.out.println(book.getName() + " by " + book.getAuthor());
}
//输出结果为:
Design Patterns by Erich Gamma
Head First Java by Kathy Sierra
Effective Java by Joshua Bloch
Clean Code by Robert C. Martin
迭代器模式的优点包括:
- 简化了聚合对象的接口,使得聚合对象可以独立地变化。
- 提供了一种统一的访问聚合对象中元素的方式,使得客户端可以使用相同的代码来访问不同类型的聚合对象。
- 支持延迟访问聚合对象中的元素,从而提高了系统的性能。
迭代器模式的缺点包括:
- 增加了系统的复杂度,需要额外的迭代器类。
- 在某些情况下,迭代器模式可能会降低系统的性能,特别是在访问聚合对象中元素的顺序不确定的情况下。
17.中介者模式
中介者模式是一种行为型设计模式,它允许对象之间通过中介者对象进行通信,而不必直接相互引用。中介者模式将对象之间的通信集中在一个中介者对象中,从而降低了对象之间的耦合度,使得系统更加灵活和可扩展。
在中介者模式中,中介者对象充当了对象之间的协调者,它负责处理对象之间的通信和协作。对象之间的通信不再直接发生,而是通过中介者对象进行。当一个对象需要与其他对象通信时,它将消息发送给中介者对象,中介者对象再将消息转发给其他对象。
中介者模式在实际应用中非常广泛,例如GUI界面设计中的事件处理机制就是基于中介者模式实现的。
以下是一个简单的中介者模式的例子:
假设我们有一个名为ChatRoom的中介者类,它负责处理用户之间的聊天消息。我们还有一个名为User的类,它表示一个用户,可以发送和接收聊天消息。我们希望能够让用户之间进行聊天,但是我们不想让用户直接相互引用。因此,我们可以使用中介者模式来实现这个功能。
首先,我们定义一个ChatRoom类,它包含一个users列表,用于存储所有的用户。ChatRoom类还包含一个sendMessage()方法,用于将消息发送给指定的用户。
import java.util.ArrayList;
import java.util.List;
public class ChatRoom {
private List<User> users = new ArrayList<User>();
public void addUser(User user) {
users.add(user);
}
public void sendMessage(String message, User sender, User receiver) {
receiver.receiveMessage(message, sender);
}
}
然后,我们实现User类,并在其中定义一个receiveMessage()方法,用于接收聊天消息。User类还包含一个对ChatRoom对象的引用,用于发送和接收聊天消息。
public class User {
private String name;
private ChatRoom chatRoom;
public User(String name, ChatRoom chatRoom) {
this.name = name;
this.chatRoom = chatRoom;
chatRoom.addUser(this);
}
public String getName() {
return name;
}
public void sendMessage(String message, User receiver) {
chatRoom.sendMessage(message, this, receiver);
}
public void receiveMessage(String message, User sender) {
System.out.println(sender.getName() + " sent message to " + getName() + ": " + message);
}
}
最后,我们可以创建多个用户,并让他们之间进行聊天。
ChatRoom chatRoom = new ChatRoom();
User alice = new User("Alice", chatRoom);
User bob = new User("Bob", chatRoom);
User charlie = new User("Charlie", chatRoom);
alice.sendMessage("Hello Bob!", bob);
bob.sendMessage("Hi Alice!", alice);
charlie.sendMessage("Hello everyone!", null);
//输出结果为:
Bob sent message to Alice: Hello Bob!
Alice sent message to Bob: Hi Alice!
中介者模式的优点包括:
- 降低了对象之间的耦合度,使得系统更加灵活和可扩展。
- 将对象之间的通信集中在一个中介者对象中,使得系统更加简单和易于维护。
- 支持松耦合的设计,使得系统更加容易进行单元测试和调试。
中介者模式的缺点包括:
- 中介者对象可能会变得过于复杂,难以维护。
- 中介者对象可能会成为系统的瓶颈,影响系统的性能。
18.备忘录模式
备忘录模式是一种行为型设计模式,它允许在不破坏封装性的前提下保存和恢复对象的内部状态。备忘录模式将对象的状态保存在备忘录对象中,以便在需要时恢复对象的状态。备忘录模式通常与命令模式和/或撤销机制一起使用。
在备忘录模式中,我们定义了三个角色:
- 发起人(Originator):负责创建备忘录对象,并将自己的状态保存到备忘录对象中。
- 备忘录(Memento):用于存储发起人的状态。
- 管理者(Caretaker):负责保存备忘录对象,并在需要时将其恢复到发起人的状态。
备忘录模式在实际应用中非常广泛,例如文本编辑器中的撤销和重做操作就是基于备忘录模式实现的。
以下是一个简单的备忘录模式的例子:
假设我们有一个名为Editor的类,它表示一个文本编辑器。我们希望能够在编辑器中进行撤销和重做操作,即撤销和恢复编辑器中的文本内容。因此,我们可以使用备忘录模式来实现这个功能。
首先,我们定义一个EditorMemento类,它包含一个content字段,用于存储编辑器的文本内容。EditorMemento类还包含一个getContent()方法,用于获取编辑器的文本内容。
public class EditorMemento {
private String content;
public EditorMemento(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
然后,我们实现Editor类,并在其中定义一个createMemento()方法,用于创建备忘录对象,并将当前的文本内容保存到备忘录对象中。Editor类还包含一个restoreMemento()方法,用于将编辑器的文本内容恢复到指定的备忘录对象中保存的状态。
import java.util.ArrayList;
import java.util.List;
public class Editor {
private String content;
private List<EditorMemento> mementos = new ArrayList<EditorMemento>();
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public EditorMemento createMemento() {
EditorMemento memento = new EditorMemento(content);
mementos.add(memento);
return memento;
}
public void restoreMemento(EditorMemento memento) {
content = memento.getContent();
}
}
最后,我们可以创建一个Editor对象,并在其中进行撤销和重做操作。
Editor editor = new Editor();
editor.setContent("Hello World!");
editor.createMemento();
editor.setContent("Hello Java!");
editor.createMemento();
editor.setContent("Hello Design Patterns!");
editor.createMemento();
System.out.println(editor.getContent()); // 输出:Hello Design Patterns!
editor.restoreMemento(editor.getMementos().get(1));
System.out.println(editor.getContent()); // 输出:Hello Java!
//输出结果为:
Hello Design Patterns!
Hello Java!
备忘录模式的优点包括:
- 允许在不破坏封装性的前提下保存和恢复对象的内部状态。
- 支持撤销和重做操作,使得系统更加灵活和可靠。
- 简化了发起人的代码,使其更加清晰和易于维护。
备忘录模式的缺点包括:
- 如果备忘录对象过多,可能会占用大量的内存空间。
- 如果发起人的状态过于复杂,可能会导致备忘录对象过于庞大,从而影响系统的性能。
19.观察者模式
观察者模式是一种行为型设计模式,它允许对象之间建立一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。观察者模式通常用于事件驱动系统中,例如GUI界面设计、消息队列等。
在观察者模式中,我们定义了两个角色:
- 主题(Subject):负责维护一组观察者对象,并在自身状态发生改变时通知观察者对象。
- 观察者(Observer):负责接收主题的通知,并根据主题的状态进行相应的操作。
以下是一个简单的观察者模式的例子:
假设我们有一个名为Subject的主题类,它包含一个observers列表,用于存储所有的观察者对象。Subject类还包含一个attach()方法,用于将观察者对象添加到列表中,以及一个notifyObservers()方法,用于通知所有的观察者对象。
import java.util.ArrayList;
import java.util.List;
public class Subject {
private List<Observer> observers = new ArrayList<Observer>();
private int state;
public void attach(Observer observer) {
observers.add(observer);\ }
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyObservers();
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
然后,我们实现Observer接口,并在其中定义一个update()方法,用于接收主题的通知,并根据主题的状态进行相应的操作。
public interface Observer {
void update();
}
最后,我们可以创建多个观察者对象,并将它们添加到主题对象的观察者列表中。当主题的状态发生改变时,所有的观察者对象都会得到通知并自动更新。
Subject subject = new Subject();
Observer observer1 = new Observer() {
@Override
public void update() {
System.out.println("Observer 1: " + subject.getState());
}
};
Observer observer2 = new Observer() {
@Override
public void update() {
System.out.println("Observer 2: " + subject.getState());
}
};
subject.attach(observer1);
subject.attach(observer2);
subject.setState(1);
subject.setState(2);
//输出结果为:
Observer 1: 1
Observer 2: 1
Observer 1: 2
Observer 2: 2
观察者模式的优点包括:
- 允许对象之间建立一种一对多的依赖关系,从而降低了对象之间的耦合度。
- 支持广播通信,使得系统更加灵活和可扩展。
- 简化了对象之间的交互,使得系统更加清晰和易于维护。
观察者模式的缺点包括:
- 如果观察者对象过多,可能会导致系统的性能下降。
- 如果观察者对象之间存在循环依赖关系,可能会导致系统出现死锁或其他问题。
20.状态模式
状态模式是一种行为型设计模式,它允许对象在内部状态发生改变时改变它的行为。状态模式将对象的行为封装在不同的状态类中,使得对象在不同的状态下具有不同的行为。状态模式通常用于解决对象的状态转换问题,例如状态机、游戏角色状态等。
在状态模式中,我们定义了三个角色:
- 环境(Context):负责维护一个状态对象,并在状态发生改变时改变它的行为。
- 抽象状态(State):定义一个接口或抽象类,用于封装与环境的一个特定状态相关的行为。
- 具体状态(Concrete State):实现抽象状态接口或抽象类,用于封装与环境的一个特定状态相关的行为。
状态模式在实际应用中非常广泛,例如游戏开发中的角色状态、电梯控制系统中的电梯状态等。
以下是一个简单的状态模式的例子:
假设我们有一个名为Context的环境类,它包含一个state字段,用于表示当前的状态。Context类还包含一个setState()方法,用于改变当前的状态,并调用当前状态的相应方法。
public class Context {
private State state;
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle();
}
}
然后,我们定义一个抽象状态类State,其中包含一个handle()方法,用于封装与环境的一个特定状态相关的行为。
public abstract class State {
public abstract void handle();
}
最后,我们实现具体状态类ConcreteStateA和ConcreteStateB,分别表示环境的两种不同状态。在具体状态类中,我们实现handle()方法,用于封装与当前状态相关的行为。
public class ConcreteStateA extends State {
@Override
public void handle() {
System.out.println("ConcreteStateA is handling...");
}
}
public class ConcreteStateB extends State {
@Override
public void handle() {
System.out.println("ConcreteStateB is handling...");
}
}
最后,我们可以创建一个Context对象,并在其中设置不同的状态。当调用request()方法时,Context对象会根据当前的状态调用相应的方法。
Context context = new Context();
State stateA = new ConcreteStateA();
State stateB = new ConcreteStateB();
context.setState(stateA);
context.request(); // 输出:ConcreteStateA is handling...
context.setState(stateB);
context.request(); // 输出:ConcreteStateB is handling...
//输出结果为:
ConcreteStateA is handling...
ConcreteStateB is handling...
状态模式的优点包括:
- 将对象的状态转换逻辑封装在具体状态类中,使得状态转换更加清晰和易于维护。
- 将对象的行为和状态分离,使得对象的行为可以独立于状态进行扩展和修改。
- 简化了环境类的代码,使其更加清晰和易于维护。
状态模式的缺点包括:
- 如果状态过多,可能会导致类的数量增加,从而影响系统的性能和可维护性。
- 如果状态转换逻辑过于复杂,可能会导致状态类之间的耦合度增加,从而影响系统的灵活性和可扩展性。
21.策略模式
策略模式是一种行为型设计模式,它允许对象在运行时选择算法的行为。策略模式将算法封装在不同的策略类中,使得它们可以相互替换,从而使得对象的行为可以根据需要动态地改变。策略模式通常用于解决对象的行为多样性问题,例如排序算法、加密算法等。
在策略模式中,我们定义了三个角色:
- 环境(Context):负责维护一个策略对象,并在运行时选择合适的策略对象。
- 抽象策略(Strategy):定义一个接口或抽象类,用于封装不同的算法行为。
- 具体策略(Concrete Strategy):实现抽象策略接口或抽象类,用于封装具体的算法行为。
策略模式在实际应用中非常广泛,例如Java中的集合排序算法就是基于策略模式实现的。
以下是一个简单的策略模式的例子:
假设我们有一个名为Context的环境类,它包含一个strategy字段,用于维护一个策略对象。Context类还包含一个setStrategy()方法,用于设置当前的策略对象,并调用策略对象的相应方法。
public class Context {
private Strategy strategy;
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy() {
strategy.execute();
}
}
然后,我们定义一个抽象策略类Strategy,其中包含一个execute()方法,用于封装不同的算法行为。
public interface Strategy {
void execute();
}
最后,我们实现具体策略类ConcreteStrategyA和ConcreteStrategyB,分别表示不同的算法行为。在具体策略类中,我们实现execute()方法,用于封装具体的算法行为。
public class ConcreteStrategyA implements Strategy {
@Override
public void execute() {
System.out.println("Executing strategy A...");
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void execute() {
System.out.println("Executing strategy B...");
}
}
最后,我们可以创建一个Context对象,并在其中设置不同的策略。当调用executeStrategy()方法时,Context对象会根据当前的策略调用相应的方法。
Context context = new Context();
Strategy strategyA = new ConcreteStrategyA();
Strategy strategyB = new ConcreteStrategyB();
context.setStrategy(strategyA);
context.executeStrategy(); // 输出:Executing strategy A...
context.setStrategy(strategyB);
context.executeStrategy(); // 输出:Executing strategy B...
//输出结果为:
Executing strategy A...
Executing strategy B...
策略模式的优点包括:
- 将算法封装在不同的策略类中,使得它们可以相互替换,从而使得对象的行为可以根据需要动态地改变。
- 简化了环境类的代码,使其更加清晰和易于维护。
- 提高了代码的复用性和可扩展性,使得系统更加灵活和可维护。
策略模式的缺点包括:
- 如果策略过多,可能会导致类的数量增加,从而影响系统的性能和可维护性。
- 如果策略类之间存在相互依赖关系,可能会导致系统出现死锁或其他问题。
22.模板方法模式
模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,将算法中不变的部分放在父类中实现,将可变的部分留给子类来实现。模板方法模式允许子类在不改变算法结构的情况下重新定义算法中的某些步骤。模板方法模式通常用于解决算法的复用性问题,例如排序算法、文件读写等。
在模板方法模式中,我们定义了两个角色:
- 抽象类(Abstract Class):定义一个模板方法,用于封装算法的骨架,并定义一些抽象方法,用于让子类实现算法中的可变部分。
- 具体类(Concrete Class):实现抽象类中的抽象方法,用于实现算法中的可变部分。
模板方法模式在实际应用中非常广泛,例如Java中的Servlet生命周期就是基于模板方法模式实现的。
以下是一个简单的模板方法模式的例子:
假设我们有一个名为AbstractClass的抽象类,其中包含一个模板方法templateMethod(),用于封装算法的骨架,并定义了两个抽象方法primitiveOperation1()和primitiveOperation2(),用于让子类实现算法中的可变部分。
public abstract class AbstractClass {
public void templateMethod() {
primitiveOperation1();
primitiveOperation2();
}
public abstract void primitiveOperation1();
public abstract void primitiveOperation2();
}
然后,我们实现具体类ConcreteClassA和ConcreteClassB,分别表示不同的算法实现。在具体类中,我们实现抽象方法,用于实现算法中的可变部分。
public class ConcreteClassA extends AbstractClass {
@Override
public void primitiveOperation1() {
System.out.println("ConcreteClassA: primitiveOperation1...");
}
@Override
public void primitiveOperation2() {
System.out.println("ConcreteClassA: primitiveOperation2...");
}
}
public class ConcreteClassB extends AbstractClass {
@Override
public void primitiveOperation1() {
System.out.println("ConcreteClassB: primitiveOperation1...");
}
@Override
public void primitiveOperation2() {
System.out.println("ConcreteClassB: primitiveOperation2...");
}
}
最后,我们可以创建一个AbstractClass对象,并调用其模板方法templateMethod()。根据不同的具体类,模板方法会调用相应的抽象方法,从而实现不同的算法实现。
AbstractClass abstractClassA = new ConcreteClassA();
AbstractClass abstractClassB = new ConcreteClassB();
abstractClassA.templateMethod();
// 输出:
// ConcreteClassA: primitiveOperation1...
// ConcreteClassA: primitiveOperation2...
abstractClassB.templateMethod();
// 输出:
// ConcreteClassB: primitiveOperation1...
// ConcreteClassB: primitiveOperation2...
//输出结果为:
ConcreteClassA: primitiveOperation1...
ConcreteClassA: primitiveOperation2...
ConcreteClassB: primitiveOperation1...
ConcreteClassB: primitiveOperation2...
模板方法模式的优点包括:
- 将算法的骨架封装在抽象类中,使得算法的结构更加清晰和易于维护。
- 提高了代码的复用性和可扩展性,使得系统更加灵活和可维护。
- 允许子类在不改变算法结构的情况下重新定义算法中的某些步骤,从而增加了系统的灵活性和可扩展性。
模板方法模式的缺点包括:
- 如果算法的骨架过于复杂,可能会导致抽象类的代码量过大,从而影响系统的性能和可维护性。
- 如果子类的实现过于复杂,可能会导致系统的可读性和可维护性降低。
23.访问者模式
访问者模式是一种行为型设计模式,它允许在不改变对象结构的情况下定义新的操作。访问者模式将对象结构和操作分离开来,使得操作可以独立于对象结构进行变化。访问者模式通常用于解决对象结构中元素的操作问题,例如编译器中的语法树、图形界面中的组件等。
在访问者模式中,我们定义了两个角色:
- 访问者(Visitor):定义一个访问方法,用于访问不同类型的元素,并对其进行操作。
- 元素(Element):定义一个接受方法,用于接受访问者的访问,并将自身作为参数传递给访问者。
访问者模式在实际应用中比较少见,但在某些场景下非常有用,例如编译器中的语法树分析、图形界面中的组件操作等。
以下是一个简单的访问者模式的例子:
假设我们有一个名为Element的元素接口,其中包含一个接受访问者的方法accept(),用于接受访问者的访问,并将自身作为参数传递给访问者。
public interface Element {
void accept(Visitor visitor);
}
然后,我们实现具体元素类ConcreteElementA和ConcreteElementB,分别表示不同类型的元素。在具体元素类中,我们实现accept()方法,用于接受访问者的访问,并将自身作为参数传递给访问者。
public class ConcreteElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class ConcreteElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
然后,我们定义一个访问者接口Visitor,其中包含多个访问方法,用于访问不同类型的元素,并对其进行操作。
public interface Visitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
最后,我们实现具体访问者类ConcreteVisitor,用于实现访问方法,对不同类型的元素进行操作。
public class ConcreteVisitor implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("ConcreteVisitor: visit ConcreteElementA...");
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("ConcreteVisitor: visit ConcreteElementB...");
}
}
最后,我们可以创建一个Visitor对象,并调用其访问方法。根据不同的具体元素,访问者会调用相应的访问方法,从而实现不同的操作。
Element elementA = new ConcreteElementA();
Element elementB = new ConcreteElementB();
Visitor visitor = new ConcreteVisitor();
elementA.accept(visitor); // 输出:ConcreteVisitor: visit ConcreteElementA...
elementB.accept(visitor); // 输出:ConcreteVisitor: visit ConcreteElementB...
//输出结果为:
ConcreteVisitor: visit ConcreteElementA...
ConcreteVisitor: visit ConcreteElementB...
访问者模式的优点包括:
- 将对象结构和操作分离开来,使得操作可以独立于对象结构进行变化。
- 增加新的操作很容易,只需要定义一个新的访问者即可,无需修改现有的对象结构。
- 将相关的操作集中在一个访问者中,使得代码更加清晰和易于维护。
访问者模式的缺点包括:
- 增加新的元素很困难,需要修改现有的访问者接口和所有的访问者实现类。
- 访问者模式增加了系统的复杂度和理解难度,不适合简单的应用场景。