23种设计模式详解,举例说明
- 一、创建型模式
- 1.1、抽象工厂模式(Abstract Factory)
- 1.1.1、简介
- 1.1.2、意图与应用场景
- 1.1.3、结构
- 1.1.4、优缺点
- 1.1.4、示例代码(简化版)
- 1.2、建造者模式(Builder)
- 1.2.1、简介
- 1.2.2、意图与应用场景
- 1.2.3、结构
- 1.2.4、优缺点
- 1.2.5、示例代码(简化版)
- 1.3、原型模式(Prototype)
- 1.3.1、简介
- 1.3.2、结构
- 1.3.3、工作原理和应用场景
- 1.3.4、优缺点
- 1.4.5、示例代码
- 1.4、工厂方法模式(Factory Method)
- 1.4.1、简介
- 1.4.2、意图与应用场景
- 1.4.3、结构
- 1.4.4、工作流程
- 1.4.5、示例代码(简化版)
- 1.5、单例模式(Singleton)
- 1.5.1、简介
- 1.5.2、意图与应用场景
- 1.5.3、结构
- 1.5.4、工作流程
- 1.5.5、优缺点
- 1.5.6、示例代码(简化版)
- 二、结构型模式
- 2.1、代理模式(Proxy)
- 2.2、享元模式(Flyweight)
- 2.3、外观模式(Facade)
- 2.4、适配器模式(Adapter)
- 2.5、组合模式(Composite)
- 2.6、桥接模式(Bridge)
- 2.7、装饰器模式(Decorator)
- 三、行为型模式
- 3.1、访问者模式(Visitor)
- 3.2、观察者模式(Observer)
- 3.3、备忘录模式(Memento)
- 3.4、状态模式(State)
- 3.5、模板方法模式(Template Method)
- 3.6、责任链模式(Chain of Responsibility)
- 3.7、命令模式(Command)
- 3.8、中介者模式(Mediator)
- 3.9、解释器模式(Interpreter)
- 3.10、迭代器模式(Iterator)
- 3.11、策略模式(Strategy)
一、创建型模式
这类模式提供了对象创建机制,增加了程序的灵活性和复用性。记忆方法:想见员工丹。
1.1、抽象工厂模式(Abstract Factory)
1.1.1、简介
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式是工厂方法模式的一种扩展,它用于处理多维度的大规模产品族,而不是单一产品的创建问题。
抽象工厂模式可以想象成一个大型工厂,这个大工厂里有好几个小工厂,每个小工厂负责生产一系列相关的产品。这种设计模式的核心就是帮助我们在不直接指定具体类的情况下创建一系列相关或依赖对象。
.
来个更生活化的比喻:假设你现在要装修一个新房子,你需要一套家具,包括沙发、咖啡桌和电视柜。你当然希望这些家具风格相配,要么是现代简约风,要么是经典欧式风,而不是混搭风。
.
如果用抽象工厂模式来解决这个问题,那么“家具工厂”就是一个“抽象工厂”,它能够提供一系列相关的产品:沙发、咖啡桌、电视柜。而具体生产这些家具的,则是它下面的“具体工厂”,比如“现代简约风家具工厂”和“经典欧式风家具工厂”。你只需要告诉它,我需要一套“现代简约风”的家具,剩下的事情就交给它去做,它会给你一套风格统一的家具。
1.1.2、意图与应用场景
抽象工厂模式主要用于以下情景:
- 当需要创建的对象是一系列相互关联或相互依赖的产品族时。
- 当系统中的产品有多于一个的产品系列,而系统只消费其中某一系列的产品时。
- 当系统需要提供一个产品类库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
1.1.3、结构
抽象工厂模式通常包含以下几个角色:
- 抽象工厂(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
- 具体工厂(Concrete Factory):实现抽象工厂的操作,创建具体的产品对象。
- 抽象产品(Abstract Product):为一类产品对象声明一个接口。
- 具体产品(Concrete Product):定义一个将被相应的具体工厂创建的产品对象,实现抽象产品接口。
- 客户端(Client):仅使用由抽象工厂和抽象产品定义的接口。
1.1.4、优缺点
-
优点
- 隔离具体类的生成:客户端不需要知道它所创建的对象的类。客户端只需要知道具体工厂的接口即可。
- 易于交换产品系列:因为具体工厂类在一个应用中只需要在初始化的时候出现一次,这使得改变一个应用的具体工厂变得非常容易。
- 促进产品系列的一致性:在一个系列的产品对象被设计成一起工作时,它能保证客户端始终只使用同一个产品系列中的对象。
-
缺点
- 难以支持新种类的产品:扩展抽象工厂以包含新产品的功能是困难的。这是因为抽象工厂接口确定了可以被创建的产品集合,支持新种类的产品需要扩展工厂接口,这将涉及到抽象工厂类及其所有子类的改变。
1.1.4、示例代码(简化版)
以下是一个简化的抽象工厂模式示例,展示了如何创建两种不同的UI组件族:Light 和 Dark。
// 抽象产品:按钮
interface Button {
void paint();
}
// 具体产品:浅色按钮
class LightButton implements Button {
public void paint() {
System.out.println("Rendering light button");
}
}
// 具体产品:深色按钮
class DarkButton implements Button {
public void paint() {
System.out.println("Rendering dark button");
}
}
// 抽象工厂
interface GUIFactory {
Button createButton();
}
// 具体工厂:创建浅色UI组件
class LightGUIFactory implements GUIFactory {
public Button createButton() {
return new LightButton();
}
}
// 具体工厂:创建深色UI组件
class DarkGUIFactory implements GUIFactory {
public Button createButton() {
return new DarkButton();
}
}
客户端代码
public class Client {
private Button button;
public Client(GUIFactory factory) {
button = factory.createButton();
}
public void paint() {
button.paint();
}
public static void main(String[] args) {
// 使用浅色主题
GUIFactory lightFactory = new LightGUIFactory();
Client client1 = new Client(lightFactory);
client1.paint();
// 使用深色主题
GUIFactory darkFactory = new DarkGUIFactory();
Client client2 = new Client(darkFactory);
client2.paint();
}
}
在这个例子中,GUIFactory
是一个抽象工厂,它有两个具体的实现:LightGUIFactory
和 DarkGUIFactory
,分别用于创建浅色和深色主题的按钮。客户端代码不直接实例化产品对象,而是通过工厂接口,这使得添加新的产品族或更改产品族变得容易,且客户端代码不需要改变。
1.2、建造者模式(Builder)
允许创建复杂对象的步骤被分解,使用相同的创建过程可以创建不同的表示。
1.2.1、简介
建造者模式(Builder Pattern)是一种创建型设计模式,旨在解决对象构建过程复杂、需要多步骤和多个部件组装的问题。它允许你分步骤创建复杂对象,将对象构建过程与其表示分离,使得相同的构建过程可以创建不同的表示。
建造者模式就像是搭积木一样,把一个复杂的东西分成很多小块,然后一块一块地慢慢搭建起来。比如,你要建一栋房子,房子里有很多部分,比如墙壁、窗户、门等等,每个部分都有自己的构建步骤。建造者模式就像是有一群专门负责建造这些部分的工人,每个工人负责一部分,最后把所有部分组合起来就得到了完整的房子。
.
在这个过程中,你不需要亲自去指挥每个工人怎么做,而是交给一个指挥者来管理,指挥者知道每个部分应该由哪个工人来负责,然后按照规定的顺序组织工人们一起完成建造。这样一来,不仅分工明确、井然有序,而且如果需要改变建造过程,也很容易调整,只需修改指挥者的指导方针,而不用改动每个工人的具体工作步骤。
1.2.2、意图与应用场景
建造者模式主要用于以下情景:
- 当需要创建的对象包含多个部件,并且需要按照特定顺序或逻辑来构建对象时。
- 当对象的构建过程复杂且需要隐藏构建细节时。
- 当需要创建不同表示的对象,但构建过程相同或相似。
1.2.3、结构
建造者模式包含以下几个核心角色:
- 产品(Product):要构建的复杂对象,通常包含多个部件。
- 抽象建造者(Builder):定义了构建产品的抽象方法,通常包括构建各个部件的方法以及返回最终产品的方法。
- 具体建造者(Concrete Builder):实现了抽象建造者定义的方法,负责实际构建产品的过程。
- 指挥者(Director):负责使用建造者构建产品的具体过程,也可以通过指挥者来指定构建顺序或逻辑。
1.2.4、优缺点
- 优点
-
分步构建:允许你按照特定顺序或逻辑构建对象的各个部件,使得构建过程更加灵活和可控。
-
隐藏细节:将对象的构建过程与其表示分离,客户端不需要知道具体的构建细节。 复用性:可以使用相同的构建过程创建不同的表示,提高代码复用性。
-
易于扩展:添加新的具体建造者或修改现有建造者对于系统的扩展和维护都比较容易。
-
1.2.5、示例代码(简化版)
以下是一个简化的建造者模式示例,假设我们要构建一份电脑产品,电脑包含 CPU、内存、硬盘等部件。
// 产品:电脑
class Computer {
private String cpu;
private String memory;
private String hardDisk;
public void setCpu(String cpu) {
this.cpu = cpu;
}
public void setMemory(String memory) {
this.memory = memory;
}
public void setHardDisk(String hardDisk) {
this.hardDisk = hardDisk;
}
public String getInfo() {
return "CPU: " + cpu + ", Memory: " + memory + ", Hard Disk: " + hardDisk;
}
}
// 抽象建造者
interface ComputerBuilder {
void buildCPU();
void buildMemory();
void buildHardDisk();
Computer getComputer();
}
// 具体建造者:高配版
class HighEndComputerBuilder implements ComputerBuilder {
private Computer computer = new Computer();
public void buildCPU() {
computer.setCpu("Intel i9");
}
public void buildMemory() {
computer.setMemory("32GB DDR4");
}
public void buildHardDisk() {
computer.setHardDisk("1TB SSD");
}
public Computer getComputer() {
return computer;
}
}
// 指挥者
class Director {
private ComputerBuilder builder;
public Director(ComputerBuilder builder) {
this.builder = builder;
}
public void constructComputer() {
builder.buildCPU();
builder.buildMemory();
builder.buildHardDisk();
}
}
客户端代码
public class Client {
public static void main(String[] args) {
ComputerBuilder builder = new HighEndComputerBuilder();
Director director = new Director(builder);
director.constructComputer();
Computer computer = builder.getComputer();
System.out.println(computer.getInfo());
}
}
在这个例子中,Computer
是产品,具有 CPU、内存、硬盘等属性。ComputerBuilder
是抽象建造者接口,定义了构建电脑的抽象方法。HighEndComputerBuilder
是具体建造者,实现了具体的构建过程。Director
是指挥者,负责使用具体建造者构建产品。
通过建造者模式,我们可以按照特定的顺序或逻辑构建电脑对象,客户端代码不需要关心具体的构建过程,只需要通过指挥者来获取构建完成的产品。
1.3、原型模式(Prototype)
通过复制现有对象的方式创建新对象,避免了与对象类耦合。
1.3.1、简介
原型模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需知道对象的具体类型或如何创建对象。这种模式的核心思想是基于现有对象来生成新对象,而不是通过实例化类来创建新对象。
1.3.2、结构
原型模式包含以下几个核心角色:
- 原型接口(Prototype):声明了一个克隆自身的方法,是所有具体原型类的公共接口。
- 具体原型类(Concrete Prototype):实现了原型接口,提供了克隆自身的具体实现。
- 客户端(Client):负责创建新对象的对象,通过调用具体原型类的克隆方法来生成新对象。
1.3.3、工作原理和应用场景
-
原型模式的工作原理如下:
定义一个原型接口,其中包含一个克隆方法,用于生成新对象。 创建具体原型类,实现原型接口,并在克隆方法中进行对象的复制操作。
在客户端代码中,通过调用具体原型类的克隆方法来生成新对象,而不是通过实例化类来创建对象。 -
原型模式适用于以下场景:
-
当需要创建的对象包含复杂的构建过程,而且这个过程的开销比较大时,可以使用原型模式来提高性能。
-
当需要创建的对象与已有对象相似,只有部分属性不同,可以使用原型模式来快速生成变体对象。
-
当需要避免类之间的耦合关系,而又需要通过复制对象来创建新对象时,可以使用原型模式。
-
1.3.4、优缺点
-
优点
-
性能提升:避免了重复创建复杂对象的开销,通过复制现有对象可以快速生成新对象。
-
简化对象创建:不需要关心对象的具体创建过程,只需要复制一个现有对象即可。
-
灵活性:可以在运行时动态地克隆对象,而不需要修改代码。
-
-
缺点
-
对象构建过程复杂:如果对象包含的部分属性是引用类型,并且需要深度复制,会增加代码复杂度。
-
需要注意浅拷贝与深拷贝:如果对象内部有引用类型的成员变量,需要考虑是否需要进行深拷贝,否则可能会造成对象状态共享问题。
-
1.4.5、示例代码
以下是一个简单的示例代码,展示了如何使用原型模式创建克隆对象。
// 原型接口
interface Prototype {
Prototype clone();
void setProperty(String property);
String getProperty();
}
// 具体原型类
class ConcretePrototype implements Prototype {
private String property;
public Prototype clone() {
ConcretePrototype cloned = new ConcretePrototype();
cloned.setProperty(this.property);
return cloned;
}
public void setProperty(String property) {
this.property = property;
}
public String getProperty() {
return this.property;
}
}
客户端代码
public class Client {
public static void main(String[] args) {
// 创建原型对象
ConcretePrototype prototype = new ConcretePrototype();
prototype.setProperty("Original Property");
// 克隆原型对象
Prototype clonedPrototype = prototype.clone();
System.out.println("Original Prototype Property: " + prototype.getProperty());
System.out.println("Cloned Prototype Property: " + clonedPrototype.getProperty());
// 修改克隆对象的属性
((ConcretePrototype) clonedPrototype).setProperty("Modified Property");
System.out.println("Modified Cloned Prototype Property: " + clonedPrototype.getProperty());
}
}
在这个示例中,Prototype
是原型接口,定义了克隆方法和一些操作属性的方法。ConcretePrototype
是具体原型类,实现了原型接口,并在克隆方法中进行了对象的复制操作。在客户端代码中,我们先创建了一个原型对象,并设置了属性值,然后通过克隆方法生成了一个克隆对象,并输出属性值,最后修改了克隆对象的属性并输出。
运行以上代码会输出以下结果:
Original Prototype Property: Original Property
Cloned Prototype Property: Original Property
Modified Cloned Prototype Property: Modified Property
可以看到,通过原型模式成功地生成了克隆对象,并且可以对克隆对象进行修改而不影响原型对象。
1.4、工厂方法模式(Factory Method)
在父类中提供一个创建对象的接口,让子类决定实例化哪一个类。
1.4.1、简介
工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它提供了一个将对象创建的过程延迟到子类的接口。工厂方法模式的核心思想是定义一个创建对象的接口,但让子类决定实例化哪个类。这样可以将对象的创建和使用分离,使得系统更加灵活,能够根据需要动态地选择和创建对象。
1.4.2、意图与应用场景
工厂方法模式的工作流程如下:
定义一个抽象工厂接口,声明一个工厂方法用于创建产品对象。
创建具体产品类,实现抽象产品接口,定义产品的具体功能。
创建具体工厂类,实现抽象工厂接口,实现工厂方法用于创建具体产品对象。
在客户端代码中,通过实例化具体工厂类并调用工厂方法来创建具体产品对象,而不是直接实例化具体产品类。
工厂方法模式主要用于以下情景:
- 当一个类无法预知要创建的对象类,只有在运行时才能确定时,可以使用工厂方法模式。
- 当一个类希望由其子类来指定所创建对象的具体类时,可以使用工厂方法模式。
- 当一个类希望将对象的创建延迟到其子类时,可以使用工厂方法模式。
1.4.3、结构
工厂方法模式包含以下几个核心角色:
- 抽象产品(Product):定义了产品的接口,是所有具体产品类的公共接口。
- 具体产品(Concrete Product):实现了抽象产品接口,是工厂方法模式中要创建的具体对象。
- 抽象工厂(Creator):声明了一个工厂方法,用于创建产品对象,是所有具体工厂类的公共接口。
- 具体工厂(Concrete Creator):实现了抽象工厂接口,负责创建具体产品对象。
1.4.4、工作流程
-
优点
- 解耦合:将对象的创建和使用分离,降低了系统的耦合度。
- 灵活性:可以根据需要动态地选择和创建具体产品对象,使得系统更加灵活。
- 可扩展性:可以方便地增加新的具体产品类和具体工厂类,扩展性好。
-
缺点
- 类的数量增加:每增加一个具体产品类,就需要增加一个对应的具体工厂类,类的数量会增加。
1.4.5、示例代码(简化版)
以下是一个简化的工厂方法模式示例,展示了如何使用工厂方法模式创建具体产品对象。
// 抽象产品接口
interface Product {
void operation();
}
// 具体产品类A
class ConcreteProductA implements Product {
public void operation() {
System.out.println("Product A operation");
}
}
// 具体产品类B
class ConcreteProductB implements Product {
public void operation() {
System.out.println("Product B operation");
}
}
// 抽象工厂接口
interface Factory {
Product createProduct();
}
// 具体工厂类A
class ConcreteFactoryA implements Factory {
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂类B
class ConcreteFactoryB implements Factory {
public Product createProduct() {
return new ConcreteProductB();
}
}
客户端代码
public class Client {
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
是具体产品类,实现了抽象产品接口。Factory
是抽象工厂接口,声明了一个工厂方法用于创建产品对象。ConcreteFactoryA
和 ConcreteFactoryB
是具体工厂类,实现了抽象工厂接口,并在工厂方法中创建具体产品对象。在客户端代码中,我们通过实例化具体工厂类并调用工厂方法来创建具体产品对象,并调用产品对象的操作方法。
1.5、单例模式(Singleton)
确保一个类只有一个实例,并提供全局访问点。
1.5.1、简介
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。单例模式的核心思想是通过一个私有的静态变量来保存类的实例,并提供一个公共的静态方法来获取这个实例,确保在程序运行期间只有一个实例被创建和使用。
1.5.2、意图与应用场景
单例模式主要用于以下情景:
- 当一个类只能有一个实例,而且这个实例需要在全局范围内被访问时,可以使用单例模式。
- 当需要控制类的实例化过程,确保只有一个实例存在时,可以使用单例模式。
- 当需要共享一个资源或提供一个全局的访问点时,可以使用单例模式。
1.5.3、结构
单例模式包含以下几个核心角色:
- 单例类(Singleton):定义了一个静态方法来获取类的实例,确保只有一个实例存在。
- 私有构造函数:单例类的构造函数是私有的,确保外部无法直接实例化类。
- 静态变量:用于保存类的唯一实例。
- 静态方法:提供了一个全局的访问点来获取类的实例。
1.5.4、工作流程
单例模式的工作流程如下:
- 将类的构造函数设置为私有,防止外部直接实例化类。
- 在类的内部定义一个私有静态变量来保存类的唯一实例。
- 提供一个公共的静态方法来获取这个实例,在方法中判断实例是否已经存在,如果不存在则创建实例并返回。
1.5.5、优缺点
- 优点
- 全局访问点:提供了一个全局的访问点来访问类的唯一实例。
- 延迟实例化:只有在需要时才会创建实例,节省了资源。
- 控制实例化过程:可以控制类的实例化过程,确保只有一个实例存在。
- 缺点
- 可能引起线程安全问题:在多线程环境下,如果没有适当的措施,可能会出现多个线程同时创建实例的情况。
- 可能造成资源浪费:如果实例一直不被使用,可能会造成资源的浪费。
1.5.6、示例代码(简化版)
以下是一个简化的单例模式示例,展示了如何实现一个线程安全的单例类。
public class Singleton {
// 私有静态变量,用于保存唯一实例
private static Singleton instance;
// 私有构造函数,防止外部实例化类
private Singleton() {}
// 公共静态方法,获取类的唯一实例
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// 其他方法
public void doSomething() {
System.out.println("Singleton instance is doing something");
}
}
客户端代码
public class Client {
public static void main(String[] args) {
// 获取单例实例
Singleton singleton = Singleton.getInstance();
singleton.doSomething();
}
}
在这个示例中,Singleton
是单例类,其中私有静态变量 instance
用于保存唯一实例。私有构造函数确保外部无法直接实例化类。公共静态方法 getInstance
提供了全局访问点来获取唯一实例,并通过加锁保证了线程安全。在客户端代码中,我们通过调用 getInstance
方法来获取单例实例,并调用其方法进行操作。
二、结构型模式
这类模式关注类和对象的组合,继承的概念被用来组合接口和定义组合对象获得新功能的方式。
记忆方法:代理员外适合乔装
持续更新中。。。关注我,一起进步~
2.1、代理模式(Proxy)
为其他对象提供一种代理以控制对这个对象的访问。
2.2、享元模式(Flyweight)
通过共享技术来有效地支持大量细粒度的对象。
2.3、外观模式(Facade)
提供了一个统一的接口,用来访问子系统中的一群接口。
2.4、适配器模式(Adapter)
允许接口不兼容的对象能够相互合作。
2.5、组合模式(Composite)
将对象组合成树形结构以表示“部分-整体”的层次结构。
2.6、桥接模式(Bridge)
将抽象部分与实现部分分离,使它们都可以独立地变化。
2.7、装饰器模式(Decorator)
动态地给对象添加一些额外的职责。
三、行为型模式
这类模式专注于对象之间的通信。
记忆方法:访问者观察备忘录状态,老板任命中介解释迭代器策略。
3.1、访问者模式(Visitor)
允许一个或多个操作应用到一组对象上,解耦操作和对象本身。
3.2、观察者模式(Observer)
当一个对象状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
3.3、备忘录模式(Memento)
在不破坏封装的前提下,捕获并保存一个对象的内部状态。
3.4、状态模式(State)
允许一个对象在其内部状态改变时改变它的行为。
3.5、模板方法模式(Template Method)
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。
3.6、责任链模式(Chain of Responsibility)
为请求创建了一个接收者对象的链。
3.7、命令模式(Command)
将请求封装成对象,从而让你使用不同的请求、队列或日志请求,并支持可撤销的操作。
3.8、中介者模式(Mediator)
通过引入一个第三方对象(中介者)来控制两个对象(同事)之间的交互。
3.9、解释器模式(Interpreter)
实现一个表达式接口,该接口解释一个特定的上下文。
3.10、迭代器模式(Iterator)
提供一种方法顺序访问一个集合对象中的各个元素,而又不暴露其内部的表示。
3.11、策略模式(Strategy)
定义一系列的算法,把它们一个个封装起来,并使它们可相互替换。