玄子Share 设计模式 GOF 全23种 + 七大设计原则
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NWLAOFtO-1691793071647)(./assets/%E7%8E%84%E5%AD%90Share%20%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20GOF%20%E5%85%A823%E7%A7%8D%20+%20%E4%B8%83%E5%A4%A7%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99.png)]
目录
GoF(Gang of Four)
GoF(Gang of Four)是四位计算机科学家(Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides)在1994年出版的书籍《设计模式:可复用面向对象软件的基础》中提出的一种设计模式分类和解决方案。该书介绍了23种常用的设计模式,分为创建型、结构型和行为型三大类。这本书的社会认可度非常高,被公认为设计模式领域的经典之作。
- 影响深远:GoF的设计模式成为了软件工程领域的经典之一,对于软件设计和架构具有广泛的应用。许多软件开发人员和团队都在日常工作中使用这些模式来解决常见的设计问题。
- 经典的设计原则:GoF的设计模式不仅提供了具体的解决方案,还强调了面向对象设计的一些重要原则,如封装、继承、多态和接口分离等。
- 共同的设计术语:GoF提出的模式在软件开发社区中创建了共同的设计术语和设计思维方式,使得开发人员之间更容易进行沟通和理解。
- 可复用性和可维护性:通过应用这些设计模式,开发人员可以更好地设计出具有可复用性和可维护性的软件系统,降低了代码的耦合性。
总的来说,GoF的设计模式在软件开发领域得到了广泛的认可和应用,成为了许多软件开发人员必备的知识和工具之一。它为软件设计和架构提供了有力的指导,帮助开发人员构建高质量、可扩展的软件系统。
创建型模式(Creational Patterns)
序号 | 中文名称 | 英文名称 | 所用设计原则 |
---|---|---|---|
1 | 工厂方法模式 | Factory Method Pattern | SRP、OCP、LSP、DIP |
2 | 抽象工厂模式 | Abstract Factory Pattern | SRP、OCP、LSP、DIP |
3 | 单例模式 | Singleton Pattern | SRP、OCP、LSP、DIP |
4 | 原型模式 | Prototype Pattern | SRP、OCP、LSP、DIP |
5 | 建造者模式 | Builder Pattern | SRP、OCP、LSP、DIP |
结构型模式(Structural Patterns)
序号 | 中文名称 | 英文名称 | 所用设计原则 |
---|---|---|---|
6 | 适配器模式 | Adapter Pattern | SRP、OCP、LSP、ISP、DIP |
7 | 桥接模式 | Bridge Pattern | SRP、OCP、LSP、ISP、DIP |
8 | 组合模式 | Composite Pattern | SRP、OCP、LSP、ISP、DIP |
9 | 装饰模式 | Decorator Pattern | SRP、OCP、LSP、ISP、DIP |
10 | 外观模式 | Facade Pattern | SRP、OCP、LSP、ISP、DIP |
11 | 享元模式 | Flyweight Pattern | SRP、OCP、LSP、ISP、DIP |
12 | 代理模式 | Proxy Pattern | SRP、OCP、LSP、ISP、DIP |
行为型模式(Behavioral Patterns)
序号 | 中文名称 | 英文名称 | 所用设计原则 |
---|---|---|---|
13 | 责任链模式 | Chain of Responsibility Pattern | SRP、OCP、LSP、ISP、DIP |
14 | 命令模式 | Command Pattern | SRP、OCP、LSP、ISP、DIP |
15 | 解释器模式 | Interpreter Pattern | SRP、OCP、LSP、ISP、DIP |
16 | 迭代器模式 | Iterator Pattern | SRP、OCP、LSP、ISP、DIP |
17 | 中介者模式 | Mediator Pattern | SRP、OCP、LSP、ISP、DIP |
18 | 备忘录模式 | Memento Pattern | SRP、OCP、LSP、ISP、DIP |
19 | 观察者模式 | Observer Pattern | SRP、OCP、LSP、ISP、DIP |
20 | 状态模式 | State Pattern | SRP、OCP、LSP、ISP、DIP |
21 | 策略模式 | Strategy Pattern | SRP、OCP、LSP、ISP、DIP |
22 | 模板方法模式 | Template Method Pattern | SRP、OCP、LSP、ISP、DIP |
23 | 访问者模式 | Visitor Pattern | SRP、OCP、LSP、ISP、DIP |
面向对象设计原则(OOD Principle)
面向对象设计原则(OODP)Object-Oriented Design Principle
- 面向对象设计原则是一组指导性准则,用于帮助软件开发人员设计和构建高质量、灵活、可维护和可扩展的面向对象软件系统。
- 这些原则强调了良好的面向对象设计实践,有助于避免常见的设计问题并提高软件系统的可靠性和可重用性。
1. 单一职责原则(SRP)
单一职责原则(SRP)Single Responsibility Principle
- 每个类或模块应该有且只有一个单一的责任。
- 换句话说,一个类应该只负责完成一个明确定义的功能。
- 这样做可以降低类的复杂性,提高类的可维护性和可重用性。
2. 开闭原则(OCP)
开闭原则(OCP)Open/Closed Principle
- 软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
- 这意味着当需要添加新功能时,应该通过扩展现有代码来实现,而不是修改现有代码。
- 这样可以保持现有功能的稳定性,并降低引入新功能时引入错误的风险。
3. 里氏替换原则(LSP)
里氏替换原则(LSP)Liskov Substitution Principle
- 子类应该能够替换其父类并出现在任何使用父类的地方,而不会影响程序的正确性。
- 这就要求子类必须保持父类的行为,即子类不能修改父类的行为。
4. 接口隔离原则(ISP)
接口隔离原则(ISP) Interface Segregation Principle
- 不应该强迫客户端依赖于它们不使用的接口。
- 接口应该被细化,只包含客户端需要的方法,这样可以降低类之间的耦合度,提高系统的灵活性和可维护性。
5. 依赖倒置原则(DIP)
依赖倒置原则(DIP)Dependency Inversion Principle
- 高层模块不应该依赖于低层模块,而是应该依赖于抽象。
- 抽象不应该依赖于具体实现,而是具体实现应该依赖于抽象。
- 这样可以降低模块之间的耦合度,提高代码的可测试性和可扩展性。
6. 迪米特法则(LOD)
迪米特法则(LoD)Law Of Demeter
- 也称为最少知识原则。
- 一个对象应该对其他对象有尽可能少的了解,只与直接的朋友进行交互。
- 这样可以降低类之间的耦合度,使系统更加灵活和易于维护。
7. 合成复用原则(CRP)
合成复用原则(CRP)Composite Reuse Principle
- 尽量使用组合/聚合的方式而不是继承关系达到软件复用的目的,是 has-a 关系,而不是通过继承。
- 该原则促使开发者在设计中更多地考虑对象之间的组合关系,以达到模块化、灵活性和可维护性的目标。
这些面向对象设计原则是设计模式的基础,它们指导着我们如何设计出更加健壮、灵活和可维护的面向对象软件系统。
遵循这些原则有助于提高代码的质量,减少重构的需求,并帮助我们构建出更好的软件。
创建型模式 (Creational Patterns)
创建型模式是设计模式中的一类,主要关注如何创建对象以及对象的实例化过程。它们提供了一种灵活、可复用的方式来创建对象,同时隐藏了对象创建的细节,从而降低了系统的耦合性。
这些创建型模式各自适用于不同的场景,可以根据具体需求来选择合适的设计模式来实现对象的创建和初始化。它们的共同目标是降低对象的创建和使用之间的耦合,提供更加灵活和可扩展的代码结构。
工厂方法模式(Factory Method Pattern)
在学习工厂方法模式之前建议先学习文章最后一章《必要拓展》中的简单工厂模式(Simple Factory Pattern)
1. 简单介绍
工厂方法模式(Factory Method Pattern)定义一个创建对象的接口,但由子类决定具体实例化哪个类。客户端只需要知道工厂接口,而不关心具体的产品类。
2. 实际问题
假设你是一家汽车制造公司,你有多个型号的汽车需要生产,每种型号的汽车有不同的配置和特性。你需要一个灵活的方式来生产不同型号的汽车,并且在将来能够轻松添加新的汽车型号。
3. 解决方案
- 定义一个产品接口,规范产品的行为。
- 创建一个工厂接口,包含一个工厂方法用于创建产品对象。
- 对每个具体产品,创建一个具体产品类,实现产品接口。
- 对每个具体产品,创建一个具体工厂类,实现工厂接口,负责创建具体产品的实例。
// 产品接口:汽车
interface Car {
void assemble();
}
// 具体产品:SUV 汽车
class SUVCar implements Car {
@Override
public void assemble() {
System.out.println("组装SUV汽车");
}
}
// 具体产品:轿车
class SedanCar implements Car {
@Override
public void assemble() {
System.out.println("组装轿车");
}
}
// 工厂接口:汽车工厂
interface CarFactory {
Car createCar();
}
// 具体工厂:SUV 汽车工厂
class SUVCarFactory implements CarFactory {
@Override
public Car createCar() {
return new SUVCar();
}
}
// 具体工厂:轿车工厂
class SedanCarFactory implements CarFactory {
@Override
public Car createCar() {
return new SedanCar();
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
System.out.println("===========SUV===========");
Car suvCar = new SUVCarFactory().createCar();
suvCar.assemble();
System.out.println("===========Sedan===========");
Car sedanCar = new SedanCarFactory().createCar();
sedanCar.assemble();
}
}
4. 类关系图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q3KXdmJK-1691793071648)(./assets/FactoryMethodPattern.png)]
5. 应用场景
- 当一个类无法预先知道它需要创建的对象的确切类时,使用工厂方法可以延迟对象的实例化,使得具体的实例化由子类来决定。
- 当需要在运行时动态地选择创建某个类的对象时,工厂方法提供了一种灵活的解决方案。
6. 易错点
- 忘记实现产品接口:每个具体产品都必须实现产品接口,否则无法通过工厂方法创建产品对象。
- 忘记实现工厂接口:每个具体工厂都必须实现工厂接口,以提供工厂方法来创建产品对象。
7. 优缺点
优点
- 通过工厂方法,将产品的创建与使用解耦,客户端不需要知道具体产品的类名,只需要通过工厂接口来创建对象,降低了耦合度。
- 具体产品的新增和变更对客户端代码没有影响,只需要新增或修改相应的具体工厂类即可。
缺点
- 每新增一个具体产品,都需要新增一个相应的具体工厂类,导致类的个数增加,增加了系统的复杂性。
8. 总结
工厂方法模式是一种创建型设计模式,通过将产品的创建与使用解耦,让子类决定实例化哪个类。这样可以提高代码的灵活性和可扩展性,使得系统的演化和维护更加容易。虽然增加了类的个数,但对于复杂系统的开发来说,这是值得的权衡。在需要动态地选择创建对象,或者有多个类似产品等级结构的情况下,工厂方法模式是一个很好的选择。
抽象工厂模式(Abstract Factory Pattern)
1. 简单介绍
抽象工厂模式(Abstract Factory Pattern)提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂可以创建多个不同类型的产品。
2. 实际问题
假设你是一家家具制造公司,你有多种类型的家具,如沙发、床、桌子等,每种类型的家具有不同的风格和材料。你需要一种灵活的方式来生产不同类型和风格的家具,并且在将来能够轻松添加新的家具类型和风格。
3. 解决方案
// 产品接口:沙发
interface Sofa {
void sitOn();
}
// 产品接口:床
interface Bed {
void sleepOn();
}
// 具体产品:现代风格沙发
class ModernSofa implements Sofa {
@Override
public void sitOn() {
System.out.println("坐在现代风格的沙发上");
}
}
// 具体产品:现代风格床
class ModernBed implements Bed {
@Override
public void sleepOn() {
System.out.println("睡在现代风格的床上");
}
}
// 具体产品:古典风格沙发
class ClassicalSofa implements Sofa {
@Override
public void sitOn() {
System.out.println("坐在古典风格的沙发上");
}
}
// 具体产品:古典风格床
class ClassicalBed implements Bed {
@Override
public void sleepOn() {
System.out.println("睡在古典风格的床上");
}
}
// 抽象工厂接口:家具工厂
interface FurnitureFactory {
Sofa createSofa();
Bed createBed();
}
// 具体工厂:现代风格家具工厂
class ModernFurnitureFactory implements FurnitureFactory {
@Override
public Sofa createSofa() {
return new ModernSofa();
}
@Override
public Bed createBed() {
return new ModernBed();
}
}
// 具体工厂:古典风格家具工厂
class ClassicalFurnitureFactory implements FurnitureFactory {
@Override
public Sofa createSofa() {
return new ClassicalSofa();
}
@Override
public Bed createBed() {
return new ClassicalBed();
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
System.out.println("===========现代家具===========");
ModernFurnitureFactory modernFurnitureFactory = new ModernFurnitureFactory();
Bed modernBed = modernFurnitureFactory.createBed();
modernBed.sleepOn();
Sofa modernSofa = modernFurnitureFactory.createSofa();
modernSofa.sitOn();
System.out.println("======古典家具========");
ClassicalFurnitureFactory classicalFurnitureFactory = new ClassicalFurnitureFactory();
Bed classicaBed = classicalFurnitureFactory.createBed();
classicaBed.sleepOn();
Sofa classicaSofa = classicalFurnitureFactory.createSofa();
classicaSofa.sitOn();
}
}
4. 类关系图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o80LAUXY-1691793071648)(./assets/AbstractFactoryPattern.png)]
5. 应用场景
- 当需要创建一组相关或相互依赖的对象时,可以使用抽象工厂模式。例如,创建不同风格的家具,或者创建不同主题的界面元素等。
- 当希望系统在未来能够支持新的产品种类或产品族,而不需要修改现有代码时,抽象工厂模式提供了一种扩展的解决方案。
6. 易错点
- 在添加新的产品族时,需要修改抽象工厂接口及其所有子类,可能导致较大的改动。
- 在添加新的产品等级结构时,需要修改抽象工厂接口及其所有子类,可能导致较大的改动。
7. 优缺点
优点
- 将一组相关的产品封装在一起,客户端使用抽象接口来创建产品,不需要关心具体的实现细节,降低了客户端和具体产品类之间的耦合。
- 可以轻松替换不同的工厂类来创建不同的产品族,增加新的产品族也方便,符合开闭原则。
缺点
- 增加新的产品族或产品等级结构时,需要修改抽象工厂接口及其所有子类,可能导致较大的改动。
- 当产品族和产品等级结构过于复杂时,抽象工厂模式的类的数量可能会增加,导致系统复杂性增加。
8. 总结
抽象工厂模式是一种创建型设计模式,通过提供一个接口来创建一组相关或相互依赖的对象,将一系列产品封装在一起,使得客户端不需要知道具体产品的类名,只需使用抽象接口即可。抽象工厂模式适用于创建一组相关的产品,且希望系统能够轻松支持新的产品族或产品等级结构的场景。在使用抽象工厂模式时,需要注意在添加新的产品族或产品等级结构时,可能需要修改较多的代码。
单例模式(Singleton Pattern)
1. 简单介绍
单例模式(Singleton Pattern)确保一个类只有一个实例,并提供一个全局访问点。这样可以控制对象的创建和访问,通常用于管理共享资源或全局配置。
2. 实际问题
在现实世界中,我们可能会遇到这样的问题:希望某个类在整个应用程序中只能拥有一个实例,不论在何处访问该类,始终获取到相同的唯一实例。
3. 解决方案
- 懒汉式单例
懒汉式是指在首次使用时才创建实例。解决方案为在类内部定义一个私有静态变量作为该类的唯一实例,并提供一个公共静态方法来获取该实例。
// 懒汉式单例
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
// 私有构造方法,防止其他类通过 new 创建实例
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 获取懒汉式单例实例
LazySingleton instance1 = LazySingleton.getInstance();
LazySingleton instance2 = LazySingleton.getInstance();
// 验证是否为同一实例
System.out.println(instance1 == instance2);
// Output: true
}
}
- 饿汉式单例
饿汉式是指在类加载时就创建实例。解决方案为在类定义时直接创建一个私有静态实例,并提供一个公共静态方法来获取该实例。
// 饿汉式单例
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
// 私有构造方法,防止其他类通过 new 创建实例
public static EagerSingleton getInstance() {
return instance;
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 获取饿汉式单例实例
EagerSingleton instance1 = EagerSingleton.getInstance();
EagerSingleton instance2 = EagerSingleton.getInstance();
// 验证是否为同一实例
System.out.println(instance1 == instance2);
// Output: true
}
}
- 双重校验锁单例
双重校验锁单例是一种在懒汉式基础上进行改进的解决方案,旨在减少不必要的同步开销。
// 双重校验锁单例
public class DoubleCheckedSingleton {
private volatile static DoubleCheckedSingleton instance;
private DoubleCheckedSingleton() {}
// 私有构造方法,防止其他类通过 new 创建实例
public static DoubleCheckedSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedSingleton();
}
}
}
return instance;
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 获取双重校验锁单例实例
DoubleCheckedSingleton instance1 = DoubleCheckedSingleton.getInstance();
DoubleCheckedSingleton instance2 = DoubleCheckedSingleton.getInstance();
// 验证是否为同一实例
System.out.println(instance1 == instance2);
// Output: true
}
}
- 枚举单例
枚举单例是一种简洁且线程安全的单例模式解决方案。在Java中,枚举类型是天然的单例,保证在任何情况下都只有一个实例。
// 枚举单例
public enum EnumSingleton {
INSTANCE;
// 可以添加其他方法和属性
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 获取枚举单例实例
EnumSingleton instance1 = EnumSingleton.INSTANCE;
EnumSingleton instance2 = EnumSingleton.INSTANCE;
// 验证是否为同一实例
System.out.println(instance1 == instance2);
// Output: true
}
}
在每个示例中,我们都通过相应的静态方法获取单例实例,并通过比较引用地址验证是否为同一实例。由于单例模式保证在整个应用程序中只有一个实例,所以输出结果都应该是
true
。
4. 类关系图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cIdeohu0-1691793071648)(./assets/SingletonPattern.png)]
5. 应用场景
- 需要在整个应用程序中共享某个资源,例如配置信息、数据库连接等。
- 控制资源的并发访问,避免资源冲突。
6. 易错点
- 线程安全问题:懒汉式和双重校验锁单例在多线程环境下可能会创建多个实例,需要采取同步措施保证线程安全,但同步会影响性能。
- 反序列化问题:如果单例类实现了Serializable接口,当对象被反序列化时,可能会创建新的实例,破坏单例特性,需要通过readResolve()方法解决。
7. 优缺点
懒汉式单例模式
-
优点
- 实现懒加载,只有在实例需要时才会创建。
- 简单易懂。
-
缺点
- 线程安全需要额外考虑,可能会影响性能。
- 反序列化可能会破坏单例特性。
饿汉式单例模式
-
优点
- 实现简单,不需要考虑线程安全问题。
- 线程安全,可以直接用于多线程环境。
-
缺点
- 不支持懒加载,可能导致资源浪费。
双重校验锁单例模式
-
优点
- 实现懒加载,只有在实例需要时才会创建。
- 在多线程环境下保持了较好的性能。
-
缺点
- 实现相对复杂,容易出错。
- JDK 1.5之前的版本中可能存在双重检查锁失效的问题。
枚举单例模式
-
优点
- 简洁且线程安全,天然保证只有一个实例。
- 支持其他方法和属性的定义。
-
缺点
- 不能实现懒加载,实例在类加载时就被创建。
8. 总结
懒汉式、饿汉式、枚举单例和双重校验锁单例是常见的单例模式解决方案。懒汉式适用于需要懒加载的场景,但需注意线程安全和反序列化问题。饿汉式适用于不需要懒加载的场景,且线程安全。枚举单例是一种简洁且线程安全的解决方案,但不能实现懒加载。双重校验锁单例在懒加载的基础上进行了改进,可以在多线程环境下保持较好的性能,但实现相对复杂。在选择解决方案时,需根据具体场景的需求和性能要求做出选择。
原型模式(Prototype Pattern)
1. 简单介绍
原型模式(Prototype Pattern)通过复制现有对象来创建新的对象,避免了使用常规构造函数来创建对象,从而提高性能和灵活性。
2. 实际问题
在现实世界中,我们可能会遇到这样的问题:希望创建一个对象的副本,并且可以根据原对象进行快速的复制和修改。
3. 解决方案
原型模式是一种创建型设计模式,它通过复制现有对象来创建新对象。Java中可以通过实现Cloneable接口和重写clone()方法来实现原型模式。
- 浅克隆
// 原型对象类
public class Prototype implements Cloneable {
private String data;
public Prototype(String data) {
this.data = data;
}
// 重写clone方法,实现浅拷贝
@Override
public Prototype clone() throws CloneNotSupportedException {
return (Prototype) super.clone();
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
// 调用代码
public class XZ {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建原型对象
Prototype originalPrototype = new Prototype("玄子Share");
// 克隆原型对象
Prototype clonedPrototype = originalPrototype.clone();
System.out.println("新对象克隆的数据: " + clonedPrototype.getData());
// 修改克隆对象的数据
clonedPrototype.setData("XuanZiShare");
System.out.println("获取修改后的数据: " + clonedPrototype.getData());
System.out.println("获取源对象的数据: " + originalPrototype.getData());
// 修改新对象数据不影响源对象
}
}
- 深克隆
4. 类关系图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EAThpScU-1691793071649)(./assets/PrototypePattern.png)]
5. 应用场景
- 需要创建复杂对象,但创建过程较为耗时。
- 需要创建大量相似对象,复制现有对象比重新创建更高效。
6. 易错点
浅拷贝问题:默认情况下,clone()方法执行的是浅拷贝,即只复制基本类型的字段和引用类型的地址,而不复制引用类型对象本身。如果对象中包含引用类型字段,需要注意处理深拷贝问题。
7. 优缺点
优点
- 提高对象的创建效率,避免重复执行初始化操作。
- 方便快速创建和修改对象副本。
缺点
- 如果对象的复制过程较为复杂,可能需要实现深拷贝。
- 在处理引用类型字段时需要特别注意,避免共享引用对象导致意外修改。
8. 总结
原型模式是一种创建型设计模式,通过复制现有对象来创建新对象。在Java中,可以通过实现Cloneable接口和重写clone()方法来实现原型模式。原型模式适用于需要创建复杂对象或大量相似对象的场景,可以提高对象的创建效率,并方便快速创建和修改对象副本。需要注意处理引用类型字段时的浅拷贝问题,并根据具体情况考虑是否需要实现深拷贝。
建造者模式(Builder Pattern)
1. 简单介绍
建造者模式(Builder Pattern)将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
2. 实际问题
在现实世界中,我们可能会遇到这样的问题:希望构建一个复杂的对象,该对象有多个可选属性,并且在创建过程中可以灵活组合这些属性。
3. 解决方案
建造者模式是一种创建型设计模式,它通过将对象的构建过程和表示分离,使得同样的构建过程可以创建不同的表示。建造者模式通常包含一个Builder接口和一个ConcreteBuilder实现类,以及一个Director类来指导构建过程。
- 建造者模式
// 建造者接口
public interface Builder {
void buildPart1();
void buildPart2();
void buildPart3();
Product getResult();
}
// 具体建造者
public class ConcreteBuilder implements Builder {
private final Product product = new Product();
public void buildPart1() {
product.setPart1("Part1");
}
public void buildPart2() {
product.setPart2("Part2");
}
public void buildPart3() {
product.setPart3("Part3");
}
public Product getResult() {
return product;
}
}
// 产品类
public class Product {
private String part1;
private String part2;
private String part3;
public String getPart1() {
return part1;
}
public void setPart1(String part1) {
this.part1 = part1;
}
public String getPart2() {
return part2;
}
public void setPart2(String part2) {
this.part2 = part2;
}
public String getPart3() {
return part3;
}
public void setPart3(String part3) {
this.part3 = part3;
}
@Override
public String toString() {
return "Product{" +
"part1='" + part1 + '\'' +
", part2='" + part2 + '\'' +
", part3='" + part3 + '\'' +
'}';
}
}
// 指导者类
public class Director {
public void construct(Builder builder) {
builder.buildPart1();
builder.buildPart2();
builder.buildPart3();
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 创建指导者和建造者对象
Director director = new Director();
Builder builder = new ConcreteBuilder();
// 指导建造过程并获取产品对象
director.construct(builder);
Product product = builder.getResult();
// 输出产品信息
System.out.println(product);
// Output: Product{part1='Part1', part2='Part2', part3='Part3'}
}
}
产品信息顺序固定
- 建造者模式(无指挥
Director
)
// 建造者接口
public interface Builder {
Builder buildPart1(String part);
Builder buildPart2(String part);
Builder buildPart3(String part);
Product getResult();
}
// 具体建造者
public class ConcreteBuilder implements Builder {
private final Product product = new Product();
public Builder buildPart1(String part) {
product.setPart1(part);
return this;
}
public Builder buildPart2(String part) {
product.setPart2(part);
return this;
}
public Builder buildPart3(String part) {
product.setPart3(part);
return this;
}
public Product getResult() {
return product;
}
}
// 产品类
public class Product {
private String part1 = "part1";
private String part2 = "part1";
private String part3 = "part1";
public String getPart1() {
return part1;
}
public void setPart1(String part1) {
this.part1 = part1;
}
public String getPart2() {
return part2;
}
public void setPart2(String part2) {
this.part2 = part2;
}
public String getPart3() {
return part3;
}
public void setPart3(String part3) {
this.part3 = part3;
}
@Override
public String toString() {
return "Product{" +
"part1='" + part1 + '\'' +
", part2='" + part2 + '\'' +
", part3='" + part3 + '\'' +
'}';
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 创建指导者和建造者对象
ConcreteBuilder concreteBuilder = new ConcreteBuilder();
Product product = concreteBuilder.getResult();
// 输出产品信息
System.out.println(product);
// Output: Product{part1='Part1', part2='Part2', part3='Part3'}
System.out.println("============================");
// 创建指导者和建造者对象
ConcreteBuilder concreteBuilder2 = new ConcreteBuilder();
// 链式编程:在原来的基础上,可自由组合,若不组合,则默认
Product product2 = concreteBuilder2.buildPart1("part2").buildPart2("part1").buildPart3("part3").getResult();
// 输出产品信息
System.out.print(product2);
// Output: Product{part1='part2', part2='part1', part3='part3'}
}
}
产品信息顺序可自由搭配
4. 类关系图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zpNk258S-1691793071649)(./assets/BuilderPatternDirector.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4EN9GsQi-1691793071649)(./assets/BuilderPatternChain.png)]
5. 应用场景
- 需要创建复杂对象,且对象的构建过程相对稳定,但表示可以灵活组合的场景。
- 当一个对象有多个可选属性,且属性之间有依赖关系时,可以使用建造者模式进行构建。
6. 易错点
- 需要根据实际需求定义建造者接口和具体建造者类,确保建造过程的正确性和灵活性。
- 在指导者类中指导建造过程,确保正确的建造顺序和步骤。
7. 优缺点
优点
- 将对象的构建过程和表示分离,使得构建过程可以创建不同的表示,提高了构建过程的灵活性。
- 可以隐藏产品的内部结构,使得客户端只关注产品的高层接口。
缺点
- 需要定义多个类,增加了代码复杂性。
- 不适用于只有少量可选属性的对象,过度使用建造者模式可能会导致代码冗余。
8. 总结
建造者模式是一种创建型设计模式,它通过将对象的构建过程和表示分离,使得同样的构建过程可以创建不同的表示。在Java中,可以通过定义建造者接口和具体建造者类来实现建造者模式,并通过指导者类指导建造过程。建造者模式适用于需要创建复杂对象,且对象的构建过程相对稳定但表示可以灵活组合的场景。需要注意定义建造者接口和具体建造者类,确保建造过程的正确性和灵活性。同时,建造者模式也应避免过度使用,以免造成代码冗余。
结构型模式 (Structural Patterns)
结构型模式(Structural Patterns)是设计模式中的一类,它主要关注对象和类的组合,以实现更大的结构,以及改变或简化类之间的交互方式。这些模式使得不同类和对象之间的关系更加灵活,同时也降低了系统的耦合度,使系统更易于维护和扩展。
结构型模式在软件开发中起到了重要的作用,它们帮助我们在设计阶段选择合适的模式,提高系统的设计质量和性能,并促进代码的重用和维护。不同的结构型模式适用于不同的场景和需求,通过合理使用结构型模式,可以让系统更加灵活、可扩展和易于理解。
适配器模式(Adapter Pattern)
桥接模式(Bridge Pattern)
组合模式(Composite Pattern)
装饰模式(Decorator Pattern)
外观模式(Facade Pattern)
享元模式(Flyweight Pattern)
代理模式(Proxy Pattern)
行为型模式 (Behavioral Patterns)
行为型模式(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)
1. 简单介绍
简单工厂模式(Simple Factory Pattern)虽然它不在 GoF 的列表中,但在实际开发中,简单工厂模式是一种经常被使用的设计模式。它简化了对象的创建过程,提高了代码的可维护性和扩展性。
2. 实际问题
假设你正在开发一个计算器应用程序,用户可以输入两个操作数和运算符,然后应用程序会根据输入的运算符进行相应的计算,并返回结果。不同的运算符对应不同的计算方法,例如加法、减法、乘法和除法等。你希望设计一个通用的计算器工厂,根据用户输入的运算符,生产出相应的计算器对象,从而实现不同运算的计算功能。
3. 实现方式
// 运算器接口
interface Operator {
double calculate(double operand1, double operand2);
}
// 具体运算器:加法运算器
class AdditionOperator implements Operator {
@Override
public double calculate(double operand1, double operand2) {
return operand1 + operand2;
}
}
// 具体运算器:减法运算器
class SubtractionOperator implements Operator {
@Override
public double calculate(double operand1, double operand2) {
return operand1 - operand2;
}
}
// 具体运算器:乘法运算器
class MultiplicationOperator implements Operator {
@Override
public double calculate(double operand1, double operand2) {
return operand1 * operand2;
}
}
// 具体运算器:除法运算器
class DivisionOperator implements Operator {
@Override
public double calculate(double operand1, double operand2) {
if (operand2 == 0) {
throw new IllegalArgumentException("除数不能为零");
}
return operand1 / operand2;
}
}
// 简单工厂类
class CalculatorFactory {
public static Operator createOperator(String operatorType) {
switch (operatorType) {
case "+":
return new AdditionOperator();
case "-":
return new SubtractionOperator();
case "*":
return new MultiplicationOperator();
case "/":
return new DivisionOperator();
default:
throw new IllegalArgumentException("不支持的运算符:" + operatorType);
}
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
Operator operator1 = CalculatorFactory.createOperator("+");
System.out.println(operator1.calculate(1, 2));
Operator operator2 = CalculatorFactory.createOperator("-");
System.out.println(operator2.calculate(1, 2));
Operator operator3 = CalculatorFactory.createOperator("*");
System.out.println(operator3.calculate(1, 2));
Operator operator4 = CalculatorFactory.createOperator("/");
System.out.println(operator4.calculate(1, 2));
// Operator operator5 = CalculatorFactory.createOperator("%");
// System.out.println(operator5.calculate(1, 2));
// 执行没有的 Operator 就会报错
}
}
在这个示例中,Operator
是运算器接口,定义了一个 calculate()
方法来进行运算。AdditionOperator
、SubtractionOperator
、MultiplicationOperator
和 DivisionOperator
是具体运算器类,分别表示加法运算器、减法运算器、乘法运算器和除法运算器,它们都实现了运算器接口的方法。CalculatorFactory
是简单工厂类,它通过 createOperator()
方法来根据用户输入的运算符类型,创建相应的运算器对象。
4. 类关系图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XIXD3ZDl-1691793071649)(./assets/SimpleFactoryPattern.png)]
5. 应用场景
- 当需要根据不同的条件创建不同类型的对象时,可以使用简单工厂模式。
- 简单工厂模式将对象的创建逻辑封装在工厂类中,使得客户端不需要知道具体的对象创建过程,只需要通过工厂类来获取对象。
6. 易错点
- 在实现简单工厂模式时,需要注意工厂类的职责,它应该负责根据条件创建相应的对象,而不应该承担过多的业务逻辑。
7. 优缺点
优点
- 将对象的创建逻辑封装在工厂类中,使得客户端不需要关心对象的具体创建过程,降低了客户端和具体对象的耦合。
- 简单工厂模式可以通过工厂类来统一管理对象的创建,便于代码的维护和管理。
缺点
- 如果需要添加新的运算器类型,需要修改工厂类的代码,这可能违反了开闭原则,导致工厂类的修改。
8. 总结
简单工厂模式是一种创建型设计模式,它通过一个工厂类来创建不同类型的对象,使得客户端不需要关心对象的具体创建过程。简单工厂模式适用于当需要根据不同的条件创建不同类型的对象时。在实现简单工厂模式时,需要注意工厂类的职责,它应该负责根据条件创建相应的对象,而不应该承担过多的业务逻辑。虽然简单工厂模式有一些优点,但也有一些缺点,例如如果需要添加新的对象类型,可能需要修改工厂类的代码,这可能违反了开闭原则。因此,在应用简单工厂模式时需要权衡考虑,选择合适的设计方案。
迪米特法则(LOD)
迪米特法则(Law Of Demeter,LOD),也被称为最少知识原则(Least Knowledge Principle,LKP),是面向对象设计的一个重要原则。它指导我们在设计类和模块时要尽量减少对象之间的耦合,让类之间的关系尽可能简单。迪米特法则的核心思想是一个对象应该对其他对象有尽可能少的了解,它只与其直接的朋友(成员变量、方法的输入参数、方法返回值)发生交互,而不与间接的朋友发生交互。
虽然迪米特法则在面向对象设计中是一个重要的原则,但并不是所有设计模式都明确地使用了它。有些设计模式确实能够很好地体现迪米特法则的思想,例如外观模式、中介者模式等,它们都能够通过封装和简化对象之间的交互,减少耦合。但并不是所有设计模式都显式地遵循迪米特法则。
有以下几个原因可能解释为什么不是所有设计模式都使用了迪米特法则:
- 设计模式的目的多样性:设计模式是为了解决不同的设计问题而产生的,每种设计模式都有其特定的目的和应用场景。并不是所有的设计问题都可以通过减少对象之间的交互来解决,因此不是所有设计模式都会涉及迪米特法则。
- 原则冲突:在实际的设计中,有时可能会存在多个设计原则之间的冲突,需要在不同的情况下进行权衡取舍。有时为了满足其他设计原则,可能会导致迪米特法则无法完全满足。
- 上下文依赖:某些设计模式可能需要多个对象之间的密切合作,这可能导致它们之间的交互较多,而迪米特法则要求尽量减少对象之间的交互。在这种情况下,可能需要权衡考虑,不一定能完全符合迪米特法则。
虽然不是所有设计模式都直接遵循迪米特法则,但迪米特法则仍然是一个有价值的原则,可以在设计中作为参考指导。在实际设计中,我们应该综合考虑各种设计原则和模式,以便得到合适的解决方案。每个设计模式都有其独特的用途和优势,我们应该根据具体的情况选择合适的设计模式来解决问题。
设计模式拓展
设计模式是一种用于解决特定问题的通用解决方案,它们是经过验证的、被广泛接受的最佳实践。除了常见的设计模式,还有一些拓展和衍生的设计模式,以及一些设计原则和编程技巧可以进一步优化和扩展设计模式的应用。
- 行为型设计模式的拓展:除了前面提到的行为型设计模式(策略模式、观察者模式、状态模式等),还有一些其他行为型设计模式,如备忘录模式、命令模式、解释器模式等。
- 创建型设计模式的拓展:创建型设计模式(工厂方法模式、抽象工厂模式、建造者模式等)也有一些拓展,如单例模式的各种实现方式(懒汉式、饿汉式、双重检查锁等)。
- 结构型设计模式的拓展:结构型设计模式(适配器模式、装饰器模式、代理模式等)也有一些相关的模式,如桥接模式、组合模式等。
- 并发设计模式:并发编程中有一些特定的设计模式,用于解决多线程和并发访问的问题,如锁、信号量、阻塞队列等。
- 函数式编程的设计模式:随着函数式编程的兴起,也出现了一些与函数式编程相关的设计模式,如柯里化、函数组合、惰性求值等。
- 架构模式:除了针对具体问题的设计模式,还有一些面向整个软件系统架构的设计模式,如分层架构、微服务架构等。
- 响应式编程模式:响应式编程是一种面向异步数据流和事件驱动的编程范式,它在处理事件和数据流时使用了一些特定的设计模式,如观察者模式和迭代器模式。
- 数据访问设计模式:用于解决数据访问层的设计问题,如数据访问对象模式(DAO)、仓储模式等。
这些拓展和衍生的设计模式都是在特定场景下为了解决特定问题而产生的,它们丰富了设计模式的应用范围,并提供了更多的解决方案供开发者选择。在实际应用中,我们需要根据具体的问题和需求选择合适的设计模式,或者结合多种设计模式来构建出更复杂、更灵活的系统。同时,也应该注意不要滥用设计模式,只有在合适的场景下使用合适的设计模式才能真正发挥它们的优势。文章来源:https://www.toymoban.com/news/detail-651632.html
玄子Share 设计模式 GOF 全23种 + 七大设计原则 8.2文章来源地址https://www.toymoban.com/news/detail-651632.html
到了这里,关于创建型模式 (Creational Patterns) 玄子Share 设计模式 GOF 全23种 + 七大设计原则的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!