0003_二十三种设计模式
设计模式
二十三种设计模式
根据经典的《设计模式:可复用面向对象软件的基础》(Gang of Four)一书,共提出了23种常见的设计模式。这些设计模式分为三个分类:
-
创建型模式(Creational Patterns):这些模式关注对象的创建机制,帮助解决对象的实例化过程。
- 简单工厂模式(Simple Factory Pattern)
- 工厂方法模式(Factory Method Pattern)
- 抽象工厂模式(Abstract Factory Pattern)
- 建造者模式(Builder Pattern)
- 原型模式(Prototype Pattern)
- 单例模式(Singleton Pattern)
-
结构型模式(Structural Patterns):这些模式关注类和对象的组合,以便形成更大的结构。
- 适配器模式(Adapter Pattern)
- 桥接模式(Bridge Pattern)
- 组合模式(Composite Pattern)
- 装饰器模式(Decorator Pattern)
- 外观模式(Facade Pattern)
- 享元模式(Flyweight Pattern)
- 代理模式(Proxy Pattern)
-
行为型模式(Behavioral Patterns):这些模式关注对象之间的通信和交互,以及分配职责。
- 责任链模式(Chain of Responsibility Pattern)
- 命令模式(Command Pattern)
- 解释器模式(Interpreter Pattern)
- 迭代器模式(Iterator Pattern)
- 中介者模式(Mediator Pattern)
- 备忘录模式(Memento Pattern)
- 观察者模式(Observer Pattern)
- 状态模式(State Pattern)
- 策略模式(Strategy Pattern)
- 模板方法模式(Template Method Pattern)
- 访问者模式(Visitor Pattern)
设计模式举例解释:
①简单工厂模式
简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它提供了一个用于创建对象的工厂类,而不需要将对象的创建逻辑暴露给客户端。简单工厂模式通过一个共享的工厂方法,根据传入的参数或条件来创建并返回具体的对象实例。
// 产品接口
interface Product {
void operation();
}
// 具体产品实现类A
class ConcreteProductA implements Product {
@Override
public void operation() {
System.out.println("Concrete Product A operation");
}
}
// 具体产品实现类B
class ConcreteProductB implements Product {
@Override
public void operation() {
System.out.println("Concrete Product B operation");
}
}
// 简单工厂类
class SimpleFactory {
public static Product createProduct(String type) {
if (type.equals("A")) {
return new ConcreteProductA();
} else if (type.equals("B")) {
return new ConcreteProductB();
} else {
throw new IllegalArgumentException("Invalid product type.");
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 使用简单工厂创建具体产品对象
Product productA = SimpleFactory.createProduct("A");
productA.operation(); // 输出:Concrete Product A operation
Product productB = SimpleFactory.createProduct("B");
productB.operation(); // 输出:Concrete Product B operation
}
}
在上面的示例中,我们定义了一个产品接口(Product),并有两个具体的产品实现类(ConcreteProductA和ConcreteProductB)。然后,我们定义了一个简单工厂类(SimpleFactory),它提供了一个静态方法createProduct(),根据传入的参数来创建并返回具体的产品对象。
客户端代码中,我们通过调用SimpleFactory的createProduct()方法来获取具体的产品对象,并使用该对象进行操作。
使用简单工厂模式的好处是客户端代码无需关心具体产品对象的创建过程,只需要通过工厂类获取所需的产品对象即可。这样在增加新的产品实现类时,只需要修改工厂类的代码,而不需要修改客户端代码。简单工厂模式有助于降低代码耦合性,提供了一种简单而灵活的对象创建方式。
②工厂方法模式
工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它通过定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法模式将对象的实例化延迟到子类中进行,从而实现了解耦和灵活性。
// 产品接口
interface Product {
void operation();
}
// 具体产品实现类A
class ConcreteProductA implements Product {
@Override
public void operation() {
System.out.println("Concrete Product A operation");
}
}
// 具体产品实现类B
class ConcreteProductB implements Product {
@Override
public void operation() {
System.out.println("Concrete Product B operation");
}
}
// 抽象工厂类
abstract class Factory {
public abstract Product createProduct();
public void doOperation() {
Product product = createProduct();
product.operation();
}
}
// 具体工厂类A
class ConcreteFactoryA extends Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂类B
class ConcreteFactoryB extends Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 使用具体工厂类A创建产品A
Factory factoryA = new ConcreteFactoryA();
factoryA.doOperation(); // 输出:Concrete Product A operation
// 使用具体工厂类B创建产品B
Factory factoryB = new ConcreteFactoryB();
factoryB.doOperation(); // 输出:Concrete Product B operation
}
}
在上面的示例中,我们定义了一个产品接口(Product),并有两个具体的产品实现类(ConcreteProductA和ConcreteProductB)。然后,我们定义了一个抽象工厂类(Factory),其中包含一个抽象方法createProduct()用于创建产品对象,并有一个公共方法doOperation()用于执行产品的操作。
具体的工厂类(ConcreteFactoryA和ConcreteFactoryB)继承自抽象工厂类,并实现了createProduct()方法,分别创建了具体的产品实例。
客户端代码中,我们通过实例化具体的工厂类来获取具体的产品对象,并调用doOperation()方法执行产品的操作。
工厂方法模式的核心思想是将对象的创建交给具体的工厂类来完成,从而达到解耦的目的。通过定义基于接口的工厂和产品类,我们可以轻松地扩展系统,添加新的产品类和对应的工厂类,而无需修改已有的代码。工厂方法模式提供了一种灵活而可扩展的对象创建方式。
③抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一种封装一组相关或相互依赖对象的接口,而不需要指定具体类。抽象工厂模式通过定义一个用于创建相关或依赖对象族的接口,使得客户端可以使用此接口创建一系列的产品对象,而无需关心具体的实现细节。
// 抽象产品A接口
interface AbstractProductA {
void operationA();
}
// 具体产品A1
class ConcreteProductA1 implements AbstractProductA {
@Override
public void operationA() {
System.out.println("Concrete Product A1 operation");
}
}
// 具体产品A2
class ConcreteProductA2 implements AbstractProductA {
@Override
public void operationA() {
System.out.println("Concrete Product A2 operation");
}
}
// 抽象产品B接口
interface AbstractProductB {
void operationB();
}
// 具体产品B1
class ConcreteProductB1 implements AbstractProductB {
@Override
public void operationB() {
System.out.println("Concrete Product B1 operation");
}
}
// 具体产品B2
class ConcreteProductB2 implements AbstractProductB {
@Override
public void operationB() {
System.out.println("Concrete Product B2 operation");
}
}
// 抽象工厂接口
interface AbstractFactory {
AbstractProductA createProductA();
AbstractProductB createProductB();
}
// 具体工厂1
class ConcreteFactory1 implements AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}
}
// 具体工厂2
class ConcreteFactory2 implements AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ConcreteProductA2();
}
@Override
public AbstractProductB createProductB() {
return new ConcreteProductB2();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 使用具体工厂1创建产品族1的产品
AbstractFactory factory1 = new ConcreteFactory1();
AbstractProductA productA1 = factory1.createProductA();
AbstractProductB productB1 = factory1.createProductB();
productA1.operationA(); // 输出:Concrete Product A1 operation
productB1.operationB(); // 输出:Concrete Product B1 operation
// 使用具体工厂2创建产品族2的产品
AbstractFactory factory2 = new ConcreteFactory2();
AbstractProductA productA2 = factory2.createProductA();
AbstractProductB productB2 = factory2.createProductB();
productA2.operationA(); // 输出:Concrete Product A2 operation
productB2.operationB(); // 输出:Concrete Product B2 operation
}
}
在上面的示例中,我们定义了抽象产品接口(AbstractProductA和AbstractProductB),并有两个具体的产品实现类。然后,我们定义了抽象工厂接口(AbstractFactory),其中包含了两个创建产品的抽象方法。
具体的工厂类(ConcreteFactory1和ConcreteFactory2)分别实现了抽象工厂接口,并重写了创建产品的方法,以创建属于自己产品族的具体产品。
客户端代码中,我们通过实例化具体的工厂类来获取属于特定产品族的产品对象,并调用这些产品对象的方法执行操作。
抽象工厂模式使得客户端代码与具体的产品实现相互解耦,客户端只需要使用抽象工厂和抽象产品接口,并不需要关心具体的实现类。当需要添加新的产品族时,只需增加对应的具体工厂类和具体产品类,而无需修改已有的代码。抽象工厂模式提供了一种灵活且可扩展的方式来创建相关的一组对象。
④建造者模式
建造者模式(Builder Pattern)是一种创建型设计模式,它允许你按照步骤或顺序创建复杂对象。它将对象的构建过程与其表示分离,从而使同样的构建过程可以创建不同的表示。
// 产品类
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 Parts: " + partA + ", " + partB + ", " + partC);
}
}
// 抽象建造者类
abstract class Builder {
protected Product product;
public void createProduct() {
product = new Product();
}
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
public Product getProduct() {
return product;
}
}
// 具体建造者类1
class ConcreteBuilder1 extends Builder {
@Override
public void buildPartA() {
product.setPartA("Part A - Builder 1");
}
@Override
public void buildPartB() {
product.setPartB("Part B - Builder 1");
}
@Override
public void buildPartC() {
product.setPartC("Part C - Builder 1");
}
}
// 具体建造者类2
class ConcreteBuilder2 extends Builder {
@Override
public void buildPartA() {
product.setPartA("Part A - Builder 2");
}
@Override
public void buildPartB() {
product.setPartB("Part B - Builder 2");
}
@Override
public void buildPartC() {
product.setPartC("Part C - Builder 2");
}
}
// 指挥者类
class Director {
public Product construct(Builder builder) {
builder.createProduct();
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getProduct();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Director director = new Director();
// 使用具体建造者1创建产品
Builder builder1 = new ConcreteBuilder1();
Product product1 = director.construct(builder1);
product1.show();
// 输出:Product Parts: Part A - Builder 1, Part B - Builder 1, Part C - Builder 1
// 使用具体建造者2创建产品
Builder builder2 = new ConcreteBuilder2();
Product product2 = director.construct(builder2);
product2.show();
// 输出:Product Parts: Part A - Builder 2, Part B - Builder 2, Part C - Builder 2
}
}
在上面的示例中,我们有一个产品类(Product),其中定义了产品的各个部分。抽象建造者类(Builder)定义了构建产品的抽象方法,并提供了获取产品的方法。具体的建造者类(ConcreteBuilder1和ConcreteBuilder2)继承了抽象建造者类,并实现了构建产品的具体方法。
指挥者类(Director)定义了一个构建方法,该方法按照一定的步骤或顺序使用特定的建造者来构建产品。客户端代码中,我们首先创建一个指挥者对象,然后使用不同的具体建造者来构建不同的产品。
通过使用建造者模式,我们可以将对象的构建过程封装在具体的建造者类中,从而使客户端代码与产品的构建过程解耦。客户端只需要知道指挥者类和抽象建造者类即可,无需关心具体的构建细节。另外,建造者模式也使得客户端代码更加灵活,可以通过改变具体的建造者类来构建不同的产品表示。
⑤原型模式
原型模式(Prototype Pattern)是一种创建型设计模式,它允许你通过复制已有对象来创建新对象,而无需依赖于构造函数或子类化。这种方式能够提高对象的创建效率,尤其在创建对象过程复杂或代价较高时更为有效。
// 抽象原型类
abstract class Prototype implements Cloneable {
public abstract Prototype clone();
}
// 具体原型类1
class ConcretePrototype1 extends Prototype {
@Override
public Prototype clone() {
try {
return (ConcretePrototype1) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
// 具体原型类2
class ConcretePrototype2 extends Prototype {
@Override
public Prototype clone() {
try {
return (ConcretePrototype2) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建具体原型对象1,并通过克隆创建新对象
ConcretePrototype1 prototype1 = new ConcretePrototype1();
ConcretePrototype1 clone1 = (ConcretePrototype1) prototype1.clone();
// 创建具体原型对象2,并通过克隆创建新对象
ConcretePrototype2 prototype2 = new ConcretePrototype2();
ConcretePrototype2 clone2 = (ConcretePrototype2) prototype2.clone();
}
}
在上面的示例中,抽象原型类(Prototype)定义了一个抽象的克隆方法,并实现了
Cloneable
接口。具体的原型类(ConcretePrototype1和ConcretePrototype2)继承了抽象原型类,并重写了克隆方法,使用super.clone()
实现对象的复制。客户端代码中,我们首先创建具体原型对象1(ConcretePrototype1),然后通过调用其克隆方法
clone()
来创建新对象(clone1)。同样地,我们也创建了具体原型对象2(ConcretePrototype2),并使用克隆方法clone()
创建新对象(clone2)。通过使用原型模式,我们可以通过克隆来创建新对象,而无需关心对象的构造过程。这不仅提高了对象的创建效率,还避免了重复的初始化步骤。另外,原型模式还使得客户端代码更加灵活,可以根据需要创建多个对象实例。
⑥单例模式
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供全局访问点。
public class Singleton {
private static Singleton instance;
// 私有构造方法,防止外部通过new关键字实例化对象
private Singleton() {
}
// 全局访问点,获取唯一的实例
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
在上面的示例中,我们通过私有构造方法将类的实例化限制在类内部。这样做可以防止外部通过
new
关键字创建新的对象。
getInstance()
方法是单例模式的全局访问点,用于获取唯一的实例。在该方法中,我们使用双重检查锁定来确保线程安全性和性能。首先,我们检查实例是否已经存在,如果不存在,就进入同步块。在同步块中再次检查实例是否为null
,然后再创建新的实例。客户端代码可以通过
Singleton.getInstance()
来访问单例对象。单例模式确保了一个类只有一个实例,并且提供了全局访问点,使得我们可以方便地在代码的任何地方获取该实例。它在需要共享资源的情况下非常有用,例如数据库连接、日志记录器等。此外,单例模式还可以用于控制对象数量,比如线程池、缓存等场景。
⑦适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口。适配器模式旨在解决不兼容接口之间的问题,使得不相关的类可以协同工作。
// 目标接口
interface Target {
void request();
}
// 需要适配的类
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee specificRequest");
}
}
// 适配器类
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建适配者对象
Adaptee adaptee = new Adaptee();
// 创建适配器对象并传入适配者对象
Target adapter = new Adapter(adaptee);
// 调用目标接口的方法,实际上会调用适配者对象的特定方法
adapter.request();
}
}
在上面的示例中,我们有一个目标接口(Target),定义了客户端所期望的接口方法
request()
。然后有一个需要适配的类(Adaptee),其中有一个特定的方法specificRequest()
。我们希望适配器(Adapter)将适配者的接口转换为目标接口。适配器类实现了目标接口,并在其内部持有适配者对象的引用。在适配器的
request()
方法中,通过调用适配者的特定方法来实现目标接口的方法。在客户端代码中,我们首先创建适配者对象(Adaptee),然后创建适配器对象并将适配者对象传入。最后,通过调用目标接口的方法(
adapter.request()
),实际上会调用适配者对象的特定方法(adaptee.specificRequest()
)。适配器模式可以帮助我们解决不兼容接口之间的问题。它使得不相关的类可以协同工作,无需修改已有代码,提高了代码的复用性和灵活性。适配器模式通常用于集成第三方组件、旧系统的接口适配等场景。
⑧桥接模式
桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立地变化。桥接模式通过组合来实现这种分离,而不是继承。
// 实现部分的接口
interface Implementor {
void operationImpl();
}
// 具体的实现类A
class ConcreteImplementorA implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorA operationImpl");
}
}
// 具体的实现类B
class ConcreteImplementorB implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorB operationImpl");
}
}
// 抽象部分的接口
abstract class Abstraction {
protected Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public abstract void operation();
}
// 扩展抽象部分的类A
class RefinedAbstractionA extends Abstraction {
public RefinedAbstractionA(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
System.out.println("RefinedAbstractionA operation");
implementor.operationImpl();
}
}
// 扩展抽象部分的类B
class RefinedAbstractionB extends Abstraction {
public RefinedAbstractionB(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
System.out.println("RefinedAbstractionB operation");
implementor.operationImpl();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Implementor implementorA = new ConcreteImplementorA();
Implementor implementorB = new ConcreteImplementorB();
Abstraction abstractionA = new RefinedAbstractionA(implementorA);
abstractionA.operation();
Abstraction abstractionB = new RefinedAbstractionB(implementorB);
abstractionB.operation();
}
}
在上面的示例中,我们有一个实现部分的接口(Implementor),定义了实现部分的方法
operationImpl()
。然后我们有两个具体的实现类(ConcreteImplementorA和ConcreteImplementorB),分别实现了实现部分的接口。然后我们有一个抽象部分的接口(Abstraction),其中包含一个对实现部分接口的引用,并定义了抽象部分的方法
operation()
。然后我们有两个扩展抽象部分的类(RefinedAbstractionA和RefinedAbstractionB),它们继承了抽象部分的接口,并在其
operation()
方法中调用实现部分的方法。在客户端代码中,我们首先创建具体的实现类对象(ConcreteImplementorA和ConcreteImplementorB),然后通过传入不同的实现类对象来创建扩展抽象部分的对象(RefinedAbstractionA和RefinedAbstractionB)。最后,我们调用扩展抽象部分的方法,它会调用相应的实现部分的方法。
桥接模式通过将抽象和实现分离,使它们可以独立地变化。这样,我们可以通过添加新的抽象部分或实现部分的类来扩展系统,而无需修改已有的代码。桥接模式常用于处理多维度的变化,例如不同品牌的手机与不同操作系统的软件之间的组合。
⑨组合模式
组合模式(Composite Pattern)是一种结构型设计模式,它允许将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户可以统一处理单个对象和组合对象,而无需区分它们的差异。
// 组件接口
interface Component {
void operation();
}
// 叶子组件类
class Leaf implements Component {
@Override
public void operation() {
System.out.println("Leaf operation");
}
}
// 容器组件类
class Composite implements Component {
private List<Component> components = new ArrayList<>();
public void addComponent(Component component) {
components.add(component);
}
public void removeComponent(Component component) {
components.remove(component);
}
@Override
public void operation() {
System.out.println("Composite operation");
for (Component component : components) {
component.operation();
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建叶子组件对象
Component leaf1 = new Leaf();
Component leaf2 = new Leaf();
// 创建容器组件对象
Composite composite = new Composite();
// 将叶子组件添加到容器组件
composite.addComponent(leaf1);
composite.addComponent(leaf2);
// 执行操作
composite.operation();
}
}
在上面的示例中,我们有一个组件接口(Component),定义了组件的方法
operation()
。然后,我们有一个叶子组件类(Leaf),实现了组件接口。我们还有一个容器组件类(Composite),它包含一个组件列表,并实现了组件接口。容器组件类可以添加和移除其他组件,以及调用其内部组件的方法。
在客户端代码中,我们首先创建叶子组件对象(Leaf),然后创建容器组件对象(Composite)。然后将叶子组件添加到容器组件中。最后,通过调用容器组件的方法(
composite.operation()
),会递归执行容器组件及其内部组件的操作。组合模式允许我们通过树形结构来表示对象的层次关系,并且可以一致地对待单个对象和组合对象。这样可以简化客户端的使用,使得客户端代码更加灵活和可扩展。组合模式常用于处理树状结构、菜单、文件系统等具有层次结构的场景。
⑩装饰器模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许在不改变现有对象结构的情况下,动态地将新功能添加到对象上。
装饰器模式通过创建一个装饰器类,该类包含了原始对象(被装饰的对象)的引用,并实现了与原始对象相同的接口。装饰器类还可以包含额外的方法或属性来扩展原始对象的功能。
// 组件接口
interface Component {
void operation();
}
// 具体组件类
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent operation");
}
}
// 装饰器抽象类
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰器类A
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("ConcreteDecoratorA operation");
}
}
// 具体装饰器类B
class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("ConcreteDecoratorB operation");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Component component = new ConcreteComponent();
component.operation();
System.out.println("----------");
Component decoratedComponentA = new ConcreteDecoratorA(component);
decoratedComponentA.operation();
System.out.println("----------");
Component decoratedComponentB = new ConcreteDecoratorB(component);
decoratedComponentB.operation();
System.out.println("----------");
Component decoratedComponentAB = new ConcreteDecoratorB(new ConcreteDecoratorA(component));
decoratedComponentAB.operation();
}
}
在上面的示例中,我们有一个组件接口(Component),定义了组件的方法
operation()
。然后,我们有一个具体组件类(ConcreteComponent),实现了组件接口。我们还有一个装饰器抽象类(Decorator),它包含了一个组件对象的引用,并实现了组件接口。装饰器类在调用原始对象的操作之前或之后,可以添加额外的操作。
然后,我们有两个具体装饰器类(ConcreteDecoratorA和ConcreteDecoratorB),它们都扩展了装饰器抽象类。这些具体装饰器类可以在调用原始对象的操作之前或之后,添加特定的行为。
在客户端代码中,我们首先创建具体组件对象(ConcreteComponent)。然后,我们可以选择将一个或多个具体装饰器类(ConcreteDecoratorA和ConcreteDecoratorB)与具体组件对象进行组合。通过不同的组合方式,我们可以按照需要添加新功能。
最后,我们调用装饰器对象的操作方法,它会依次执行添加的操作。
装饰器模式可以动态地将新功能添加到现有对象上,而无需修改现有对象的代码。这样可以实现对现有对象的透明包装,使得客户端可以轻松地添加、移除或替换功能。装饰器模式常用于需要在不同的时机添加额外功能的场景,例如日志记录、缓存、安全性检查等。
⑪外观模式
外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个简化接口,用于访问复杂系统、子系统或类库的功能。
外观模式通过创建一个外观类(也称为门面类),该类封装了与复杂系统交互的子系统的复杂性,提供了一个简单的接口给客户端使用。客户端只需要与外观类进行交互,而不需要直接与子系统进行通信。
// 子系统A
class SubsystemA {
public void operationA() {
System.out.println("SubsystemA operation");
}
}
// 子系统B
class SubsystemB {
public void operationB() {
System.out.println("SubsystemB operation");
}
}
// 子系统C
class SubsystemC {
public void operationC() {
System.out.println("SubsystemC operation");
}
}
// 外观类
class Facade {
private SubsystemA subsystemA;
private SubsystemB subsystemB;
private SubsystemC subsystemC;
public Facade() {
this.subsystemA = new SubsystemA();
this.subsystemB = new SubsystemB();
this.subsystemC = new SubsystemC();
}
public void operation() {
subsystemA.operationA();
subsystemB.operationB();
subsystemC.operationC();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.operation();
}
}
在上述示例中,我们有三个子系统类(SubsystemA、SubsystemB和SubsystemC)。每个子系统都提供了一些具体的操作。
然后,我们有一个外观类(Facade),它包含了所有子系统的实例。外观类提供了一个公共的方法(例如
operation()
),将调用子系统类中的多个操作,以完成一个复杂的任务。在客户端代码中,我们创建了一个外观对象(Facade),并调用了其
operation()
方法。这个方法在内部调用了子系统类的相应操作,隐藏了子系统的复杂性,使得客户端可以通过一个简单的接口来完成任务。外观模式的优点在于它简化了客户端与复杂系统之间的交互,提供了一个更高级别的接口,使得客户端更容易使用系统。此外,外观模式还可以降低系统的耦合度,因为客户端不需要直接和子系统进行通信。
外观模式常用于以下情况:
- 当一个复杂系统拥有大量的子系统或类库,并且客户端只需要使用其中的一部分功能时;
- 当需要将经常使用的功能进行封装,以提供一个更简洁的接口给客户端使用。
总而言之,外观模式通过引入一个简化的接口,隐藏了复杂系统的复杂性,提供了一个更方便的方式给客户端使用系统的功能。这可以提高代码的可读性、可维护性和灵活性。
⑫享元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享对象来最大限度地减少内存使用和提高性能。
享元模式的核心思想是将对象分为两类:内部状态(Intrinsic State)和外部状态(Extrinsic State)。内部状态是对象可以共享的部分,它不随环境的改变而改变。外部状态则是对象依赖的、随环境的改变而改变的部分。
为了实现对象的共享,享元模式引入了一个享元工厂(Flyweight Factory),它负责创建和管理共享的享元对象。
// 享元接口
interface Flyweight {
void operation();
}
// 具体享元类
class ConcreteFlyweight implements Flyweight {
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation() {
System.out.println("ConcreteFlyweight: " + intrinsicState);
}
}
// 享元工厂类
class FlyweightFactory {
private Map<String, Flyweight> flyweights;
public FlyweightFactory() {
flyweights = new HashMap<>();
}
public Flyweight getFlyweight(String key) {
if (flyweights.containsKey(key)) {
return flyweights.get(key);
} else {
Flyweight flyweight = new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
return flyweight;
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
// 通过享元工厂获取享元对象
Flyweight flyweight1 = factory.getFlyweight("key1");
Flyweight flyweight2 = factory.getFlyweight("key2");
Flyweight flyweight3 = factory.getFlyweight("key1");
// 调用享元对象的操作方法
flyweight1.operation();
flyweight2.operation();
flyweight3.operation();
}
}
在上述示例中,我们有一个享元接口(Flyweight),定义了享元对象的操作方法
operation()
。然后,我们有一个具体的享元类(ConcreteFlyweight),它实现了享元接口。具体享元类包含了内部状态(intrinsicState)作为私有成员变量,该变量在创建对象时初始化,并在调用
operation()
方法时使用。我们还有一个享元工厂类(FlyweightFactory),它负责创建和管理享元对象。享元工厂类维护一个享元对象的集合(通常使用哈希表或缓存),以便根据需要返回共享的对象。在每次请求享元对象时,享元工厂首先检查对象是否已经存在,如果存在则直接返回,否则创建新对象并将其添加到集合中。
在客户端代码中,我们首先创建一个享元工厂对象(FlyweightFactory)。然后,我们通过调用享元工厂的
getFlyweight()
方法来获取享元对象。客户端可以根据需要传递外部状态的参数(例如"key1"和"key2"),享元工厂会返回相应的享元对象。最后,我们调用享元对象的
operation()
方法,它会打印出享元对象的内部状态。享元模式通过共享对象来减少内存使用和提高性能。由于享元对象是共享的,所以可以在多个地方同时使用,而不需要创建多个实例。这对于需要创建大量细粒度对象的场景特别有用。
总而言之,享元模式通过共享对象来优化系统资源的使用。它在需要创建大量相似对象时可以提供显著的性能和内存优势。
⑬代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,它允许通过代理对象控制对实际对象的访问。
代理模式的核心思想是引入一个代理类,该代理类与实际对象实现相同的接口,从而使得客户端可以通过代理对象来间接访问实际对象。代理类在不改变实际对象的功能情况下,可以增加额外的逻辑或控制访问权限。
// 接口
interface Subject {
void request();
}
// 实际对象
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 代理类
class Proxy implements Subject {
private RealSubject realSubject;
public Proxy() {
realSubject = new RealSubject();
}
@Override
public void request() {
// 在调用实际对象之前或之后添加额外的逻辑
System.out.println("Proxy: Pre-processing request.");
realSubject.request();
System.out.println("Proxy: Post-processing request.");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy();
// 通过代理对象调用实际对象的方法
proxy.request();
}
}
在上述示例中,我们有一个接口(Subject),定义了实际对象和代理对象共同拥有的方法
request()
。然后,我们有一个实际对象(RealSubject),它实现了接口。实际对象负责处理具体的请求。
我们还有一个代理类(Proxy),它也实现了接口。代理类内部包含一个实际对象作为成员变量,并在调用
request()
方法时通过实际对象来处理请求。在调用实际对象之前或之后,代理类可以添加额外的逻辑,如输出日志、权限控制等。在客户端代码中,我们创建了一个代理对象(Proxy)。然后,我们通过调用代理对象的
request()
方法来间接访问实际对象。客户端代码不需要直接与实际对象交互,它只需与代理对象交互即可。代理模式的优点包括:
- 代理类可以控制对实际对象的访问,从而增加额外的逻辑或控制访问权限。
- 代理模式可以实现懒加载,延迟创建实际对象,节省系统资源。
- 代理模式可以隐藏实际对象的具体实现细节,保护实际对象的安全性。
总而言之,代理模式通过引入代理对象来控制对实际对象的访问。代理对象与实际对象实现相同的接口,使得客户端可以通过代理对象来间接操作实际对象,并且可以在不改变实际对象的功能情况下增加额外的逻辑或控制访问权限。
⑭责任链模式
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它将请求的发送者和接收者解耦,并通过一条由多个处理对象组成的责任链来处理请求。
责任链模式的核心思想是将多个处理对象按照其在责任链中的顺序连接在一起,当请求发生时,从第一个处理对象开始,如果该对象可以处理请求,就处理请求并结束;否则,将请求传递给下一个处理对象,直到请求被处理或者责任链的末端。
// 抽象处理者
abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void handleRequest(int request);
}
// 具体处理者A
class ConcreteHandlerA extends Handler {
@Override
public void handleRequest(int request) {
if (request >= 0 && request < 10) {
System.out.println("ConcreteHandlerA: Handling request " + request);
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
// 具体处理者B
class ConcreteHandlerB extends Handler {
@Override
public void handleRequest(int request) {
if (request >= 10 && request < 20) {
System.out.println("ConcreteHandlerB: Handling request " + request);
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
// 具体处理者C
class ConcreteHandlerC extends Handler {
@Override
public void handleRequest(int request) {
if (request >= 20 && request < 30) {
System.out.println("ConcreteHandlerC: Handling request " + request);
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
Handler handlerC = new ConcreteHandlerC();
// 构建责任链
handlerA.setSuccessor(handlerB);
handlerB.setSuccessor(handlerC);
// 发送请求
handlerA.handleRequest(5);
handlerA.handleRequest(15);
handlerA.handleRequest(25);
}
}
在上述示例中,我们有一个抽象处理者(Handler),它定义了处理请求的接口
handleRequest(int request)
并包含一个后继处理者(successor)的引用。然后,我们有三个具体处理者(ConcreteHandlerA、ConcreteHandlerB和ConcreteHandlerC),它们分别实现了抽象处理者的接口。每个具体处理者根据自己的责任范围来判断是否能够处理请求,如果可以,则处理请求;否则,将请求传递给下一个处理者。
在客户端代码中,我们创建了三个具体处理者对象。然后,我们按顺序将它们连接在一起,构建了一个责任链。首先,将请求发送给第一个处理者(handlerA)。如果第一个处理者无法处理请求,则将请求传递给下一个处理者(handlerB),以此类推。
最后,我们通过调用第一个处理者的
handleRequest()
方法来发送请求。请求将从第一个处理者开始,按照责任链的顺序依次传递,直到被某个处理者处理或者到达责任链的末端。责任链模式的优点包括:
- 将请求的发送者和接收者解耦,客户端代码不需要知道请求由哪个具体处理者处理。
- 可以灵活地增加或修改责任链中的处理者,而无需修改客户端代码。
- 具体处理者可以根据需要选择是否继续传递请求或者中断处理。
总而言之,责任链模式通过构建一个由多个处理对象组成的责任链,将请求的发送者和接收者解耦,并且可以灵活地处理请求。每个处理者根据自己的责任范围来判断是否能够处理请求,从而形成一条处理请求的责任链。
⑮命令模式
命令模式(Command Pattern)是一种行为型设计模式,它将一个请求封装为一个对象,从而使用户可以用不同的请求对客户进行参数化。
命令模式包含以下几个关键角色:
- Command(命令):定义了执行操作的接口,通常包含
execute()
方法。- ConcreteCommand(具体命令):实现了Command接口,具体命令对象与接收者相关联,并负责调用接收者的相应方法。
- Receiver(接收者):执行具体操作的对象。
- Invoker(调用者):调用命令对象执行请求的对象。
- Client(客户端):创建具体命令对象并设置其接收者,然后将命令对象传递给调用者执行。
// Command(命令)接口
interface Command {
void execute();
}
// 具体命令类A
class ConcreteCommandA implements Command {
private Receiver receiver;
public ConcreteCommandA(Receiver receiver) {
this.receiver = receiver;
}
public void execute() {
receiver.actionA();
}
}
// 具体命令类B
class ConcreteCommandB implements Command {
private Receiver receiver;
public ConcreteCommandB(Receiver receiver) {
this.receiver = receiver;
}
public void execute() {
receiver.actionB();
}
}
// 接收者类
class Receiver {
public void actionA() {
System.out.println("Receiver: Executing action A.");
}
public void actionB() {
System.out.println("Receiver: Executing action B.");
}
}
// 调用者类
class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void executeCommand() {
command.execute();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Receiver receiver = new Receiver();
Command commandA = new ConcreteCommandA(receiver);
Command commandB = new ConcreteCommandB(receiver);
Invoker invoker = new Invoker();
invoker.setCommand(commandA);
invoker.executeCommand();
invoker.setCommand(commandB);
invoker.executeCommand();
}
}
在上述示例中,我们有一个Command接口,定义了
execute()
方法。然后,我们有两个具体命令类(ConcreteCommandA和ConcreteCommandB),它们实现了Command接口,并与接收者(Receiver)相关联。具体命令类负责调用接收者的相应方法。
我们还有一个接收者类(Receiver),其中包含两个执行具体操作的方法(actionA和actionB)。
调用者类(Invoker)负责调用命令对象的执行方法。它具有
setCommand()
方法用于设置命令对象,以及executeCommand()
方法来执行命令。在客户端代码中,我们创建了一个接收者对象和两个具体命令对象。然后,我们通过调用者对象将命令对象设置为具体命令A,并执行命令;接着将命令对象设置为具体命令B,并再次执行命令。
命令模式的优点包括:
- 解耦了请求的发送者和接收者,使得可以很容易地新增、修改和删除命令,而不影响其他部分的代码。
- 可以将请求参数化,即通过传递不同的命令对象来实现不同的操作。
- 支持撤销和重做操作。
总而言之,命令模式通过将请求封装成一个对象,将发送者和接收者解耦,使得请求的发送者不需要知道请求的具体执行过程。这样提高了系统的灵活性和可扩展性,并且支持撤销和重做操作。
⑯解释器模式
解释器模式(Interpreter Pattern)是一种行为型设计模式,它定义了一种语言文法的表示,并确定了该语言的解释方式。使用解释器模式,我们可以定义一个语言并解释该语言中的句子或表达式。
解释器模式包含以下几个关键角色:
- AbstractExpression(抽象表达式):定义了一个抽象的解释方法
interpret()
,所有具体表达式都要实现这个方法。- TerminalExpression(终结符表达式):终结符表达式代表语言中的基本单元,它实现了抽象表达式接口的解释方法。终结符表达式通常对应语法中的终结符。
- NonterminalExpression(非终结符表达式):非终结符表达式由终结符表达式和其他非终结符表达式组成,它实现了抽象表达式接口的解释方法。非终结符表达式通常对应语法中的非终结符。
- Context(上下文):包含解释器需要的一些全局信息,它可以是解释器的输入。
- Client(客户端):创建抽象表达式对象,并配置各个表达式之间的关系,最终调用解释方法进行解释。
// 抽象表达式
interface AbstractExpression {
void interpret(Context context);
}
// 终结符表达式
class TerminalExpression implements AbstractExpression {
public void interpret(Context context) {
System.out.println("终结符表达式解释:" + context.getInput());
}
}
// 非终结符表达式
class NonterminalExpression implements AbstractExpression {
private AbstractExpression expression1;
private AbstractExpression expression2;
public NonterminalExpression(AbstractExpression expression1, AbstractExpression expression2) {
this.expression1 = expression1;
this.expression2 = expression2;
}
public void interpret(Context context) {
System.out.println("非终结符表达式解释");
expression1.interpret(context);
expression2.interpret(context);
}
}
// 上下文
class Context {
private String input;
public Context(String input) {
this.input = input;
}
public String getInput() {
return input;
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Context context = new Context("Hello, World!");
AbstractExpression expression1 = new TerminalExpression();
AbstractExpression expression2 = new NonterminalExpression(expression1, expression1);
expression2.interpret(context);
}
}
在上述示例中,我们有一个抽象表达式接口(AbstractExpression),其中定义了一个解释方法
interpret()
。然后,我们有两个具体表达式类:终结符表达式(TerminalExpression)和非终结符表达式(NonterminalExpression)。终结符表达式实现了
interpret()
方法,输出终结符表达式解释的内容。非终结符表达式包含两个表达式对象,并在解释方法中依次调用这两个对象的解释方法。我们还有一个上下文类(Context),它包含解释器需要的一些全局信息,这里只有一个输入字符串。
在客户端代码中,我们创建了一个上下文对象,并创建了一个终结符表达式对象和一个非终结符表达式对象。然后,我们调用非终结符表达式的解释方法进行解释。
解释器模式的优点包括:
- 扩展语言很方便,只需要修改或新增表达式类。
- 易于实现文法规则,每个语法规则都可以表示为一个表达式对象。
- 易于实现对文法的解释。
- 容易构建语法树,每个非终结符节点可以作为子表达式。
总而言之,解释器模式通过定义一种语言文法的表示,并确定解释方式,使得可以解释该语言中的句子或表达式。它适用于需要解释和执行一些规则或脚本的场景。
⑰迭代器模式
迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供了一种顺序访问聚合对象中各个元素的方法,而又不需要暴露该对象内部表示。通过使用迭代器模式,我们可以在不暴露聚合对象内部结构的情况下,对聚合对象进行遍历操作。
迭代器模式包含以下几个关键角色:
- Iterator(迭代器接口):定义了访问和遍历聚合对象元素的方法。
- ConcreteIterator(具体迭代器):实现Iterator接口,通过追踪聚合对象的当前位置来管理遍历。
- Aggregate(聚合对象接口):定义创建迭代器对象的方法。
- ConcreteAggregate(具体聚合对象):实现Aggregate接口,创建具体迭代器对象,并提供数据集合。
- Client(客户端):使用迭代器对象遍历聚合对象中的元素。
// 迭代器接口
interface Iterator {
boolean hasNext();
Object next();
}
// 具体迭代器
class ConcreteIterator implements Iterator {
private String[] collection;
private int index;
public ConcreteIterator(String[] collection) {
this.collection = collection;
this.index = 0;
}
public boolean hasNext() {
return index < collection.length;
}
public Object next() {
return collection[index++];
}
}
// 聚合对象接口
interface Aggregate {
Iterator createIterator();
}
// 具体聚合对象
class ConcreteAggregate implements Aggregate {
private String[] collection;
public ConcreteAggregate() {
this.collection = new String[]{"Apple", "Banana", "Orange"};
}
public Iterator createIterator() {
return new ConcreteIterator(collection);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Aggregate aggregate = new ConcreteAggregate();
Iterator iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
在上述示例中,我们有一个迭代器接口(Iterator),其中定义了两个方法:
hasNext()
判断是否还有下一个元素,next()
返回下一个元素。然后,我们有一个具体迭代器类(ConcreteIterator),它实现了迭代器接口。具体迭代器维护了一个对聚合对象的引用,并通过追踪当前位置来管理遍历。
我们还有一个聚合对象接口(Aggregate),其中定义了一个创建迭代器对象的方法。
最后,我们有一个具体聚合对象类(ConcreteAggregate),它实现了聚合对象接口。具体聚合对象维护了一个数据集合,并通过创建具体迭代器对象来对集合进行遍历。
在客户端代码中,我们创建了一个具体聚合对象,并调用其
createIterator()
方法来获取迭代器对象。然后,我们使用迭代器对象遍历聚合对象中的元素,并逐个打印出来。迭代器模式的优点包括:
- 分离了聚合对象和遍历行为,使得聚合对象可以独立变化。
- 简化了聚合对象的接口,客户端只需要通过迭代器来访问聚合对象。
- 支持多种遍历方式,客户端可以根据需要选择不同的迭代器。
总而言之,迭代器模式通过提供一种标准的遍历方式,使得可以顺序访问聚合对象中的元素,而又不需要暴露其内部结构。它适用于需要访问聚合对象中的元素,并且希望在不暴露其内部细节的情况下进行操作的场景。
⑱中介者模式
中介者模式(Mediator Pattern)是一种行为型设计模式,通过引入一个中介者对象,来解耦和集中一组对象之间的交互。中介者模式将对象之间的通信转移为与中介者对象的通信,从而减少对象之间的直接依赖关系。
中介者模式包含以下几个关键角色:
- Mediator(中介者接口):定义了各个同事对象之间通信的抽象方法。
- ConcreteMediator(具体中介者):实现了中介者接口,协调各个同事对象之间的交互,并负责控制各个同事对象的行为。
- Colleague(同事对象接口):定义了同事对象之间通信的方法。
- ConcreteColleague(具体同事对象):实现同事对象接口,每个具体同事对象都知道中介者对象,并通过中介者对象来与其他同事对象通信。
// 中介者接口
interface Mediator {
void send(String message, Colleague colleague);
}
// 具体中介者
class ConcreteMediator implements Mediator {
private Colleague colleagueA;
private Colleague colleagueB;
public void setColleagueA(Colleague colleagueA) {
this.colleagueA = colleagueA;
}
public void setColleagueB(Colleague colleagueB) {
this.colleagueB = colleagueB;
}
public void send(String message, Colleague colleague) {
if (colleague == colleagueA) {
colleagueB.receive(message);
} else if (colleague == colleagueB) {
colleagueA.receive(message);
}
}
}
// 同事对象接口
interface Colleague {
void send(String message);
void receive(String message);
}
// 具体同事对象
class ConcreteColleagueA implements Colleague {
private Mediator mediator;
public ConcreteColleagueA(Mediator mediator) {
this.mediator = mediator;
}
public void send(String message) {
mediator.send(message, this);
}
public void receive(String message) {
System.out.println("ConcreteColleagueA received: " + message);
}
}
// 具体同事对象
class ConcreteColleagueB implements Colleague {
private Mediator mediator;
public ConcreteColleagueB(Mediator mediator) {
this.mediator = mediator;
}
public void send(String message) {
mediator.send(message, this);
}
public void receive(String message) {
System.out.println("ConcreteColleagueB received: " + message);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleagueA colleagueA = new ConcreteColleagueA(mediator);
ConcreteColleagueB colleagueB = new ConcreteColleagueB(mediator);
mediator.setColleagueA(colleagueA);
mediator.setColleagueB(colleagueB);
colleagueA.send("Hello, colleagueB!");
colleagueB.send("Hi, colleagueA!");
}
}
在上述示例中,我们有一个中介者接口(Mediator),其中定义了用于同事对象之间通信的抽象方法
send()
。我们还有一个具体中介者类(ConcreteMediator),它实现了中介者接口。具体中介者维护了对各个同事对象的引用,并在
send()
方法中根据不同的同事对象向其他同事对象发送消息。我们有一个同事对象接口(Colleague),其中定义了用于与中介者对象通信的方法。
然后,我们有两个具体同事对象类(ConcreteColleagueA和ConcreteColleagueB),它们分别实现了同事对象接口。每个具体同事对象都持有对中介者对象的引用,并通过中介者对象来发送消息和接收消息。
在客户端代码中,我们创建了一个具体中介者对象和两个具体同事对象。然后,将同事对象分别设置到中介者对象中。最后,我们通过调用同事对象的
send()
方法来发送消息,并通过中介者对象来协调其他同事对象的行为。中介者模式的优点包括:
- 减少了对象之间的直接依赖关系,使得对象之间更加解耦。
- 集中了对象之间的交互逻辑,使得它们之间的通信变得简单明确。
- 可以添加新的同事对象和新的中介者对象,而不需要修改原有的对象。
总而言之,中介者模式通过引入一个中介者对象,将对象之间的交互转移为与中介者对象的通信,从而降低了对象之间的耦合度。它适用于一组对象之间存在复杂的、紧密的交互关系的场景。
⑲备忘录模式
备忘录模式(Memento Pattern)是一种行为型设计模式,用于保存和恢复对象的内部状态,而不破坏其封装性。备忘录模式通过在不破坏对象封装性的前提下,捕获并保存对象的内部状态,并且可以将对象恢复到之前的某个状态。
备忘录模式涉及以下几个关键角色:
- Originator(原发器):负责创建一个备忘录对象,以记录当前时刻自身的内部状态,并可以使用备忘录对象恢复到之前的状态。
- Memento(备忘录):用于存储Originator的内部状态。
- Caretaker(负责人):负责保存备忘录对象,并不对备忘录进行操作或检查备忘录的内容。
// 备忘录类
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 原发器类
class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento saveStateToMemento() {
return new Memento(state);
}
public void restoreStateFromMemento(Memento memento) {
state = memento.getState();
}
}
// 负责人类
class Caretaker {
private Memento memento;
public void saveMemento(Memento memento) {
this.memento = memento;
}
public Memento retrieveMemento() {
return memento;
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
// 设置初始状态
originator.setState("State 1");
System.out.println("Current state: " + originator.getState());
// 保存备忘录
caretaker.saveMemento(originator.saveStateToMemento());
// 修改状态
originator.setState("State 2");
System.out.println("Current state: " + originator.getState());
// 恢复到之前的状态
originator.restoreStateFromMemento(caretaker.retrieveMemento());
System.out.println("Restored state: " + originator.getState());
}
}
在上述示例中,我们有一个备忘录类(Memento),它存储了原发器对象的内部状态。
我们还有一个原发器类(Originator),它负责创建备忘录对象,并可以使用备忘录对象恢复到之前的状态。
然后,我们有一个负责人类(Caretaker),它保存备忘录对象,并且不对备忘录进行操作或检查备忘录的内容。
在客户端代码中,我们创建了一个原发器对象和一个负责人对象。首先,我们设置了原发器的初始状态并打印出当前状态。然后,我们保存了一个备忘录对象,接着修改了原发器的状态并打印出当前状态。最后,我们使用负责人对象中保存的备忘录对象来恢复原发器的状态,并打印出恢复后的状态。
备忘录模式的优点包括:
- 可以在不破坏封装性的情况下捕获和恢复对象的内部状态。
- 简化了原发器类,因为它无需管理保存的状态的版本和历史记录。
- 可以灵活地保存不同时间点的对象状态。
总而言之,备忘录模式允许我们在不破坏对象封装性的前提下,保存和恢复对象的内部状态。它适用于需要保存或恢复对象状态的场景,例如撤销操作、历史记录等。
⑳观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,用于定义对象之间的一种一对多的依赖关系,当一个对象的状态发生变化时,它的所有依赖对象都会收到通知并自动更新。
观察者模式涉及以下几个关键角色:
- Subject(主题):也称为被观察者或可观察对象,负责维护一个观察者列表,提供注册和取消注册观察者的方法,并在状态发生改变时通知观察者。
- Observer(观察者):定义一个更新接口,用于接收主题的通知,并进行相应的更新操作。
- ConcreteSubject(具体主题):具体的主题实现类,继承自Subject,实现相应的注册、取消注册以及状态改变通知的方法。
- ConcreteObserver(具体观察者):具体的观察者实现类,实现Observer接口中的更新方法,并根据需要进行相应的业务处理。
import java.util.ArrayList;
import java.util.List;
// 主题接口
interface Subject {
void registerObserver(Observer observer);
void unregisterObserver(Observer observer);
void notifyObservers();
}
// 观察者接口
interface Observer {
void update(String message);
}
// 具体主题类
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String message;
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void unregisterObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(message);
}
}
public void setMessage(String message) {
this.message = message;
notifyObservers();
}
}
// 具体观察者类
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received a message: " + message);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver("Observer 1");
ConcreteObserver observer2 = new ConcreteObserver("Observer 2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.setMessage("Hello, observers!");
subject.unregisterObserver(observer1);
subject.setMessage("Observer 1 has been unregistered.");
}
}
在上述示例中,我们有一个主题接口(Subject),定义了注册观察者、取消注册观察者以及通知观察者的方法。
我们还有一个观察者接口(Observer),定义了更新接口,用于接收主题的通知并进行相应的业务处理。
具体主题类(ConcreteSubject)和具体观察者类(ConcreteObserver)分别实现了主题接口和观察者接口,并实现了相应的方法。
在客户端代码中,我们创建了一个具体主题对象(subject)和两个具体观察者对象(observer1和observer2)。首先,我们注册了两个观察者对象到主题对象中。然后,我们调用主题对象的setMessage方法来改变状态并通知观察者。每个观察者对象都收到了通知并进行相应的更新操作。然后,我们取消注册一个观察者对象,并再次调用setMessage方法来改变状态并通知观察者。只有一个观察者对象收到了通知,因为另一个观察者已经被取消注册。
观察者模式的优点包括:
- 主题与观察者之间的解耦,主题不需要知道具体观察者的细节,只需要维护观察者列表即可。
- 支持动态添加和移除观察者,使得系统更加灵活。
- 观察者模式符合开闭原则,可以在不修改主题和观察者的情况下增加新的观察者。
总而言之,观察者模式允许多个观察者对象监听一个主题对象,并在状态改变时接收通知并进行相应的业务处理。它适用于需要实现一对多依赖关系的场景,例如事件处理、消息通知等。
㉑状态模式
状态模式(State Pattern)是一种行为型设计模式,用于解决对象在不同状态下具有不同行为的问题。它将对象的状态封装成独立的对象,使得对象在不同状态下可以切换,并且每个状态都具备自己的行为。
状态模式涉及以下几个关键角色:
- Context(上下文):定义客户端所感兴趣的接口,同时维护一个对应当前状态的具体状态对象的引用。
- State(状态):定义了一个接口或抽象类,用于封装与Context的特定状态相关的行为。
- ConcreteState(具体状态):实现了State接口的具体状态类,负责实现与特定状态相关的行为,并在需要时切换到其他状态。
// 上下文类
class Context {
private State state;
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle();
}
}
// 状态接口
interface State {
void handle();
}
// 具体状态类A
class ConcreteStateA implements State {
@Override
public void handle() {
System.out.println("Handle in State A");
}
}
// 具体状态类B
class ConcreteStateB implements State {
@Override
public void handle() {
System.out.println("Handle in State B");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Context context = new Context();
State stateA = new ConcreteStateA();
context.setState(stateA);
context.request(); // 输出: Handle in State A
State stateB = new ConcreteStateB();
context.setState(stateB);
context.request(); // 输出: Handle in State B
}
}
在上述示例中,我们有一个上下文类Context,它维护了一个对当前状态的引用,并定义了一个request方法用于请求处理。
接下来,我们有一个状态接口State,它定义了handle方法来处理特定状态下的行为。
然后,我们有两个具体状态类ConcreteStateA和ConcreteStateB,它们分别实现了State接口,并分别实现了handle方法以表明不同的行为。
在客户端代码中,我们首先创建一个上下文对象Context,然后创建具体状态对象stateA,并将其设置为当前状态。接下来,我们调用Context对象的request方法,该方法会委托当前状态的handle方法进行处理,输出"Handle in State A"。
然后,我们创建具体状态对象stateB并将其设置为当前状态。再次调用Context对象的request方法,输出"Handle in State B"。
通过状态模式,我们可以通过改变上下文对象的状态来改变其行为,而无需修改上下文对象的代码。这提供了一种灵活的方式来处理对象的状态转换,并使得代码更加可扩展和可维护。
总而言之,状态模式将对象的状态封装成独立的对象,并使得对象在不同状态下可以切换,并且每个状态都具备自己的行为。它通过将特定状态的行为封装到独立的状态类中,实现了状态与行为的解耦,提高了代码的可维护性和扩展性。
㉒策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,用于定义一组算法,将每个算法都封装到具有共同接口的独立类中,使得它们之间可以互相替换。这样客户端就可以根据需要选择不同的算法,而无需修改客户端代码。
策略模式涉及以下几个关键角色:
- Context(上下文):定义了一个抽象类或接口,它维持一个对策略类的引用,并提供一个方法来委托具体的策略类进行操作。
- Strategy(策略):定义了一个共同接口,用于封装具体算法的实现。
- ConcreteStrategy(具体策略):实现了策略接口的具体算法类,提供了具体的算法实现。
// 上下文类
class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy() {
strategy.execute();
}
}
// 策略接口
interface Strategy {
void execute();
}
// 具体策略类A
class ConcreteStrategyA implements Strategy {
@Override
public void execute() {
System.out.println("Executing Strategy A");
}
}
// 具体策略类B
class ConcreteStrategyB implements Strategy {
@Override
public void execute() {
System.out.println("Executing Strategy B");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Context context;
// 使用策略A
context = new Context(new ConcreteStrategyA());
context.executeStrategy(); // 输出: Executing Strategy A
// 使用策略B
context = new Context(new ConcreteStrategyB());
context.executeStrategy(); // 输出: Executing Strategy B
}
}
在上述示例中,我们有一个上下文类Context,它维持一个对策略类的引用,并提供了一个executeStrategy方法来委托具体的策略类进行操作。
然后,我们定义了一个策略接口Strategy,它定义了一个execute方法用于封装具体算法的实现。
接下来,我们有两个具体策略类ConcreteStrategyA和ConcreteStrategyB,它们分别实现了Strategy接口,并提供了具体的算法实现。
在客户端代码中,我们首先创建一个上下文对象Context,并传入具体策略对象ConcreteStrategyA,然后调用executeStrategy方法,输出"Executing Strategy A"。
然后,我们再次创建一个上下文对象Context,并传入具体策略对象ConcreteStrategyB,再次调用executeStrategy方法,输出"Executing Strategy B"。
通过策略模式,我们可以将不同的算法封装成独立的策略类,客户端代码只需要与上下文类交互,通过传入不同的策略对象来选择具体的算法。这样可以方便地切换和扩展算法,同时使得客户端代码更加简洁和可读。
总而言之,策略模式将不同的算法封装成独立的策略类,并使得它们可以互相替换,客户端代码只需要与上下文类交互。通过策略模式,可以实现算法的灵活配置和扩展,提高代码的可维护性和复用性。
㉓模板方法模式
模板方法模式(Template Method Pattern)是一种行为型设计模式,用于定义算法的骨架,将算法中可变的部分延迟到子类来实现。模板方法模式通过将具体步骤的实现延迟到子类,使得子类可以在不改变算法结构的情况下重新定义算法的某些步骤。
模板方法模式涉及以下几个关键角色:
- AbstractClass(抽象类):定义了一个模板方法,它包含了算法的骨架,其中的某些步骤由抽象方法或具体方法来实现。抽象类还可以定义钩子方法,用于控制算法的扩展点。
- ConcreteClass(具体类):继承抽象类,并实现其中的抽象方法和具体方法。
// 抽象类
abstract class AbstractClass {
// 模板方法
public void templateMethod() {
step1();
step2();
step3();
}
// 抽象方法,由子类实现
protected abstract void step1();
// 具体方法,子类也可以选择是否覆盖
protected void step2() {
System.out.println("Executing default step2");
}
// 钩子方法,子类可以选择覆盖
protected void step3() {
System.out.println("Executing default step3");
}
}
// 具体类A
class ConcreteClassA extends AbstractClass {
@Override
protected void step1() {
System.out.println("Executing step1 in ConcreteClassA");
}
}
// 具体类B
class ConcreteClassB extends AbstractClass {
@Override
protected void step1() {
System.out.println("Executing step1 in ConcreteClassB");
}
@Override
protected void step2() {
System.out.println("Executing customized step2 in ConcreteClassB");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
AbstractClass objA = new ConcreteClassA();
objA.templateMethod();
AbstractClass objB = new ConcreteClassB();
objB.templateMethod();
}
}
在上述示例中,我们有一个抽象类AbstractClass,它定义了一个模板方法templateMethod,并包含了算法的骨架。其中的步骤step1、step2和step3可以是抽象方法、具体方法或钩子方法,根据具体情况来决定。
然后,我们有两个具体类ConcreteClassA和ConcreteClassB,它们都继承了抽象类AbstractClass,并实现了其中的抽象方法step1。
在客户端代码中,我们首先创建一个具体类对象objA,它是ConcreteClassA的实例,然后调用templateMethod方法。在执行过程中,会按照定义的算法骨架,依次执行step1、step2和step3。输出内容为"Executing step1 in ConcreteClassA"、"Executing default step2"和"Executing default step3"。
然后,我们创建另一个具体类对象objB,它是ConcreteClassB的实例,再次调用templateMethod方法。在执行过程中,同样会按照算法骨架,依次执行step1、step2和step3。但此时由于ConcreteClassB覆盖了step2方法,输出内容为"Executing step1 in ConcreteClassB"和"Executing customized step2 in ConcreteClassB",而step3方法继续使用了默认实现。
通过模板方法模式,我们可以在抽象类中定义算法的骨架,将可变的部分延迟到具体类来实现。这样可以使得算法的结构保持稳定,但某些步骤的具体实现可以有不同的变化,提高代码的可复用性和扩展性。
总而言之,模板方法模式通过定义算法的骨架并延迟具体步骤的实现,实现了算法的稳定性和可变性的分离。通过模板方法模式,可以更好地组织和复用算法,提高代码的灵活性和可维护性。
㉔访问者模式
访问者模式(Visitor Pattern)是一种行为型设计模式,用于将算法与其所作用的对象分离。它允许定义新的操作(访问者),而无需修改现有对象结构(被访问者)。
访问者模式涉及以下几个关键角色:
- Visitor(访问者):定义了对不同类型元素进行访问的方法,每个方法对应一种元素类型。
- ConcreteVisitor(具体访问者):实现了Visitor接口,提供了对每种类型元素的具体访问实现。
- Element(元素):定义了一个accept方法,该方法接受一个访问者对象作为参数,将自身传递给访问者。
- ConcreteElement(具体元素):实现了Element接口,提供了accept方法的具体实现。
- ObjectStructure(对象结构):存储了一组元素,并提供了遍历这些元素的方法。
// 访问者接口
interface Visitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
// 具体访问者A
class ConcreteVisitorA implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("ConcreteVisitorA visits ConcreteElementA");
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("ConcreteVisitorA visits ConcreteElementB");
}
}
// 具体访问者B
class ConcreteVisitorB implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("ConcreteVisitorB visits ConcreteElementA");
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("ConcreteVisitorB visits ConcreteElementB");
}
}
// 元素接口
interface Element {
void accept(Visitor visitor);
}
// 具体元素A
class ConcreteElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体元素B
class ConcreteElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 对象结构
class ObjectStructure {
private List<Element> elements = new ArrayList<>();
public void addElement(Element element) {
elements.add(element);
}
public void removeElement(Element element) {
elements.remove(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.addElement(new ConcreteElementA());
objectStructure.addElement(new ConcreteElementB());
Visitor visitorA = new ConcreteVisitorA();
objectStructure.accept(visitorA);
Visitor visitorB = new ConcreteVisitorB();
objectStructure.accept(visitorB);
}
}
在上述示例中,我们首先定义了访问者接口Visitor,它包含了对不同类型元素进行访问的方法。然后我们有两个具体访问者ConcreteVisitorA和ConcreteVisitorB,它们实现了Visitor接口,并提供了对每种类型元素的具体访问实现。
接着,我们定义了元素接口Element,其中包含一个accept方法,该方法接受一个访问者对象作为参数,并将自身传递给访问者。具体元素ConcreteElementA和ConcreteElementB实现了Element接口,并提供了accept方法的具体实现。
我们还有一个对象结构ObjectStructure,它存储了一组元素,并提供了添加、删除和遍历元素的方法。在其中的accept方法中,它会依次调用每个元素的accept方法,将自身传递给访问者进行访问。
最后,在客户端代码中,我们创建了一个对象结构objectStructure,并向其中添加了ConcreteElementA和ConcreteElementB两个元素。然后,我们创建了两个具体访问者visitorA和visitorB,并分别调用objectStructure的accept方法,将不同的访问者传递给它。在accept方法的内部,会通过循环遍历每个元素,并调用其accept方法,将访问者对象传递给它。
输出结果将根据访问者对象的不同而有所不同,访问者将根据元素的类型执行相应的操作。
通过访问者模式,我们可以定义新的操作(访问者)而无需修改现有的元素结构,从而实现了算法与对象结构的分离。这样可以提供更多的灵活性,同时也符合开闭原则,即对扩展开放,对修改关闭。
总而言之,访问者模式通过将算法与对象结构分离,使得我们可以在不修改现有对象结构的情况下定义新的操作。它提供了一种可扩展且易于维护的设计方式,适用于某些需要对对象结构进行复杂操作的场景。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)