Java语言中常用的设计模式包括23种经典设计模式,它们分为三大类:创建型、结构型和行为型。
创建型模式(Creational Patterns):这类模式关注对象的创建过程,将对象的创建和使用分离,以提高系统的灵活性和可扩展性。包括以下几种:
- 单例模式(Singleton)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
- 建造者模式(Builder)
- 原型模式(Prototype)
结构型模式(Structural Patterns):这类模式关注如何组合类和对象以形成更大的结构,主要用于处理类和对象的组合。包括以下几种:
- 适配器模式(Adapter)
- 桥接模式(Bridge)
- 组合模式(Composite)
- 装饰器模式(Decorator)
- 外观模式(Facade)
- 享元模式(Flyweight)
- 代理模式(Proxy)
行为型模式(Behavioral Patterns):这类模式关注对象之间的通信,主要用于描述程序在运行时复杂的控制流程。包括以下几种:
- 责任链模式(Chain of Responsibility)
- 命令模式(Command)
- 解释器模式(Interpreter)
- 迭代器模式(Iterator)
- 中介者模式(Mediator)
- 备忘录模式(Memento)
- 观察者模式(Observer)
- 状态模式(State)
- 策略模式(Strategy)
- 模板方法模式(Template Method)
- 访问者模式(Visitor)
创建型
单例模式
单例模式介绍
单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点来访问该实例。这意味着无论何时请求该类的实例,都将返回相同的实例。
单例模式的特点:
- 单一实例: 一个类只能有一个实例。
- 全局访问点: 提供一个全局的访问点,使得其他对象可以轻松地访问到这个实例。
- 延迟实例化: 实例化过程被延迟到第一次请求该实例时。
单例模式的优点:
- 节省资源: 由于只有一个实例,节省了内存和系统资源。
- 易于控制: 由于单例模式限制了实例化过程,因此易于对实例化过程进行控制。
- 易于扩展: 对单例类的访问点是全局的,因此易于对其功能进行扩展和修改。
单例模式的实现方式:
- 懒汉式: 第一次调用时创建实例。
- 饿汉式: 在类加载时创建实例。
- 双重检查锁定: 通过双重检查加锁来实现延迟加载和线程安全。
- 静态内部类: 利用静态内部类特点实现延迟加载和线程安全。
- 枚举: 利用枚举的特性实现单例。
示例代码(懒汉式)
public class Singleton {
private static Singleton instance;
// 私有构造方法,防止外部实例化
private Singleton() {}
// 获取实例的静态方法
public static Singleton getInstance() {
// 如果实例为 null,则创建新实例
if (instance == null) {
instance = new Singleton();
}
// 返回实例
return instance;
}
}
在以上示例中,getInstance() 方法是获取 Singleton 类实例的入口。如果实例为 null,则创建一个新的实例并返回,否则直接返回现有的实例。由于构造方法是私有的,因此外部无法直接实例化该类,只能通过 getInstance() 方法获取实例。
需要注意的是,懒汉式单例模式在多线程环境下可能存在线程安全问题,可以通过双重检查锁定或者静态内部类等方式来解决。
示例代码(饿汉式)
public class Singleton {
// 在类加载时就创建实例,并赋值给静态变量
private static final Singleton instance = new Singleton();
// 私有构造方法,防止外部实例化
private Singleton() {}
// 获取实例的静态方法
public static Singleton getInstance() {
// 直接返回在类加载时创建的实例
return instance;
}
}
在以上示例中,instance 变量在类加载时就被创建,并赋值给静态变量,因此在整个应用程序的生命周期中,只会存在一个 Singleton 类的实例。
由于在类加载时就创建了实例,饿汉式单例模式没有延迟加载的特性。虽然在大多数情况下这不是问题,但如果实例化过程较为复杂或者需要耗费大量资源,可能会影响程序启动速度。
工厂方法模式
工厂方法模式介绍
工厂方法模式是一种创建型设计模式,其主要目的是定义一个用于创建对象的接口,但让子类决定实例化哪个类。这种模式通过将实例化任务委托给子类来处理对象的创建,而不是在父类中直接创建对象。这样做的好处是可以将对象的创建与使用代码解耦,从而使代码更加灵活、可扩展和可维护。
在工厂方法模式中,通常会有一个抽象的工厂类,其中包含一个用于创建产品对象的抽象方法,称为工厂方法。具体的产品由具体的工厂子类来实现,并在其工厂方法中创建具体的产品对象。客户端代码通过与抽象工厂类和产品类交互,而无需直接与具体的产品类交互,从而实现了松耦合。
工厂方法模式通常用于以下情况:
- 当一个类无法预知它所必须创建的对象的类时。
- 当一个类希望将对象的创建延迟到其子类时。
- 当一个类希望将对象的创建委托给多个帮助子类中的某个特定子类时。
总的来说,工厂方法模式提供了一种将对象创建与使用代码分离的灵活方式,有助于更好地组织代码结构并实现代码重用。
工厂方法模式优点
- 符合开闭原则(Open-Closed Principle): 工厂方法模式通过引入抽象工厂类和具体工厂子类的方式,使得新增产品时无需修改现有代码,只需要添加新的具体工厂子类即可,符合开闭原则。
- 更好的封装性: 客户端代码只需要与抽象工厂类和产品类交互,无需关心具体的产品类,从而使得客户端代码与具体产品类解耦,提高了代码的封装性。
- 更好的扩展性: 可以通过添加新的具体工厂子类和产品类来扩展系统,而无需修改现有代码,扩展性好。
- 更好的代码组织性: 工厂方法模式将对象的创建逻辑封装到具体的工厂子类中,使得代码结构更加清晰,易于理解和维护。
- 多态性的应用: 工厂方法模式通过多态性的应用,使得客户端代码可以统一通过抽象工厂类来调用工厂方法,而具体的工厂子类根据需求返回不同的产品对象,实现了对象的动态切换和灵活性。
示例代码
// 产品接口
interface Product {
void operation();
}
// 具体产品类A
class ConcreteProductA implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductA operation");
}
}
// 具体产品类B
class ConcreteProductB implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductB operation");
}
}
// 抽象工厂类
interface Factory {
Product createProduct();
}
// 具体工厂类A,用于创建产品A
class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂类B,用于创建产品B
class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
// 客户端代码
public class Main {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Product productA = factoryA.createProduct();
productA.operation();
Factory factoryB = new ConcreteFactoryB();
Product productB = factoryB.createProduct();
productB.operation();
}
}
在这个示例中,Product 是产品接口,定义了产品的操作方法。ConcreteProductA 和 ConcreteProductB 是具体产品类,实现了 Product 接口,分别代表不同的产品。
Factory 是抽象工厂接口,定义了一个工厂方法 createProduct(),用于创建产品对象。ConcreteFactoryA 和 ConcreteFactoryB 是具体工厂类,分别实现了 Factory 接口,负责创建具体的产品对象。
在客户端代码中,可以根据需要选择合适的具体工厂类来创建产品对象,并调用产品对象的操作方法。
抽象工厂模式
抽象工厂模式介绍
抽象工厂模式是一种创建型设计模式,它提供了一种将相关或相互依赖对象的创建封装在一个工厂接口的方法中,而不需要指定它们具体的类。抽象工厂模式是工厂方法模式的一种延伸,它在工厂方法模式的基础上提供了一种组织多个工厂方法的结构。
在抽象工厂模式中,有两个抽象层次:抽象工厂和具体工厂。抽象工厂定义了一个或多个工厂方法,用于创建一组相关或依赖的产品对象。具体工厂实现了抽象工厂中定义的工厂方法,负责创建具体的产品对象。
与工厂方法模式相比,抽象工厂模式更加灵活,可以创建一组相关的产品对象,并且可以在不修改客户端代码的情况下改变所创建的产品组合。同时,抽象工厂模式也更加复杂,因为它需要定义多个工厂方法和相关的产品接口。
抽象工厂模式通常应用于以下情况:
- 需要创建一组相关或相互依赖的产品对象。
- 客户端不需要关心具体的产品类,只需要与抽象工厂和抽象产品交互。
- 需要在运行时切换不同的产品组合。
下面是抽象工厂模式的一些关键角色:
- AbstractFactory(抽象工厂):声明了一组创建抽象产品的方法,每个方法对应一种产品。
- ConcreteFactory(具体工厂):实现了抽象工厂中定义的创建产品的方法,负责创建具体的产品对象。
- AbstractProduct(抽象产品):定义了产品的接口,描述了产品的操作。
- ConcreteProduct(具体产品):实现了抽象产品接口,是具体的产品对象。
抽象工厂模式的优点
- 提供了一种创建一组相关产品的方式,使得客户端代码与具体产品的创建过程解耦。
- 支持产品族的概念,可以在不修改客户端代码的情况下改变产品组合。
- 符合开闭原则,可以轻松地添加新的产品组合。
然而,抽象工厂模式也有缺点,包括:
- 增加了系统的复杂度,因为需要定义多个工厂方法和相关的产品接口。
- 不太容易支持新种类的产品,需要修改抽象工厂接口以及所有的具体工厂类。
抽象工厂模式在软件设计中广泛应用,特别是在需要创建一组相关产品对象并且需要支持产品族概念的场景下。
示例代码
// 抽象产品A接口
interface AbstractProductA {
void operationA();
}
// 具体产品A1
class ConcreteProductA1 implements AbstractProductA {
@Override
public void operationA() {
System.out.println("ConcreteProductA1 operationA");
}
}
// 具体产品A2
class ConcreteProductA2 implements AbstractProductA {
@Override
public void operationA() {
System.out.println("ConcreteProductA2 operationA");
}
}
// 抽象产品B接口
interface AbstractProductB {
void operationB();
}
// 具体产品B1
class ConcreteProductB1 implements AbstractProductB {
@Override
public void operationB() {
System.out.println("ConcreteProductB1 operationB");
}
}
// 具体产品B2
class ConcreteProductB2 implements AbstractProductB {
@Override
public void operationB() {
System.out.println("ConcreteProductB2 operationB");
}
}
// 抽象工厂接口
interface AbstractFactory {
AbstractProductA createProductA();
AbstractProductB createProductB();
}
// 具体工厂1,用于创建产品族1
class ConcreteFactory1 implements AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}
}
// 具体工厂2,用于创建产品族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) {
AbstractFactory factory1 = new ConcreteFactory1();
AbstractProductA productA1 = factory1.createProductA();
AbstractProductB productB1 = factory1.createProductB();
productA1.operationA();
productB1.operationB();
AbstractFactory factory2 = new ConcreteFactory2();
AbstractProductA productA2 = factory2.createProductA();
AbstractProductB productB2 = factory2.createProductB();
productA2.operationA();
productB2.operationB();
}
}
在这个示例中,有两个产品族:产品族1包括产品A1和产品B1,产品族2包括产品A2和产品B2。每个产品族有两个产品,分别是A和B。AbstractProductA 和 AbstractProductB 是抽象产品接口,定义了产品的操作方法。ConcreteProductA1、ConcreteProductA2、ConcreteProductB1 和 ConcreteProductB2 是具体产品类,实现了抽象产品接口。
AbstractFactory 是抽象工厂接口,定义了创建产品A和产品B的方法。ConcreteFactory1 和 ConcreteFactory2 是具体工厂类,分别实现了抽象工厂接口,负责创建产品族1和产品族2的产品。
在客户端代码中,可以根据需要选择合适的具体工厂来创建对应产品族的产品对象,并调用产品对象的操作方法。
建造者模式
建造者模式介绍
建造者模式是一种创建型设计模式,用于将一个复杂对象的构建过程和其表示分离,以便相同的构建过程可以创建不同的表示。
建造者模式通常用于创建复杂的对象,这些对象由多个部分组成,并且构建过程中的步骤较多或顺序不固定。通过将构建过程分解为多个步骤,并由具体的建造者负责每个步骤的实现,可以简化构建过程,使得客户端代码更加清晰。
以下是建造者模式的一些关键角色:
- Director(指挥者):负责使用建造者对象构建复杂对象的算法,指导构建过程的顺序。
- Builder(建造者):声明了构建复杂对象各个部分的抽象接口,通常包括多个方法用于构建不同部分的对象。
- ConcreteBuilder(具体建造者):实现了建造者接口,负责实现构建复杂对象各个部分的具体方法,并返回最终构建的对象。
- Product(产品):表示被构建的复杂对象。通常包含多个部分,每个部分由建造者逐步构建而成。
建造者模式的核心思想是将构建复杂对象的过程和最终的表示分离开来,使得同样的构建过程可以创建不同的表示。这种分离使得建造者模式更加灵活,能够支持不同的表示,同时也使得客户端代码与复杂对象的构建过程解耦。
示例代码
// 产品类
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;
}
@Override
public String toString() {
return "Product{" +
"partA='" + partA + '\'' +
", partB='" + partB + '\'' +
", partC='" + partC + '\'' +
'}';
}
}
// 抽象建造者
interface Builder {
void buildPartA();
void buildPartB();
void buildPartC();
Product getResult();
}
// 具体建造者
class ConcreteBuilder implements Builder {
private Product product;
public ConcreteBuilder() {
this.product = new Product();
}
@Override
public void buildPartA() {
product.setPartA("PartA");
}
@Override
public void buildPartB() {
product.setPartB("PartB");
}
@Override
public void buildPartC() {
product.setPartC("PartC");
}
@Override
public Product getResult() {
return product;
}
}
// 指挥者
class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
Product product = director.construct();
System.out.println(product);
}
}
在这个示例中,产品 Product 由三个部分组成:partA、partB 和 partC。Builder 接口定义了构建产品的抽象方法,包括 buildPartA()、buildPartB() 和 buildPartC()。ConcreteBuilder 类是具体的建造者,负责实现构建产品各个部分的具体方法,并最终返回构建好的产品对象。Director 类负责指导建造者按照一定的顺序构建产品。在客户端代码中,通过创建具体的建造者对象,并将其传递给指挥者,可以构建出不同的产品对象。
原型模式
原型模式介绍
原型模式是一种创建型设计模式,其核心思想是通过复制现有对象来创建新对象,而不是通过实例化新对象的方式。在原型模式中,原型对象被克隆来生成新的对象,而不是通过调用构造函数来创建新对象。这种方式能够在某些情况下提高性能,尤其是当对象的创建过程比较昂贵或复杂时。
原型模式的关键角色包括:
- 原型接口(Prototype Interface):定义了克隆方法的接口,通常包含一个名为 clone 的方法,用于复制对象。
- 具体原型(Concrete Prototype):实现了原型接口的具体类,负责实现克隆方法以生成新对象。
示例代码
// 原型接口
interface Prototype {
Prototype clone();
}
// 具体原型类
class ConcretePrototype implements Prototype {
private int id;
public ConcretePrototype(int id) {
this.id = id;
}
// 克隆方法
@Override
public Prototype clone() {
// 创建一个新的对象,并将当前对象的属性复制给新对象
return new ConcretePrototype(this.id);
}
// 获取对象的ID
public int getId() {
return id;
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建原型对象
ConcretePrototype prototype = new ConcretePrototype(1);
// 克隆原型对象
ConcretePrototype clonedPrototype = (ConcretePrototype) prototype.clone();
// 输出原型对象和克隆对象的ID
System.out.println("原型对象的ID:" + prototype.getId());
System.out.println("克隆对象的ID:" + clonedPrototype.getId());
}
}
在这个示例中,ConcretePrototype 类实现了 Prototype 接口,定义了 clone 方法用于复制对象。客户端代码中,首先创建了一个原型对象 prototype,然后通过调用其 clone 方法来克隆一个新的对象 clonedPrototype。最后,输出了原型对象和克隆对象的 ID,验证了克隆成功。
需要注意的是,在实际应用中,克隆方法的实现可能更为复杂,因为需要考虑对象内部状态的克隆以及可能存在的深拷贝与浅拷贝等问题。
结构型
适配器模式
适配器模式介绍
适配器模式是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口。这种模式通常用于解决两个已有接口之间不匹配的问题,使得原本由于接口不兼容而不能在一起工作的类可以一起工作。
适配器模式包括以下几个关键角色:
- 目标接口(Target Interface):客户端期望的接口,适配器类通过实现这个接口来与客户端进行交互。
- 适配器类(Adapter Class):实现了目标接口,并持有一个被适配的对象的引用。适配器类的主要工作是将客户端的请求委派给被适配的对象,并在必要时进行适配。
- 被适配的类(Adaptee Class):需要被适配的类,它定义了客户端不直接使用的接口。适配器类通过持有这个类的实例来实现适配。
适配器模式可以分为对象适配器和类适配器两种实现方式:
- 对象适配器:适配器类持有一个被适配的对象的引用,并通过委派来实现适配。
- 类适配器:适配器类通过多重继承或接口实现同时继承目标接口和被适配类,以实现适配。
示例代码
// 目标接口
interface Target {
void request();
}
// 被适配的类
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee's specificRequest called");
}
}
// 适配器类
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();
}
}
在这个示例中,Adaptee 类是被适配的类,它定义了 specificRequest 方法,但是客户端不直接使用。Adapter 类实现了目标接口 Target,并持有一个 Adaptee 对象的引用,在 request 方法中调用了被适配对象的 specificRequest 方法。客户端代码通过创建适配器对象来调用目标接口的方法,实际上会委派给被适配对象来执行。
桥接模式
桥接模式介绍
桥接模式是一种结构型设计模式,它将抽象部分与其实现部分分离,使它们可以独立地变化。桥接模式通过将抽象和实现分离来解耦,从而可以独立地对它们进行扩展和修改,而不会相互影响。
桥接模式的核心概念是将抽象部分和实现部分分离开来,使它们可以独立变化。在桥接模式中,通常存在两个维度的变化,一个是抽象部分的变化,另一个是实现部分的变化。通过桥接模式,可以让这两个维度的变化彼此独立,从而提高系统的灵活性和可扩展性。
桥接模式包括以下几个关键角色:
- 抽象部分(Abstraction):定义了抽象部分的接口,并维护一个指向实现部分的引用。
- 具体抽象部分(Concrete Abstraction):扩展抽象部分,实现抽象部分接口,并调用实现部分的方法。
- 实现部分(Implementor):定义了实现部分的接口,供具体实现类实现。
- 具体实现部分(Concrete Implementor):实现了实现部分接口的具体类。
桥接模式的优点包括:
- 分离抽象和实现部分,使得它们可以独立地变化和扩展,符合开闭原则。
- 提高了系统的灵活性和可扩展性,可以更容易地应对需求变化。
示例代码
// 实现部分接口
interface Implementor {
void operationImpl();
}
// 具体实现部分A
class ConcreteImplementorA implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorA operationImpl called");
}
}
// 具体实现部分B
class ConcreteImplementorB implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorB operationImpl called");
}
}
// 抽象部分
abstract class Abstraction {
protected Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public abstract void operation();
}
// 具体抽象部分A
class ConcreteAbstractionA extends Abstraction {
public ConcreteAbstractionA(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
System.out.println("ConcreteAbstractionA operation called");
implementor.operationImpl();
}
}
// 具体抽象部分B
class ConcreteAbstractionB extends Abstraction {
public ConcreteAbstractionB(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
System.out.println("ConcreteAbstractionB operation called");
implementor.operationImpl();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Implementor implementorA = new ConcreteImplementorA();
Implementor implementorB = new ConcreteImplementorB();
Abstraction abstractionA = new ConcreteAbstractionA(implementorA);
Abstraction abstractionB = new ConcreteAbstractionB(implementorB);
abstractionA.operation();
abstractionB.operation();
}
}
在这个示例中,Implementor 接口定义了实现部分的接口,具体实现类 ConcreteImplementorA 和 ConcreteImplementorB 实现了该接口。Abstraction 抽象类定义了抽象部分的接口,并维护一个 Implementor 的引用。具体抽象类 ConcreteAbstractionA 和 ConcreteAbstractionB 扩展了抽象类,并实现了抽象部分的接口。客户端代码创建了具体实现部分和具体抽象部分的对象,并调用其方法,实现了抽象和实现的分离
组合模式
组合模式介绍
组合模式是一种结构型设计模式,它允许你将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得客户端对单个对象和组合对象的使用具有一致性,因为它们共享相同的接口。
组合模式涉及以下几个关键角色:
- 组件(Component):定义了组合中对象的共有接口,可以是抽象类或接口,它声明了所有具体组件类和叶子节点类的方法。
- 叶子节点(Leaf):表示组合中的叶子对象,叶子节点没有子节点,它实现了组件接口。
- 复合节点(Composite):表示组合中的复合对象,复合节点有子节点,它实现了组件接口并提供了管理子节点的方法。
组合模式的优点包括:
- 客户端代码可以一致地处理单个对象和组合对象,无需关心对象的具体类型。
- 可以通过组合和递归来表示复杂的层次结构,使得系统更加灵活和易于扩展。
示例代码
// 组件接口
interface Component {
void operation();
}
// 叶子节点
class Leaf implements Component {
@Override
public void operation() {
System.out.println("Leaf operation called");
}
}
// 复合节点
class Composite implements Component {
private List<Component> children = new ArrayList<>();
public void add(Component component) {
children.add(component);
}
public void remove(Component component) {
children.remove(component);
}
@Override
public void operation() {
System.out.println("Composite operation called");
for (Component component : children) {
component.operation();
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Leaf leaf1 = new Leaf();
Leaf leaf2 = new Leaf();
Composite composite1 = new Composite();
composite1.add(leaf1);
composite1.add(leaf2);
Leaf leaf3 = new Leaf();
Composite composite2 = new Composite();
composite2.add(leaf3);
composite2.add(composite1);
composite2.operation();
}
}
在这个示例中,Component 接口定义了组件的共有接口,Leaf 类表示叶子节点,Composite 类表示复合节点。客户端代码创建了叶子节点对象和复合节点对象,并通过组合的方式构建了一个树形结构。调用根节点的 operation 方法时,会递归地调用所有子节点的 operation 方法,实现了整个树形结构的遍历。
装饰器模式
装饰器模式介绍
装饰器模式是一种结构型设计模式,它允许你在运行时向对象动态地添加新功能,同时又不改变其接口。装饰器模式通过创建一个包装对象来包裹原始对象,然后用一系列装饰器对象来包装这个包装对象,从而实现对原始对象功能的增强。
在装饰器模式中,通常有以下几个角色:
- 组件(Component):定义了一个对象接口,可以给这些对象动态地添加职责。
- 具体组件(Concrete Component):实现了组件接口的具体对象,是被装饰的对象。
- 装饰器(Decorator):持有一个指向组件对象的引用,并实现了组件接口,具有与组件接口相同的方法。
- 具体装饰器(Concrete Decorator):扩展装饰器类,负责向组件对象添加新的职责。
装饰器模式的核心思想是通过一系列装饰器类来包装原始对象,每个装饰器类都实现了与原始对象相同的接口,这样客户端可以透明地使用装饰器来增强对象的功能,而不需要知道具体的装饰器类。
装饰器模式的优点包括:
- 可以动态地添加或删除对象的功能,使得功能扩展更加灵活。
- 遵循开闭原则,不需要修改现有代码即可引入新的功能。
- 装饰器类可以相互组合,从而实现复杂的功能组合。
示例代码
// 组件接口
interface Component {
void operation();
}
// 具体组件
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent operation called");
}
}
// 装饰器
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();
addedBehavior();
}
private void addedBehavior() {
System.out.println("ConcreteDecoratorA added behavior");
}
}
// 具体装饰器B
class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehavior();
}
private void addedBehavior() {
System.out.println("ConcreteDecoratorB added behavior");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Component component = new ConcreteComponent();
component = new ConcreteDecoratorA(component);
component = new ConcreteDecoratorB(component);
component.operation();
}
}
在这个示例中,Component 接口定义了组件的共有接口,ConcreteComponent 类是具体的组件。Decorator 类是装饰器类,它持有一个指向组件对象的引用,并实现了组件接口。ConcreteDecoratorA 和 ConcreteDecoratorB 类是具体装饰器类,它们扩展了装饰器类并添加了额外的行为。客户端代码创建了具体组件对象,并通过一系列装饰器类来动态地为组件对象添加新的功能。
外观模式
外观模式介绍
外观模式是一种结构型设计模式,它提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,这个接口使得子系统更容易使用。
外观模式的核心思想是通过创建一个外观类来封装子系统的复杂功能,然后客户端只需要与外观类交互,而不需要直接与子系统的组件打交道。这样可以降低系统的耦合度,提高系统的灵活性和可维护性。
在外观模式中,通常有以下几个角色:
- 外观(Facade):提供了一个简单的接口,对客户端隐藏了子系统的复杂性。外观对象将客户端的请求委派给相应的子系统对象来完成具体的任务。
- 子系统类(Subsystems):实现了子系统的功能。每个子系统都可以由多个类组成,外观模式通过封装这些类的复杂性,提供了一个简单的接口给客户端使用。
外观模式的优点包括:
- 提供了一个简单的接口,隐藏了子系统的复杂性,使得客户端更容易使用。
- 降低了客户端和子系统之间的耦合度,使得子系统的改变不会影响到客户端。
- 符合单一职责原则,外观类负责对客户端的请求进行委派,而不会承担子系统的具体功能实现。
示例代码
// 子系统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() {
subsystemA = new SubsystemA();
subsystemB = new SubsystemB();
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 给客户端使用。客户端只需要与外观类交互,而不需要直接与子系统的组件打交道。
享元模式
享元模式介绍
享元模式是一种结构型设计模式,旨在通过共享对象来最大限度地减少内存使用和提高性能。在享元模式中,对象被设计为可共享的,即多个对象可以共享同一个享元对象,从而减少内存占用。
享元模式的核心思想是将对象的状态分为内部状态(Intrinsic State)和外部状态(Extrinsic State)两部分。内部状态是对象可以共享的部分,而外部状态是对象依赖于的不可共享的部分。通过将内部状态和外部状态分离,使得多个对象可以共享相同的内部状态,从而减少内存使用。
在享元模式中,通常有以下几个角色:
- 享元接口(Flyweight):定义了享元对象的接口,通过这个接口可以获取和设置内部状态。
- 具体享元(Concrete Flyweight):实现了享元接口,并为内部状态提供了存储空间。具体享元对象必须是可共享的。
- 享元工厂(Flyweight Factory):负责创建和管理享元对象,提供了一个用于存储和获取享元对象的池。
- 客户端(Client):使用享元模式的客户端,通过享元工厂获取享元对象,并设置外部状态。
享元模式的优点包括:
- 减少内存使用:通过共享对象,减少了系统中对象的数量,从而降低了内存使用。
- 提高性能:减少了对象的创建和销毁次数,提高了系统的性能。
- 提高系统灵活性:享元模式将对象的状态分为内部状态和外部状态,使得系统更容易扩展和维护。
示例代码
import java.util.HashMap;
import java.util.Map;
// 享元接口
interface Flyweight {
void operation(String extrinsicState);
}
// 具体享元
class ConcreteFlyweight implements Flyweight {
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation(String extrinsicState) {
System.out.println("ConcreteFlyweight: Intrinsic State = " + intrinsicState + ", Extrinsic State = " + extrinsicState);
}
}
// 享元工厂
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);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight1 = factory.getFlyweight("shared");
flyweight1.operation("external state 1");
Flyweight flyweight2 = factory.getFlyweight("shared");
flyweight2.operation("external state 2");
}
}
在这个示例中,Flyweight 接口定义了享元对象的接口,ConcreteFlyweight 类是具体享元类,实现了享元接口,并提供了内部状态的存储空间。FlyweightFactory 类是享元工厂,负责创建和管理享元对象,提供了一个用于存储和获取享元对象的池。客户端通过享元工厂获取享元对象,并设置外部状态。由于 ConcreteFlyweight 是可共享的,所以多次获取相同的享元对象时会返回同一个对象,从而实现了对象的共享。
代理模式
代理模式介绍
代理模式是一种结构型设计模式,其主要目的是通过代理对象控制对其他对象的访问。在代理模式中,客户端通过与代理对象进行交互,间接地访问实际的目标对象,从而可以在访问对象时添加额外的功能或控制访问权限。
代理模式的核心思想是引入一个代理对象来控制对目标对象的访问。代理对象和目标对象实现了相同的接口,使得客户端可以无需知道实际的对象,而是通过代理对象来访问。代理对象可以在调用目标对象之前或之后执行一些额外的操作,例如权限验证、日志记录、缓存等。
代理模式通常包含以下几种角色:
- 抽象主题(Subject):定义了目标对象和代理对象共同实现的接口,客户端通过该接口访问目标对象。
- 真实主题(Real Subject):实际的目标对象,代理对象所代表的真正对象,客户端最终想要访问的对象。
- 代理(Proxy):代理对象,包含了对真实主题的引用,客户端通过代理对象间接访问真实主题,并可以在访问前后添加额外的功能。
代理模式的优点包括:
- 代理对象可以在不改变目标对象的情况下,增加额外的功能:例如权限验证、日志记录、缓存等。
- 可以控制客户端对目标对象的访问:代理对象可以根据需要控制对目标对象的访问权限。
- 可以减少系统的耦合度:客户端只需要与代理对象进行交互,无需直接与目标对象交互,从而减少了系统的耦合度。
示例代码
// 抽象主题
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() {
this.realSubject = new RealSubject();
}
@Override
public void request() {
// 在访问真实主题之前可以执行一些额外的操作
System.out.println("Proxy: Logging before request.");
// 调用真实主题的方法
realSubject.request();
// 在访问真实主题之后可以执行一些额外的操作
System.out.println("Proxy: Logging after request.");
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Subject proxy = new Proxy();
proxy.request();
}
}
在这个示例中,Subject 是抽象主题接口,定义了目标对象和代理对象共同实现的接口。RealSubject 是真实主题类,实现了抽象主题接口,并提供了实际的业务逻辑。Proxy 是代理类,实现了抽象主题接口,并包含了对真实主题的引用。在 Proxy 类的 request() 方法中,可以在访问真实主题之前或之后执行额外的操作,例如日志记录。客户端通过代理对象来访问真实主题,从而可以在访问前后添加额外的功能。
行为型
责任链模式
责任链模式介绍
责任链模式是一种行为设计模式,它允许将请求沿着处理链传递,直到有一个对象能够处理它为止。这种模式将请求的发送者和接收者解耦,使得多个对象都有机会处理请求。
在责任链模式中,通常会创建一个处理器链。每个处理器都有一个对下一个处理器的引用,当一个处理器无法处理请求时,它会将请求传递给下一个处理器。这样,请求会沿着链条依次被处理,直到有一个处理器能够处理它为止。
责任链模式通常包含以下几种角色:
- 抽象处理器(Handler):定义了处理请求的接口,通常包含一个对下一个处理器的引用。该接口可以定义一个处理请求的方法,以及设置下一个处理器的方法。
- 具体处理器(Concrete Handler):实现了抽象处理器接口,具体处理请求的类。每个具体处理器都负责处理一部分请求,如果无法处理则将请求传递给下一个处理器。
- 客户端(Client):创建处理器链,并向链中的第一个处理器发送请求。
责任链模式的优点包括:
- 降低了请求发送者和接收者之间的耦合度:发送者无需知道接收者的具体信息,只需将请求发送给第一个处理器即可。
- 动态添加或修改处理器:可以根据需要动态地添加、删除或修改处理器,灵活性较高。
- 对单一职责原则的支持:每个具体处理器只负责处理自己能够处理的请求,符合单一职责原则。
示例代码
// 抽象处理器
abstract class Handler {
protected Handler nextHandler;
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void handleRequest(int request);
}
// 具体处理器A
class ConcreteHandlerA extends Handler {
@Override
public void handleRequest(int request) {
if (request <= 10) {
System.out.println("ConcreteHandlerA: Handling request " + request);
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
// 具体处理器B
class ConcreteHandlerB extends Handler {
@Override
public void handleRequest(int request) {
if (request <= 20) {
System.out.println("ConcreteHandlerB: Handling request " + request);
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
// 构建处理器链
handlerA.setNextHandler(handlerB);
// 发送请求
handlerA.handleRequest(5);
handlerA.handleRequest(15);
handlerA.handleRequest(25);
}
}
在这个示例中,Handler 是抽象处理器类,定义了处理请求的接口,并包含一个对下一个处理器的引用。ConcreteHandlerA 和 ConcreteHandlerB 是具体处理器类,实现了抽象处理器接口,并负责处理请求。在客户端中,创建了处理器链,并向链中的第一个处理器发送请求。如果一个处理器无法处理请求,则将请求传递给下一个处理器,直到有一个处理器能够处理为止。
命令模式
命令模式介绍
命令模式是一种行为设计模式,它允许将请求封装为一个对象,从而使得可以参数化客户端对象,将请求排队或记录请求日志,并且支持可撤销的操作。
在命令模式中,命令被封装为一个对象,包含了执行该命令所需的所有信息,包括命令的接收者、执行方法等。这样,客户端只需创建并传递命令对象,而无需了解具体的命令执行逻辑。
命令模式通常包含以下几种角色:
- 命令(Command):声明执行操作的接口,通常包含一个执行方法(execute)。它可以是一个抽象类或接口。
- 具体命令(Concrete Command):实现了命令接口,具体定义了命令的执行逻辑,并持有一个对命令接收者的引用。
- 命令接收者(Receiver):命令的真正执行者,包含了具体的业务逻辑。
- 调用者(Invoker):负责调用命令对象执行请求,它不了解命令的具体细节,只知道调用命令对象的 execute 方法。
- 客户端(Client):创建命令对象,并设置命令的接收者。
命令模式的优点包括:
- 解耦调用操作的对象和实现该操作的对象:调用者无需了解命令的具体执行逻辑和接收者,降低了调用者与接收者之间的耦合度。
- 容易添加新的命令:可以通过创建新的具体命令类来轻松扩展命令的种类。
- 支持撤销和恢复操作:通过保存命令的历史记录,可以支持撤销和恢复操作。
示例代码
// 命令接口
interface Command {
void execute();
}
// 具体命令
class ConcreteCommand implements Command {
private Receiver receiver;
ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.action();
}
}
// 命令接收者
class Receiver {
void action() {
System.out.println("Receiver: executing action");
}
}
// 调用者
class Invoker {
private Command command;
void setCommand(Command command) {
this.command = command;
}
void executeCommand() {
command.execute();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.executeCommand();
}
}
在这个示例中,Command 是命令接口,声明了一个 execute 方法。ConcreteCommand 是具体命令类,实现了命令接口,并持有一个对命令接收者 Receiver 的引用。Receiver 是命令的真正执行者,包含了具体的业务逻辑。Invoker 是调用者,负责调用命令对象执行请求。在客户端中,创建了命令对象并设置了命令的接收者,然后将命令对象传递给调用者执行。
解释器模式
解释器模式介绍
解释器模式是一种行为设计模式,它用于定义一种语言的文法,并且建立一个解释器来解释该语言中的句子。
通常,解释器模式适用于处理某种特定的语言或语法,例如正则表达式、查询语言、配置文件解析等。它将一个语言中的句子表示为一个抽象语法树,并提供一种方式来解释这棵树,从而执行句子所代表的操作。
解释器模式包含以下几种角色:
- 抽象表达式(Abstract Expression):声明一个抽象的解释操作,通常是一个抽象类或接口,其中包含一个 interpret 方法用于解释语法规则。
- 终结符表达式(Terminal Expression):实现了抽象表达式中的解释方法,用于表示语法规则中的终结符。
- 非终结符表达式(Nonterminal Expression):也是抽象表达式的子类,用于表示语法规则中的非终结符,并且通常包含了其他子表达式。
- 环境(Context):包含解释器解释的全局信息,可能包括解释器所需的数据。
- 客户端(Client):创建并配置好抽象语法树,然后将其传递给解释器进行解释。
解释器模式的优点包括:
- 易于扩展新的语法规则:通过添加新的终结符表达式和非终结符表达式,可以轻松地扩展解释器以支持新的语法规则。
- 简化语法分析过程:将语言的语法规则表示为抽象语法树,使得语法分析过程更加直观和简化。
示例代码
// 抽象表达式
interface Expression {
boolean interpret(String context);
}
// 终结符表达式
class TerminalExpression implements Expression {
private String data;
TerminalExpression(String data) {
this.data = data;
}
@Override
public boolean interpret(String context) {
return context.contains(data);
}
}
// 或表达式
class OrExpression implements Expression {
private Expression expr1;
private Expression expr2;
OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
// 与表达式
class AndExpression implements Expression {
private Expression expr1;
private Expression expr2;
AndExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) && expr2.interpret(context);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Expression person1 = new TerminalExpression("John");
Expression person2 = new TerminalExpression("Mary");
Expression isSingle = new OrExpression(person1, person2);
Expression married = new TerminalExpression("married");
Expression isMarried = new AndExpression(person1, married);
System.out.println("Is John single? " + isSingle.interpret("John"));
System.out.println("Is Mary single? " + isSingle.interpret("Mary"));
System.out.println("Is John married? " + isMarried.interpret("John married"));
}
}
在这个示例中,我们模拟了一个简单的语言,其中包含了终结符表达式(TerminalExpression)、或表达式(OrExpression)和与表达式(AndExpression)。客户端创建了一些表达式,并根据特定的上下文调用解释器来解释这些表达式。
迭代器模式
迭代器模式介绍
迭代器模式是一种行为设计模式,它用于提供一种顺序访问集合对象元素的方法,而不暴露集合的内部表示。该模式可以使客户端代码独立于集合对象的结构。
迭代器模式通常包括以下几个角色:
- 迭代器(Iterator):定义了访问和遍历集合元素的接口,通常包括 next()、hasNext() 等方法。
- 具体迭代器(Concrete Iterator):实现了迭代器接口,负责实现迭代器接口定义的方法,并且维护了迭代的状态。
- 可迭代对象(Iterable):定义了一个返回迭代器的方法,通常命名为 iterator()。
- 具体可迭代对象(Concrete Iterable):实现了可迭代对象接口,负责创建对应的具体迭代器。
- 客户端(Client):使用迭代器来遍历集合对象中的元素,而不需要了解集合对象的内部结构。
迭代器模式的优点包括:
- 分离集合与遍历算法:通过迭代器将集合与遍历算法分离,使得集合可以独立地变化,而不影响遍历算法。
- 简化客户端代码:客户端不需要了解集合的内部结构,只需通过迭代器进行遍历操作。
- 支持多种遍历方式:可以根据需要创建不同的迭代器,支持不同的遍历方式。
示例代码
// 迭代器接口
interface Iterator<T> {
boolean hasNext();
T next();
}
// 可迭代对象接口
interface Iterable<T> {
Iterator<T> iterator();
}
// 具体迭代器
class ConcreteIterator<T> implements Iterator<T> {
private T[] elements;
private int position = 0;
ConcreteIterator(T[] elements) {
this.elements = elements;
}
@Override
public boolean hasNext() {
return position < elements.length;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return elements[position++];
}
}
// 具体可迭代对象
class ConcreteIterable<T> implements Iterable<T> {
private T[] elements;
ConcreteIterable(T[] elements) {
this.elements = elements;
}
@Override
public Iterator<T> iterator() {
return new ConcreteIterator<>(elements);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Integer[] numbers = {1, 2, 3, 4, 5};
Iterable<Integer> iterable = new ConcreteIterable<>(numbers);
Iterator<Integer> iterator = iterable.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
在这个示例中,我们定义了一个简单的整数集合,并实现了迭代器接口和可迭代对象接口。客户端代码通过迭代器遍历集合中的元素,而不需要了解集合的内部结构。
中介者模式
中介者模式介绍
中介者模式是一种行为设计模式,用于减少对象之间的直接通信,而是通过一个中介者对象来协调对象之间的交互。这种模式有助于减少类之间的耦合度,使得类可以独立地改变它们的行为。
中介者模式通常包括以下几个角色:
- 中介者(Mediator):定义了一个接口用于与各个同事对象通信,并且负责协调各个同事对象之间的交互关系。
- 具体中介者(Concrete Mediator):实现了中介者接口,负责实际协调各个同事对象之间的交互关系。
- 同事对象(Colleague):每个同事对象都知道中介者对象,并且与中介者对象通信。当一个同事对象发生改变时,它会通知中介者对象,中介者对象负责通知其他相关的同事对象。
- 具体同事对象(Concrete Colleague):实现了同事对象接口,与其他同事对象进行通信,并且在需要时与中介者对象进行交互。
中介者模式的优点包括:
- 降低耦合度:通过中介者对象来协调对象之间的交互,减少了对象之间的直接通信,降低了耦合度。
- 集中控制:中介者对象集中了对象之间的交互逻辑,使得交互逻辑更加清晰和易于管理。
- 复用性:可以将中介者对象作为一个独立的组件,可以被多个对象重复使用。
示例代码
// 中介者接口
interface Mediator {
void sendMessage(String message, Colleague colleague);
}
// 具体中介者
class ConcreteMediator implements Mediator {
private Colleague colleague1;
private Colleague colleague2;
void setColleague1(Colleague colleague) {
this.colleague1 = colleague;
}
void setColleague2(Colleague colleague) {
this.colleague2 = colleague;
}
@Override
public void sendMessage(String message, Colleague colleague) {
if (colleague == colleague1) {
colleague2.receiveMessage(message);
} else if (colleague == colleague2) {
colleague1.receiveMessage(message);
}
}
}
// 同事对象接口
interface Colleague {
void sendMessage(String message);
void receiveMessage(String message);
}
// 具体同事对象
class ConcreteColleague1 implements Colleague {
private Mediator mediator;
ConcreteColleague1(Mediator mediator) {
this.mediator = mediator;
}
@Override
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println("ConcreteColleague1 received message: " + message);
}
}
// 具体同事对象
class ConcreteColleague2 implements Colleague {
private Mediator mediator;
ConcreteColleague2(Mediator mediator) {
this.mediator = mediator;
}
@Override
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println("ConcreteColleague2 received message: " + message);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);
mediator.setColleague1(colleague1);
mediator.setColleague2(colleague2);
colleague1.sendMessage("Hello from colleague1!");
colleague2.sendMessage("Hi from colleague2!");
}
}
在这个示例中,我们定义了一个中介者接口和两个具体同事对象,分别是ConcreteColleague1和ConcreteColleague2。中介者对象ConcreteMediator负责协调同事对象之间的交互。当一个同事对象发送消息时,它会通过中介者对象来通知其他相关的同事对象。
备忘录模式
备忘录模式介绍
备忘录模式是一种行为设计模式,用于捕获和存储一个对象的内部状态,以便在稍后的时间点可以将对象恢复到该状态。这种模式有助于实现对象状态的备份和恢复,同时又保持了封装性。
备忘录模式通常包括以下几个角色:
- 发起人(Originator):需要被保存状态的对象。它创建备忘录对象以记录当前状态,并且可以使用备忘录对象来恢复自身状态。
- 备忘录(Memento):用于存储发起人对象的内部状态。备忘录对象可以包含发起人对象的一些关键信息,但是不会暴露给其他对象。
- 负责人(Caretaker):负责保存备忘录对象,并且在需要时将备忘录对象还原到发起人对象之前的状态。负责人通常不会直接访问备忘录对象的内部状态,而是通过备忘录对象提供的接口来进行操作。
备忘录模式的优点包括:
- 封装性:备忘录模式将对象的状态保存在备忘录对象中,从而实现了状态的封装,使得发起人对象的状态对其他对象是不可见的。
- 备份和恢复:备忘录模式允许对象保存和恢复状态,从而可以在需要时将对象恢复到之前的状态,提供了一种简单的备份和恢复机制。
- 支持撤销操作:备忘录模式可以支持撤销操作,因为它可以保存对象的历史状态,使得对象可以在需要时回退到之前的状态。
示例代码
// 备忘录类
class Memento {
private final String state;
Memento(String state) {
this.state = state;
}
String getState() {
return state;
}
}
// 发起人类
class Originator {
private String state;
void setState(String state) {
this.state = state;
}
String getState() {
return state;
}
Memento saveStateToMemento() {
return new Memento(state);
}
void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
// 负责人类
class Caretaker {
private Memento memento;
void saveMemento(Memento memento) {
this.memento = memento;
}
Memento getMemento() {
return memento;
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("State #1");
originator.setState("State #2");
caretaker.saveMemento(originator.saveStateToMemento());
originator.setState("State #3");
caretaker.saveMemento(originator.saveStateToMemento());
originator.setState("State #4");
System.out.println("Current State: " + originator.getState());
originator.getStateFromMemento(caretaker.getMemento());
System.out.println("Restored State: " + originator.getState());
}
}
在这个示例中,Originator类代表了发起人对象,它拥有一个状态和保存状态的方法。Memento类代表了备忘录对象,用于存储发起人对象的状态。Caretaker类代表了负责人对象,它负责保存和获取备忘录对象。客户端代码演示了如何使用备忘录模式来保存和恢复对象的状态。
观察者模式
观察者模式介绍
观察者模式是一种行为设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新。
在观察者模式中,有以下几个核心角色:
- 主题(Subject):也称为被观察者或可观察对象,它维护一系列观察者对象,并提供添加、删除和通知观察者的方法。当主题的状态发生变化时,它会通知所有注册的观察者对象。
- 观察者(Observer):观察主题的对象,它定义了一个更新的接口,用于接收主题状态的变化通知。在收到通知后,观察者可以采取相应的行动。
观察者模式的优点包括:
- 松耦合:观察者模式将主题和观察者解耦,使得它们可以独立变化,而不会相互影响。主题对象知道观察者的存在,但并不需要了解观察者的具体实现。
- 可扩展性:可以方便地添加新的观察者,主题对象并不需要修改就能支持新的观察者。
- 通知机制:观察者模式提供了一种通知机制,使得当主题状态发生变化时,所有相关的观察者都能及时收到通知并作出相应的反应。
示例代码
import java.util.ArrayList;
import java.util.List;
// 主题接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 具体主题
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyObservers();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
}
// 观察者接口
interface Observer {
void update(int state);
}
// 具体观察者
class ConcreteObserver implements Observer {
@Override
public void update(int state) {
System.out.println("Received update with state: " + state);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver();
ConcreteObserver observer2 = new ConcreteObserver();
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.setState(5);
subject.removeObserver(observer1);
subject.setState(10);
}
}
在这个示例中,ConcreteSubject是一个具体的主题类,实现了Subject接口,并维护了一个观察者列表。当主题的状态发生变化时,它会遍历观察者列表并调用它们的update方法进行通知。ConcreteObserver是具体的观察者类,实现了Observer接口,并在收到通知后打印更新信息。客户端代码演示了如何创建主题对象、注册观察者、更新主题状态以及移除观察者。
状态模式
状态模式介绍
状态模式是一种行为设计模式,它允许一个对象在其内部状态改变时改变它的行为。这种模式把对象的行为封装在不同的状态对象中,通过切换不同的状态对象来改变对象的行为。
在状态模式中,有以下几个核心角色:
- 环境(Context):定义了客户感兴趣的接口,维护一个当前状态对象,并且将与状态相关的操作委托给当前状态对象。
- 抽象状态(State):定义一个接口,用于封装环境对象中的特定状态相关的行为。
- 具体状态(Concrete State):实现抽象状态接口,具体定义该状态下的行为。
状态模式的优点包括:
- 封装性:状态模式将状态封装在独立的状态类中,使得每个状态类的实现都相对独立,易于理解和维护。
- 扩展性:通过增加新的状态类,可以方便地扩展状态模式,而不需要修改现有代码。
- 减少条件分支:状态模式将状态转换的逻辑封装在状态类中,避免了大量的条件分支语句,使代码更清晰、更易于理解。
示例代码
// 抽象状态接口
interface State {
void handle(Context context);
}
// 具体状态类A
class ConcreteStateA implements State {
@Override
public void handle(Context context) {
System.out.println("Switching to state A.");
context.setState(new ConcreteStateB());
}
}
// 具体状态类B
class ConcreteStateB implements State {
@Override
public void handle(Context context) {
System.out.println("Switching to state B.");
context.setState(new ConcreteStateA());
}
}
// 环境类
class Context {
private State state;
public Context() {
this.state = new ConcreteStateA(); // 初始状态为A
}
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle(this);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.request(); // 切换到状态A
context.request(); // 切换到状态B
context.request(); // 切换到状态A
}
}
在这个示例中,State是抽象状态接口,定义了一个handle方法来处理状态。ConcreteStateA和ConcreteStateB是具体的状态类,分别实现了State接口,并定义了各自状态下的行为。Context是环境类,维护一个当前状态对象,并提供了一个request方法用于请求状态转换。客户端代码演示了如何使用状态模式来切换对象的状态。
策略模式
策略模式介绍
策略模式是一种行为设计模式,它定义了一系列算法,将每个算法封装成一个类,并使它们可以互相替换。策略模式可以使算法独立于使用它的客户而变化,即客户可以根据需要在运行时选择合适的算法。
在策略模式中,有以下几个核心角色:
- 环境(Context):维护一个对策略对象的引用,负责将客户的请求委派给具体的策略对象。
- 抽象策略(Strategy):定义了一个公共接口,用于所有具体策略类的统一行为。
- 具体策略(Concrete Strategy):实现了抽象策略接口,包含了具体的算法。
策略模式的优点包括:
- 灵活性:客户可以在运行时选择不同的策略,从而实现不同的行为。
- 可扩展性:通过添加新的策略类,可以方便地扩展策略模式,而不需要修改现有代码。
- 简化了单元测试:每个具体策略都有自己的类,可以独立测试,有助于保持代码清晰和可维护性。
示例代码
// 抽象策略接口
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.");
}
}
// 环境类
class Context {
private Strategy strategy;
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy() {
if (strategy != null) {
strategy.execute();
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Context context = new Context();
// 设置策略A
context.setStrategy(new ConcreteStrategyA());
context.executeStrategy();
// 设置策略B
context.setStrategy(new ConcreteStrategyB());
context.executeStrategy();
}
}
在这个示例中,Strategy是抽象策略接口,定义了一个execute方法用于执行具体的策略。ConcreteStrategyA和ConcreteStrategyB是具体的策略类,分别实现了Strategy接口,并定义了各自的具体算法。Context是环境类,负责维护一个对当前策略对象的引用,并提供了一个executeStrategy方法用于执行当前策略。客户端代码演示了如何使用策略模式来切换不同的策略。
模板方法模式
模板方法模式介绍
模板方法模式是一种行为设计模式,它定义了一个算法的框架,并允许子类重写其中的某些步骤,而不改变该算法的结构。这种模式通过将公共步骤提取到一个抽象基类中来实现代码复用,而将具体步骤延迟到子类中实现。
在模板方法模式中,有以下几个核心角色:
- 抽象类(Abstract Class):定义了一个模板方法,其中包含了算法的框架和一系列抽象方法,这些抽象方法由子类去实现。
- 具体子类(Concrete Subclass):实现了抽象类中定义的抽象方法,完成算法中具体的步骤。
模板方法模式的优点包括:
- 代码复用:将公共的代码逻辑提取到抽象类中,避免了重复编写相似的代码。
- 灵活性:允许子类根据需要重写某些步骤,从而实现定制化的算法。
- 易于扩展:通过添加新的具体子类,可以方便地扩展模板方法模式。
示例代码
// 抽象类
abstract class AbstractClass {
// 模板方法
public final void templateMethod() {
// 调用基本方法
operation1();
operation2();
operation3();
}
// 抽象方法,由子类实现
protected abstract void operation1();
// 具体方法
protected void operation2() {
System.out.println("AbstractClass: Doing operation 2");
}
// 钩子方法,子类可以选择重写
protected void operation3() {
System.out.println("AbstractClass: Doing operation 3");
}
}
// 具体子类A
class ConcreteClassA extends AbstractClass {
@Override
protected void operation1() {
System.out.println("ConcreteClassA: Doing operation 1");
}
@Override
protected void operation3() {
System.out.println("ConcreteClassA: Doing operation 3");
}
}
// 具体子类B
class ConcreteClassB extends AbstractClass {
@Override
protected void operation1() {
System.out.println("ConcreteClassB: Doing operation 1");
}
}
// 客户端
public class Client {
public static void main(String[] args) {
AbstractClass obj1 = new ConcreteClassA();
AbstractClass obj2 = new ConcreteClassB();
// 调用模板方法
obj1.templateMethod();
System.out.println("--------");
obj2.templateMethod();
}
}
在这个示例中,AbstractClass是抽象类,定义了一个模板方法templateMethod(),其中包含了算法的框架和一系列基本方法。ConcreteClassA和ConcreteClassB是具体子类,分别实现了抽象类中的抽象方法operation1(),并选择性地重写了钩子方法operation3()。客户端代码演示了如何使用模板方法模式来调用不同子类的模板方法。
访问者模式
访问者模式介绍
访问者模式是一种行为设计模式,它允许你在不修改现有代码的情况下,向对象结构中添加新的操作。这种模式通过将操作从元素的类中提取出来,并将其封装在访问者类中,实现了元素与操作之间的解耦。
在访问者模式中,有以下几个核心角色:
- 访问者(Visitor):定义了一个访问者接口,该接口中包含了多个访问者方法,用于访问不同类型的元素。
- 具体访问者(Concrete Visitor):实现了访问者接口中定义的访问者方法,完成具体的操作。
- 元素(Element):定义了一个元素接口,其中包含了一个接受方法,用于接受访问者的访问。
- 具体元素(Concrete Element):实现了元素接口中定义的接受方法,用于将自身传递给访问者进行访问。
- 对象结构(Object Structure):包含了一个元素的集合,并提供了一个接受方法,用于接受访问者的访问。
访问者模式的优点包括:
- 可扩展性:通过添加新的访问者和元素,可以方便地扩展访问者模式,而不需要修改现有代码。
- 易于维护:访问者模式将操作从元素的类中提取出来,使得操作与元素的结构分离,易于维护和修改。
- 适用于复杂结构:访问者模式适用于对象结构中的元素类型固定,但操作类型多变的情况。
示例代码
// 访问者接口
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 is visiting ConcreteElementA");
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("ConcreteVisitorA is visiting ConcreteElementB");
}
}
// 具体访问者B
class ConcreteVisitorB implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("ConcreteVisitorB is visiting ConcreteElementA");
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("ConcreteVisitorB is visiting 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 Element[] elements;
public ObjectStructure() {
elements = new Element[]{new ConcreteElementA(), new ConcreteElementB()};
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Visitor visitorA = new ConcreteVisitorA();
Visitor visitorB = new ConcreteVisitorB();
ObjectStructure objectStructure = new ObjectStructure();
// 使用具体访问者A访问对象结构
objectStructure.accept(visitorA);
// 使用具体访问者B访问对象结构
objectStructure.accept(visitorB);
}
}
在这个示例中,Visitor是访问者接口,定义了访问具体元素的方法。ConcreteVisitorA和ConcreteVisitorB是具体访问者类,分别实现了访问者接口中定义的访问方法。Element是元素接口,定义了接受访问者的方法。ConcreteElementA和ConcreteElementB是具体元素类,实现了元素接口中的接受方法,用于将自身传递给访问者进行访问。ObjectStructure是对象结构类,包含了一个元素的集合,并提供了一个接受访问者的方法。客户端代码演示了如何使用访问者模式来访问对象结构中的元素。