本系列文章简介:
设计模式是在软件开发过程中,经过实践和总结得到的一套解决特定问题的可复用的模板。它是一种在特定情境中经过验证的经验和技巧的集合,可以帮助开发人员设计出高效、可维护、可扩展和可复用的软件系统。设计模式提供了一种在设计和编码过程中的指导,它用于解决常见的设计问题,并提供了一种标准化的方案。设计模式能够帮助开发人员降低系统的复杂性,提高代码的可读性和可维护性。本系列文章将详细讲解Java中的23中设计模式 ,并配有图文解析及相应的代码示例,欢迎大家订阅《Java技术栈高级攻略》专栏,一起学习,一起涨分!
目录
6.1.2 抽象工厂模式(Abstract Factory Pattern)
1、引言
设计模式是一种解决常见软件设计问题的经验总结,它提供了一套可重用的设计思想和方法,帮助开发人员更好地组织和设计他们的代码。在软件开发中,我们经常会遇到一些常见的问题,比如如何实现代码的灵活性、可扩展性、可维护性和可复用性,以及如何减少代码的耦合性等。设计模式通过定义一些通用的解决方案来解决这些问题,从而提高代码的质量和可维护性。
设计模式的概念最早由四位名为Gang of Four(GoF)的作者提出,他们在《设计模式:可复用面向对象软件的基础》一书中总结了23种常见的设计模式。这些设计模式包括创建型模式、结构型模式和行为型模式,它们分别用于解决对象创建、对象组合和对象交互等问题。每个设计模式都有其固定的结构和用法,开发人员可以根据具体的问题选择合适的设计模式来解决。
本文将介绍这23种常见的设计模式,详细解释它们的原理、应用场景和使用方法。通过学习这些设计模式,开发人员可以更好地理解软件设计的原则和思想,提高自己的设计能力和编码水平。设计模式不仅是一种编码技巧,更是一种思维方式和设计原则的体现。希望通过本文的介绍,读者能够更好地掌握和应用设计模式,写出更优雅、可扩展和可维护的代码。
2、设计模式概述
设计模式是一种解决问题的可复用的设计思路,它提供了一套经验丰富且经过验证的解决方案。设计模式可以帮助开发人员在软件开发过程中更加灵活、高效地解决常见问题,并提高代码的可维护性和可扩展性。设计模式提供了一种标准化和结构化的方法来解决一些常见的设计问题,使得代码更易于理解和维护。
3、设计模式的基本要素
设计模式的基本要素包括:
-
模式名称:设计模式通常以一个或多个词来描述模式的目的和特征。
-
问题描述:模式描述了一个常见的问题或场景,涉及到解决特定问题时的需求和约束条件。
-
解决方案:模式描述了一个通用的解决方案,用于解决特定问题或满足特定需求。
-
结构:模式描述了解决方案的组成部分和它们之间的关系。
-
参与者:模式描述了解决方案中的角色和它们之间的通信和协作关系。
-
协作:模式描述了参与者之间如何协同工作来达到特定目的。
-
效果:模式描述了解决方案带来的优点和可能的缺点。
-
相关模式:模式描述了该模式与其他模式之间的关系和相互作用。
-
示例代码:模式通常会提供一个或多个示例代码,用于演示如何实现和使用该模式。
-
应用场景:模式描述了适用该模式的问题或场景,并提供了一些使用该模式的示例场景。
这些要素共同描述了一个设计模式的目的、解决方案和应用方式,帮助开发人员更好地理解和应用设计模式。
4、设计模式分类
设计模式是解决软件设计中常见问题的典型解决方案的规范化描述。根据问题和解决方案的不同,设计模式可以分为三大类:创建型模式、结构型模式和行为型模式。
4.1 创建型模式(Creational)
创建型模式是一种软件设计模式,它提供了一种创建对象的机制,可以根据需要动态地创建对象,而无需直接实例化对象。
创建型模式包括以下几种:
-
工厂模式(Factory Pattern):定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到子类。
-
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
-
单例模式(Singleton Pattern):保证一个类只有一个实例,并提供一个全局访问点。
-
建造者模式(Builder Pattern):将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
-
原型模式(Prototype Pattern):通过复制现有对象来创建新对象,而不是使用构造函数。
这些模式的目的都是提供一种灵活的方式来创建对象,使得系统更加可扩展和可维护。通过使用这些模式,可以将对象的创建过程和使用过程分离,降低耦合性,提高代码的复用性和可测试性。
4.2 结构型模式(Structural)
结构型模式是设计模式的一种,用于解决对象之间的组合、关联和结构的问题。结构型模式可以帮助我们设计和组织类、对象和系统的结构,以便于更好地实现系统的功能。
常见的结构型模式包括以下几种:
-
适配器模式(Adapter Pattern):用于将一个类的接口转换成客户端所期望的另一种接口。
-
桥接模式(Bridge Pattern):将抽象部分与实现部分分离,使它们可以独立变化。
-
组合模式(Composite Pattern):将对象组合成树形结构以表示“部分-整体”的层次结构。
-
装饰器模式(Decorator Pattern):动态地给对象添加额外的职责。
-
外观模式(Facade Pattern):提供一个统一的接口,用于访问子系统中的一群接口。
-
享元模式(Flyweight Pattern):使用共享对象可有效地支持大量的细粒度的对象。
-
代理模式(Proxy Pattern):为其他对象提供一个代理以控制对这个对象的访问。
这些结构型模式可以根据不同的情况选择使用,以解决特定的设计问题。
4.3 行为型模式(Behavioral)
行为型模式是软件设计中的一种设计模式,用于描述对象之间的相互作用和通信方式。行为型模式关注的是对象的行为和责任的分配,以及对象之间的相互关系,实现对象之间的松耦合。
常见的行为型模式包括:
-
解释器模式(Interpreter Pattern):给定一个语言,定义其文法的一种表示,并定义一个解释器,用来解释该语言中的句子。
-
模板方法模式(Template Method Pattern):一种行为型设计模式,它定义了一个算法的骨架,将一些步骤的具体实现延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义某些步骤的实现。
-
观察者模式(Observer Pattern):定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会被通知并自动更新。
-
策略模式(Strategy Pattern):定义了一系列的算法,并将其封装成独立的对象,使得它们可以互相替换。策略模式使得算法可以独立于客户端而变化。
-
命令模式(Command Pattern):将请求封装成对象,使得可以将请求的发送者和接收者解耦,实现请求的发送者和接收者之间的松耦合。
-
职责链模式(Chain of Responsibility Pattern):将请求的发送者和接收者解耦,使多个对象都有机会处理该请求,将这些对象串成一条链,并沿着这条链传递该请求,直到有对象处理它。
-
迭代器模式(Iterator Pattern):提供一种访问一个容器对象中各个元素的方式,而不需要暴露该对象的内部表示。
-
中介者模式(Mediator Pattern):用一个中介对象封装一系列对象的交互,中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
-
备忘录模式(Memento Pattern):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以把该对象恢复到之前的状态。
-
状态模式(State Pattern):允许一个对象在其内部状态改变时改变其行为,对象看起来似乎修改了其类。
-
访问者模式(Visitor Pattern):表示一个作用于某对象结构中的各元素的操作,可以在不改变这些元素的类的前提下定义新的操作。
5、设计模式概览
6、设计模式详解
6.1 创建型模式(Creational)
6.1.1 工厂模式(Factory Pattern)
6.1.1.1 简介
工厂模式是一种创建对象的设计模式,它通过定义一个工厂类来创建对象实例,而不是通过直接调用构造函数来创建对象。工厂模式将对象的创建过程封装在工厂类中,客户端只需要通过工厂类来创建对象,而不需要了解对象的具体实现细节。
工厂模式可以将对象的创建与使用分离,提供了一种灵活的方式来创建对象。它可以根据实际的需求来选择具体的对象实现,并且可以随时更换对象的实现,而不影响客户端的代码。
在工厂模式中,通常有一个抽象工厂类,该类定义了创建对象的接口,具体的对象创建由具体的工厂子类来实现。客户端通过调用工厂类的方法来创建对象,而不需要直接调用构造函数。这样可以将对象的创建过程集中在工厂类中,方便管理和维护。
工厂模式可以根据具体的需求来创建不同类型的对象,例如创建数据库连接、创建窗口控件、创建日志对象等。它能够提供一种统一的接口来创建对象,方便扩展和修改对象的实现,同时也提高了代码的可维护性和可读性。
6.1.1.2 优缺点
工厂模式的优点:
- 降低代码耦合度:工厂模式将对象的创建和使用分离,客户端只需要知道工厂接口,而不需要知道具体的产品实现类,降低了代码的耦合度。
- 简化对象创建过程:通过工厂模式,可以隐藏对象创建的细节,客户端只需要调用工厂方法即可获得所需的对象,简化了对象创建过程。
- 扩展性好:当需要新增一种产品时,只需要新增对应的产品实现类和工厂类,不需要修改已有代码,符合开闭原则。
- 提供接口的统一管理:工厂模式通过工厂接口来管理具体产品的创建,可以统一管理和控制产品的生成过程。
工厂模式的缺点:
- 增加了系统的抽象性和理解难度:工厂模式引入了额外的抽象层,增加了系统的复杂性和理解难度,不适用于简单的场景。
- 增加了系统的类的个数:引入工厂模式会增加系统的类的数量,增加了代码量和维护成本。
- 不符合开闭原则:工厂模式在新增产品时,需要新增相应的产品实现类和工厂类,违背了开闭原则。
6.1.1.3 使用场景
工厂模式适用于以下场景:
- 当一个类无法预知需要创建哪种类型的对象时,可以使用工厂模式。工厂类会根据传入的参数或条件来决定创建哪种类型的对象。
- 当创建一个对象的过程比较复杂,需要进行一些初始化操作或者涉及到多个步骤时,可以使用工厂模式。工厂类可以对这些复杂的创建过程进行封装,使调用方只需要简单地调用工厂方法即可得到所需的对象。
- 当需要统一管理创建的对象时,可以使用工厂模式。工厂类可以负责对象的创建、缓存、销毁等管理操作,使对象的生命周期得到有效的控制。
- 当需要将对象的创建与使用解耦时,可以使用工厂模式。调用方只需要通过工厂类来获取所需的对象,而不需要关心对象的具体创建细节。
- 当需要通过某种方式动态地切换对象的创建方式时,可以使用工厂模式。只需要修改工厂类的实现,而不需要修改调用方的代码,就可以实现对象创建方式的切换。
6.1.1.4 使用案例
工厂模式是一种常用的设计模式,用于创建对象的实例化过程封装。它提供了一种通过调用工厂方法来创建对象的方式,而无需直接使用new关键字来实例化对象。
下面是一个简单的工厂模式的Java使用案例:
首先,我们定义一个接口Shape
,用于表示各种形状:
public interface Shape {
void draw();
}
然后,我们实现三个具体的形状类,分别是Circle
、Rectangle
和Square
,它们都实现了Shape
接口:
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Drawing a square");
}
}
接下来,我们创建一个工厂类ShapeFactory
,用于根据给定的参数创建不同的形状对象:
public class ShapeFactory {
public Shape createShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("circle")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("rectangle")) {
return new Rectangle();
} else if (shapeType.equalsIgnoreCase("square")) {
return new Square();
}
return null;
}
}
最后,我们可以在客户端代码中使用工厂类来创建并使用不同形状的对象:
public class Main {
public static void main(String[] args) {
ShapeFactory factory = new ShapeFactory();
// 创建一个圆形
Shape circle = factory.createShape("circle");
circle.draw();
// 创建一个矩形
Shape rectangle = factory.createShape("rectangle");
rectangle.draw();
// 创建一个正方形
Shape square = factory.createShape("square");
square.draw();
}
}
运行上述代码,输出结果如下:
Drawing a circle
Drawing a rectangle
Drawing a square
通过工厂模式,我们可以将对象的创建过程封装起来,使客户端代码与具体对象的类耦合度降低,增强了代码的可扩展性和可维护性。
6.1.2 抽象工厂模式(Abstract Factory Pattern)
6.1.2.1 简介
抽象工厂模式是一种设计模式,旨在提供一个统一的接口来创建一系列相关或相互依赖的对象,而无需指定具体的类。
在抽象工厂模式中,有一个抽象工厂类,声明了创建不同类型对象的抽象方法。具体的工厂类继承自抽象工厂类,并实现了抽象方法来创建具体的对象。每个具体的工厂类对应一组具体的产品类。
在客户端代码中,通过实例化具体的工厂类,可以得到一组具体的产品对象。客户端代码与具体的工厂类及产品类分离,可以方便地替换不同工厂类或产品类。
抽象工厂模式可以帮助我们实现高层的解耦,同时还可以提供一定程度的扩展性。缺点是如果有新的产品类需要添加,需要修改抽象工厂类的接口及所有的具体工厂类。
6.1.2.2 优缺点
抽象工厂模式是一种用于创建相关或依赖对象系列的设计模式。它提供了一种将具体类的实例化延迟到子类的方法,以便子类可以决定创建的具体对象类型。
优点:
- 隔离了具体类的实现:客户端只需要知道抽象工厂和抽象产品的接口,而不需要了解具体的实现。这样可以减少客户端与具体类的耦合,使得系统更加灵活和可扩展。
- 提供了一种可替换的实现:由于抽象工厂模式将具体类的实现与客户端分离,可以很容易地替换具体工厂和产品的实现,从而实现系统的灵活性。
- 符合开闭原则:当需要新增一种产品系列时,只需要新增对应的具体工厂和产品类,不需要修改已有的代码,符合开闭原则。
缺点:
- 增加了系统的复杂性:引入抽象工厂模式会增加系统的类和对象数量,增加了系统的复杂性,使得系统难以理解和维护。
- 不易扩展新的产品:由于抽象工厂模式中的抽象工厂和产品是固定的,当需要新增一种产品时,需要修改抽象工厂和所有具体工厂的代码,导致系统扩展困难。
- 不符合单一职责原则:抽象工厂模式将一系列产品的创建逻辑封装在一个工厂类中,可能会导致该工厂类承担过多的职责,违反了单一职责原则。
6.1.2.3 使用场景
抽象工厂模式通常在以下场景中使用:
-
当需要创建一组相互关联或相互依赖的对象时,可以使用抽象工厂模式。例如,在一个图形编辑器中,需要创建不同类型的图形对象(如圆形、正方形、矩形等),并且这些对象需要具有相同的样式(如颜色、边框样式等),可以使用抽象工厂模式创建不同类型的图形对象和样式对象。
-
当需要创建一组具有相同接口的对象,并且客户端不关心具体实现时,可以使用抽象工厂模式。例如,在一个图形界面库中,需要提供不同类型的按钮和文本框,但是客户端只需要关心这些组件具备的基本功能(如点击事件、输入文本等),可以使用抽象工厂模式创建不同类型的按钮和文本框对象。
-
当希望客户端与具体类的实现解耦时,可以使用抽象工厂模式。例如,客户端需要使用数据库访问接口进行数据操作,但是不希望直接依赖于某个具体的数据库实现,可以使用抽象工厂模式创建数据库访问接口的具体实现。
-
当需要在运行时动态切换不同的产品族时,可以使用抽象工厂模式。例如,在一个电子设备管理系统中,需要支持不同供应商的设备(如摄像头、打印机等),可以使用抽象工厂模式创建不同供应商的设备对象,并在运行时动态切换不同的设备供应商。
总之,抽象工厂模式适用于需要创建一组相关对象,并且希望客户端与具体实现解耦的场景。
6.1.2.4 使用案例
下面是一个使用抽象工厂模式的Java案例,假设我们正在开发一个角色扮演游戏,需要创建不同种类的角色和武器。
首先,我们需要定义一个抽象工厂接口CharacterFactory
,该接口包含了创建角色和武器的方法:
public interface CharacterFactory {
Character createCharacter();
Weapon createWeapon();
}
然后,我们实现了两个具体的工厂类MedievalFactory
和SciFiFactory
,分别用于创建中世纪和科幻风格的角色和武器:
// 中世纪工厂
public class MedievalFactory implements CharacterFactory {
public Character createCharacter() {
return new Knight();
}
public Weapon createWeapon() {
return new Sword();
}
}
// 科幻工厂
public class SciFiFactory implements CharacterFactory {
public Character createCharacter() {
return new Robot();
}
public Weapon createWeapon() {
return new LaserGun();
}
}
接下来,我们定义了两个角色类Knight
和Robot
,以及两种武器类Sword
和LaserGun
,分别对应中世纪和科幻风格的角色和武器。
最后,我们可以使用抽象工厂模式来创建不同风格的角色和武器:
public class Main {
public static void main(String[] args) {
// 创建中世纪角色和武器
CharacterFactory medievalFactory = new MedievalFactory();
Character medievalCharacter = medievalFactory.createCharacter();
Weapon medievalWeapon = medievalFactory.createWeapon();
medievalCharacter.attack(medievalWeapon);
medievalCharacter.defend();
// 创建科幻角色和武器
CharacterFactory sciFiFactory = new SciFiFactory();
Character sciFiCharacter = sciFiFactory.createCharacter();
Weapon sciFiWeapon = sciFiFactory.createWeapon();
sciFiCharacter.attack(sciFiWeapon);
sciFiCharacter.defend();
}
}
在这个案例中,抽象工厂模式帮助我们通过CharacterFactory
接口创建不同风格的角色和武器,使得客户端代码不需要知道具体的实现类。这样,当我们需要添加新的角色或武器时,只需要创建新的工厂类和相应的角色和武器类即可,而无需修改现有的代码。
6.1.3 单例模式(Singleton Pattern)
6.1.3.1 简介
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。在单例模式中,类的构造函数是私有的,这意味着其他类无法直接实例化该类。而是通过一个静态方法或属性来获取类的唯一实例。
6.1.3.2 优缺点
单例模式是一种设计模式,其目的是确保一个类只有一个实例,并提供一个全局访问点。
优点:
- 在内存中只有一个实例,节省了系统资源,提高了性能。
- 简化了对象的管理,避免了重复创建和销毁对象的开销。
- 全局访问点可以让其他对象轻松地访问单例对象。
缺点:
- 单例模式会增加代码的复杂度,使代码的可读性和可维护性降低。
- 单例模式对扩展不友好,如果未来需要扩展单例对象的功能,可能需要修改已有的代码。
- 单例模式的单一实例可能会成为系统的瓶颈,如果该实例需要进行大量的计算或者处理大量的数据,可能会成为系统的性能瓶颈。
- 单例模式不适用于多线程环境下,如果多个线程同时访问单例对象,可能会导致竞争条件和数据不一致的问题,需要额外的代码来处理线程安全性。
总结起来,单例模式在某些情况下可以提供一些好处,但也需要谨慎使用,以免引入不必要的复杂性和潜在的问题。
6.1.3.3 使用场景
单例模式的使用场景有以下几种:
-
需要将某个类的实例化对象保持全局唯一,例如日志记录器、数据库连接池等。
-
需要频繁创建和销毁对象,且创建和销毁对象的代价较大,单例模式可以避免频繁创建和销毁,提高性能。
-
需要访问共享资源,例如线程池、线程管理器等。
-
需要确保系统中某个类只有一个实例存在,以便其他对象可以通过该实例访问公共资源。
总之,单例模式适用于那些需要全局唯一的对象,并且需要在多个地方进行访问的场景。
6.1.3.4 使用案例
单例模式是一种创建型设计模式,用于确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
以下是一个使用单例模式的Java案例:
public class Singleton {
private static Singleton instance;
// 私有构造函数,禁止外部直接创建对象
private Singleton() {
// 初始化代码
}
// 公共静态方法来获取实例
public static Singleton getInstance() {
if (instance == null) {
// 在第一次调用时,创建实例
instance = new Singleton();
}
return instance;
}
// 其他业务方法
public void showMessage() {
System.out.println("Hello, World!");
}
}
在上面的代码中,Singleton类只有一个私有的静态成员变量instance,用来存储类的唯一实例。getInstance方法是公共的静态方法,用来获取该实例。
使用示例:
public class Main {
public static void main(String[] args) {
// 获取Singleton实例
Singleton singleton = Singleton.getInstance();
// 调用业务方法
singleton.showMessage(); // 输出:Hello, World!
}
}
在上面的示例中,我们通过Singleton.getInstance()方法获取了Singleton类的唯一实例,并通过该实例调用了showMessage方法。
这种设计模式的好处是它确保任何时候都只有一个类的实例存在,避免了重复创建实例的开销,并提供了一个全局访问点来访问该实例。
6.1.4 建造者模式(Builder Pattern)
6.1.4.1 简介
建造者模式是一种创建型设计模式,它将对象的构建过程与其表示分离,以便相同的构建过程可以创建不同的表示。它允许你使用相同的构建代码来创建不同类型和表示的对象。
在建造者模式中,有一个建造者类,它负责创建对象的各个部分,并将它们组装成一个完整的对象。该建造者类可以包含多个方法,每个方法用于构建对象的一个部分。此外,还有一个指挥者类,它指导建造者类如何进行构建。
使用建造者模式的主要目的是将复杂对象的构建过程与其表示分离,使其更加灵活和可扩展。通过使用建造者模式,可以使构建过程更加直观,清晰,并且可以根据需要灵活地组装不同类型和表示的对象。
建造者模式的主要角色包括:
- 产品类:表示被构建的复杂对象。
- 抽象建造者类:定义构建产品所需要的各个部分的抽象方法。
- 具体建造者类:实现抽象建造者类中定义的方法,具体构建产品的各个部分。
- 指挥者类:指导具体建造者类如何进行构建,负责调用具体建造者类的方法来构建产品。
6.1.4.2 优缺点
建造者模式的优缺点如下:
优点:
- 建造者模式可以将一个复杂对象的构建过程抽象出来,使得构建过程与表示相分离,使得相同的构建过程可以创建不同的表示。
- 可以更加精细地控制对象的构建过程,使得构建的对象可以符合特定的需求和约束。
- 可以简化客户端的使用,客户端只需要指定需要构建的类型和顺序,而不需要关心具体的构建过程。
缺点:
- 建造者模式需要定义一个独立的建造者类,增加了系统的复杂度。
- 如果对象的结构较为简单,或者对象的构建过程较为简单,使用建造者模式可能会显得繁琐。
- 如果对象的部分属性之间存在较强的关联关系,而且需要以某种特定的顺序来构建对象,可能会增加建造者类的复杂度。
6.1.4.3 使用场景
建造者模式适用于以下场景:
-
当创建对象的过程比较复杂,包含多个步骤、条件和子对象时,可以使用建造者模式来封装对象的创建过程,并提供一个统一的接口。
-
当需要创建的对象具有多种变化组合时,可以使用建造者模式来构建不同组合的对象,而不需要创建大量的构造函数或者使用复杂的参数列表。
-
当需要在创建对象时进行一些额外的处理或者初始化操作时,可以使用建造者模式来在创建对象的过程中执行这些操作,而不需要在每个地方都写重复的代码。
-
当需要创建一个不可变对象时,可以使用建造者模式来通过链式调用的方式逐步构建对象的属性,最后通过调用build方法来返回一个不可变的对象。
总的来说,建造者模式适用于需要创建复杂对象、有多种组合方式或者需要在创建过程中进行一些额外处理的场景。
6.1.4.4 使用案例
建造者模式是一种创建型设计模式,它允许你通过一步一步地构造复杂对象。它将构造过程和表示对象的内部结构分离,使得同样的构建过程可以创建不同的表示。
在Java中,可以使用建造者模式来创建复杂的对象,避免构造函数的参数过多或使用过长的参数列表来构造对象。下面是一个使用建造者模式的Java案例。
假设有一个汽车类Car,它有多个可选属性,例如颜色、引擎类型、座位数量等。我们希望能够方便地创建不同配置的汽车对象。首先,我们定义Car类:
public class Car {
private String color;
private String engineType;
private int seatCount;
// 私有构造函数,只能通过Builder来创建Car对象
private Car(Builder builder) {
this.color = builder.color;
this.engineType = builder.engineType;
this.seatCount = builder.seatCount;
}
// Getter方法
public String getColor() {
return color;
}
public String getEngineType() {
return engineType;
}
public int getSeatCount() {
return seatCount;
}
// 内部静态Builder类,用于构建Car对象
public static class Builder {
private String color;
private String engineType;
private int seatCount;
// 设置必需的参数
public Builder(String color, String engineType) {
this.color = color;
this.engineType = engineType;
}
// 设置可选参数
public Builder seatCount(int seatCount) {
this.seatCount = seatCount;
return this;
}
// 构建Car对象
public Car build() {
return new Car(this);
}
}
}
现在,我们可以使用Builder类来创建不同配置的Car对象。例如:
Car car1 = new Car.Builder("red", "V8").seatCount(4).build();
Car car2 = new Car.Builder("blue", "electric").seatCount(2).build();
上述代码中,我们首先通过Builder类创建一个Builder对象,然后使用链式调用来设置可选参数,最后调用build()方法来构建Car对象。
使用建造者模式的好处是,可以按需设置对象的属性,而不必关心构造函数的参数顺序或参数类型。这样可以使代码更加清晰、易读和易于维护。
6.1.5 原型模式(Prototype Pattern)
6.1.5.1 简介
原型模式是一种创建型设计模式,它通过复制现有对象来生成新对象,而无需显式地使用构造函数。在原型模式中,通过克隆一个已经存在的对象的原型来创建新的对象,并且可以根据需要进行参数的修改。
该模式的主要目的是尽量减少对象的创建,通过复制已有对象的原型来创建新对象,可以在某些场景下提升性能和效率。
原型模式通常包含以下几个角色:
-
原型接口/抽象类(Prototype):定义克隆方法的接口或抽象类,所有具体原型类都要实现或继承此接口。
-
具体原型类(ConcretePrototype):实现克隆方法的具体原型类。
-
客户端(Client):使用原型对象的客户端,在需要创建新对象时,通过克隆原型对象来创建。
6.1.5.2 优缺点
原型模式的优点:
- 简化对象的创建过程:原型模式通过复制现有对象来创建新对象,避免了创建对象的复杂的初始化步骤,使得对象的创建过程更加简单。
- 提高性能:原型模式通过复制现有对象来创建新对象,避免了对象的重新初始化,可以提高创建对象的效率。
- 动态添加和删除对象:原型模式允许动态地添加和删除原型对象,方便了对象的管理。
- 隐藏创建过程:原型模式将对象的创建过程封装在原型类中,客户端只需要通过复制操作即可获取新对象,不需要关心对象的创建细节。
原型模式的缺点:
- 需要对原型对象进行克隆操作:使用原型模式需要在每个具体原型类中实现克隆操作,如果对象的成员变量较多或者成员变量为引用类型,需要对每个引用对象进行深拷贝,实现克隆操作可能会比较复杂。
- 可能导致循环引用问题:在克隆操作中,如果存在循环引用,可能导致克隆对象中出现重复的对象,影响对象的正确性。
- 可能导致性能问题:使用原型模式创建对象时,会通过复制操作来创建新对象,如果对象的复制过程比较复杂,可能会导致性能问题。
6.1.5.3 使用场景
原型模式的主要使用场景如下:
-
当一个对象的创建过程比较复杂或者耗时时,可以使用原型模式来复制一个已经存在的对象,然后进行适当的修改,以减少对原对象的创建过程和资源消耗。
-
当一个对象需要在不同的场景中多次创建时,可以使用原型模式来复制一个已有的对象,然后进行适当的修改和定制,以满足不同场景的需求。
-
当一个对象的创建过程需要依赖于其他对象或者配置信息时,可以使用原型模式来复制一个已有的对象,然后进行适当的修改和配置,以简化对象的创建过程。
-
当一个对象的创建过程需要根据运行时的条件决定时,可以使用原型模式来复制一个已有的对象,然后进行适当的修改和定制,以根据不同的条件创建不同的对象。
总之,原型模式适用于对象创建过程复杂、耗时或者需要重复创建的场景,可以通过复制已有对象来简化和加快对象的创建过程。
6.1.5.4 使用案例
原型模式是一种创建型设计模式,它通过复制现有对象的原型来创建新的对象。与使用new关键字创建新对象相比,原型模式更加灵活和高效。
在Java中,实现原型模式主要涉及以下几个步骤:
- 创建一个原型接口(或抽象类),声明一个克隆方法clone(),用于复制对象。
public interface Prototype {
Prototype clone();
}
2. 实现原型接口的具体类,该类需要实现clone()方法来复制对象。
public class ConcretePrototype implements Prototype {
private String name;
public ConcretePrototype(String name) {
this.name = name;
}
public Prototype clone() {
return new ConcretePrototype(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3. 在客户端代码中使用原型对象来创建新对象,而无需显式地调用构造函数。
public class Client {
public static void main(String[] args) {
ConcretePrototype prototype = new ConcretePrototype("Prototype");
// 使用原型对象创建新对象
ConcretePrototype clone = (ConcretePrototype) prototype.clone();
clone.setName("Cloned Prototype");
System.out.println("Original Prototype: " + prototype.getName());
System.out.println("Cloned Prototype: " + clone.getName());
}
}
以上代码中,我们定义了一个原型接口Prototype
,其中包含了一个clone()
方法用于复制对象。然后我们实现了一个具体的原型类ConcretePrototype
,该类实现了Prototype
接口,并复写了clone()
方法来创建新对象。
在客户端代码中,我们首先创建一个原型对象prototype
,然后使用原型对象来创建新对象clone
。通过调用clone()
方法,clone
对象将拥有和prototype
相同的属性值,并且它们是两个独立的对象。
最后,我们打印输出了原型对象和克隆对象的名称,可以看到它们的名称分别为"Prototype"和"Cloned Prototype"。这证明了原型模式的成功复制和创建对象的能力。
原型模式在Java中的应用非常广泛,特别是在需要创建大量相似对象的场景下,原型模式能够提供一种高效的解决方案。通过复制现有对象的原型,无需重复创建新对象,从而提高性能和代码的可维护性。
6.2 结构型模式(Structural)
详见《Java设计模式大全:23种常见的设计模式详解(二)》
6.3 行为型模式(Behavioral)
详见《Java设计模式大全:23种常见的设计模式详解(三)》
7、结语
文章至此,已接近尾声!希望此文能够对大家有所启发和帮助。同时,感谢大家的耐心阅读和对本文档的信任。在未来的技术学习和工作中,期待与各位大佬共同进步,共同探索新的技术前沿。最后,再次感谢各位的支持和关注。您的支持是作者创作的最大动力,如果您觉得这篇文章对您有所帮助,请考虑给予一点打赏。