深入浅出分析设计模式的优点与缺点

一、简介 1 设计模式定义 设计模式是一组有用的解决方案,用于解决特定类型的软件设计问题。它们通常提供了一种抽象出来的方式,来表达应用程序中常见问题的解决方案

一、简介

1.设计模式定义

设计模式是一组有用的解决方案,用于解决特定类型的软件设计问题。它们通常提供了一种抽象出来的方式,来表达应用程序中常见问题的解决方案,从而帮助开发者更有效地解决问题。设计模式的用途是帮助开发者解决软件设计问题,提高开发效率,降低开发成本,提高代码质量和可维护性,以及更好地管理和理解复杂的系统。设计模式的优点是可以提高代码可维护性,减少代码重复,提高开发效率,降低开发成本,提高代码质量和可维护性,以及更好地管理和理解复杂的系统。设计模式的缺点是可能会使代码变得复杂,也可能会过度设计。设计模式的出处是由GoF(Gang of Four)在1995年发表的著作“设计模式:可复用面向对象软件的基础”中提出。

2.设计模式的历史

设计模式可以追溯到1960年代,当时著名的软件工程师和设计师们开始研究如何提高软件开发的效率和可维护性。他们发现,应用一些重复出现的设计模式可以提高软件开发的效率,并极大地改善软件的可读性和可维护性。这些设计模式在1970年代末被称为“永恒的设计模式”,并在1995年由Erich Gamma,Richard Helm, Ralph Johnson和John Vlissides出版。他们的书,称为“设计模式:可复用面向对象软件的基础”,成为设计模式的代表作品,并且引发了关于设计模式的热烈讨论。此外,这本书还为软件开发人员提供了一套可复用的设计模式,以帮助他们更好地理解和实施设计模式

 

二、设计模式的六大原则

总原则:开闭原则(Open Close Principle)

实体应该对扩展开放,对修改关闭,即软件实体应尽量在不修改原有代码的情况下进行扩展。

应用时注意点是要充分利用抽象类和接口、多态等特性,将可变部分封装起来,提高系统的灵活性和可维护性。

1、单一职责原则

一个类应该只负责一项职责,不要存在多于一个的非本职责原因引起类的变更。简单来说就是类的职责要单一。

应用时注意点是要在设计时尽量把类的职责划分清楚,把功能模块化,提高系统的灵活性和可维护性。

 

2、里氏替换原则(Liskov Substitution Principle)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

历史替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。

 应用时注意点是在设计时应让父类和子类之间有一个共同的抽象层,以便子类可以重写父类的抽象方法,并在子类中实现这些抽象方法。

3、依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,具体内容:面向接口编程,依赖于抽象而不依赖于具体。

 应用时注意点是要在设计时以抽象为基础,尽量将细节层次降低,使系统可以更灵活地应对变化。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。

4、接口隔离原则(Interface Segregation Principle)

每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。简单地说使用多个专门的接口,而不使用单一的总接口。

应用时注意点是要把接口进行分解,使用多个小接口,以便更好地实现功能的模块化管理,提高系统的灵活性和可维护性。

 

5、迪米特法则(最少知道原则)(Demeter Principle)

就是说:一个类对自己依赖的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。

最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。

 应用时注意点是要尽量减少类与类之间的耦合,提高系统的灵活性和可维护性。

6、合成复用原则(Composite Reuse Principle)

指尽量使用组合/聚合的方式,而不是使用继承关系来达到复用的目的。

应用时注意点是要尽量使用组合/聚合的方式,使用继承可能带来的耦合性太强,从而影响系统的灵活性和可维护性。

 

二、设计模式的分类

总体来说设计模式分为三大类:

创建型设计模式,共五种:工厂方法设计模式、抽象工厂设计模式、单例设计模式、建造者设计模式、原型设计模式。

结构型设计模式,共七种:适配器设计模式、装饰器设计模式、代理设计模式、外观设计模式、桥接设计模式、组合设计模式、享元设计模式。

行为型设计模式,共十一种:策略设计模式、模板方法设计模式、观察者设计模式、迭代子设计模式、责任链设计模式、命令设计模式、备忘录设计模式、状态设计模式、访问者设计模式、中介者设计模式、解释器设计模式。

 

1. 工厂方法设计模式(Factory Method Pattern)

定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。

它的思想是:在工厂设计模式中,我们创建对象不是直接使用 new 关键字创建,而是将创建对象的行为委托给另一个类,也就是工厂类。把实例化的步骤抽象出来,让子类决定实例化哪一个类。
应用场景:当一个类不知道它所必须创建的对象的类的时候,或者根据不同条件创建不同实例时,可以使用工厂设计模式。

 2. 抽象工厂设计模式(Abstract Factory Pattern)

抽象工厂设计模式是一种创建型设计模式,它提供一种方法创建一组相关的产品,而无需指定它们的具体类。

优点:具体产品类的实现细节完全隐藏,不需要关心创建细节;增加新的具体产品类只需要添加一个新的具体产品类和相应的具体工厂类,不会影响已有系统;符合“开放-封闭”原则。

缺点:增加新的产品类比较困难,需要修改抽象工厂和所有的具体工厂类。

3.单例设计模式(Singleton Pattern)

单例设计模式是一种常用的软件设计模式,指的是在一个类里面有一个私有构造函数和一个公开静态方法,这个方法返回对象的唯一实例。

单例设计模式的应用场景是:需要一个只能创建一个实例的类,如 配置类、日志类、计数器类等。

优点:降低内存开销;确保全局唯一;可以控制实例数量。

缺点:不支持继承;不灵活;与单一职责原则冲突。

 

4.建造者设计模式(Builder Pattern)

建造者设计模式是一种创建型设计模式,它使用抽象的建造者来构建一个复杂的对象。

它的思想是:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

建造者设计模式的应用场景是:当创建一个复杂的对象时,可以使用建造者设计模式来将复杂对象的创建过程与它的表示进行分离,从而使得构建过程更加清晰。

优点:构建和表示分离;良好的封装性;可以构造复杂的对象。 

缺点:难以控制产品的组成部分;产品的组成部分太多,可能会出现很多具体的建造者类。

 

5.原型设计模式 

原型设计模式是一种创建型设计模式,它使用一个原型对象来创建新的对象。

它的思想是:通过复制一个已经存在的实例来创建新的实例,而不是通过new关键字来创建新的实例。

原型设计模式的应用场景是:当类的创建开销比较大或者构造函数中需要复杂操作时,原型设计模式可以派上用场。

优点:性能优良;简化创建过程;灵活性高。 

缺点:配备克隆方法需要对类的功能进行较大的改造,这是非常有风险的改动;必须实现Cloneable接口;使用原型设计模式时,不能使用final类型。

 

6.适配器设计模式(Adapter Pattern)

适配器设计模式是一种结构型设计模式,它将一个类的接口转换成客户希望的另一个接口。

它的思想是:将一个类的接口转换成客户希望的另一个接口,从而使原本接口不兼容的类可以一起工作。

适配器设计模式的应用场景是:当你希望使用一个已经存在的类,但是它的接口与你的要求不匹配时,可以使用适配器设计模式来解决这个问题。

 优点:提高了类的复用性;增加了类的透明度;灵活性高。

缺点:增加了系统的复杂度;增加系统的理解和设计难度。

 

7.装饰者设计模式(Decorator Pattern) 

装饰者设计模式是一种结构型设计模式,它可以在不改变一个对象的结构的情况下,给对象增加一些新的功能。

它的思想是:将一个类的功能进行加强,但是不改变它的结构。

装饰者设计模式的应用场景是:当你希望给一个类增加新的功能,而且不希望改变它的结构时,可以使用装饰者设计模式。

 

8.外观设计模式(Facade Pattern)

外观设计模式是一种结构型设计模式,它可以为复杂的子系统提供简单的接口,以便客户端可以方便地访问子系统。

它的思想是为复杂的子系统提供简单的接口,以便客户端可以方便地访问子系统。它的应用了依赖倒置原则。

应用场景是需要为复杂的子系统提供简单接口的场景。

 

9.桥接设计模式(Bridge Pattern)

桥接设计模式是一种结构型设计模式,它可以将抽象和实现解耦,从而让它们可以独立变化。它应用了接口隔离原则。

应用场景是需要将抽象和实现解耦的场景。

 

10.代理设计模式(Proxy Pattern)

代理设计模式是一种结构型设计模式,它可以为另一个对象提供一个替身或代理,以控制对这个对象的访问。它应用了迪米特法则。

应用场景是需要为另一个对象提供一个替身或代理的场景。

 

11.组合设计模式(Composite Pattern)

组合设计模式是一种结构型设计模式,它可以将对象组合成树形结构,从而使客户端可以使用统一的接口来处理单个对象和组合对象。它的原则是合成复用原则。

应用场景是需要将对象组合成树形结构的场景。

 

12.责任链设计模式(Chain of Responsibility Pattern)

它是一种行为设计模式,它使得请求发送者可以不需要知道处理者是谁,将请求发送给一系列的处理者,最终有处理者处理它。
它的原则是尽量减少请求发送者和处理者之间的耦合,有助于提高系统的灵活性和可扩展性。
 
 

13.命令设计模式(Command Pattern)

命令设计模式是一种行为设计模式,它将请求封装成一个对象,以便使用不同的请求、队列或者日志来参数化其他对象。
它的原则是将请求从它的发出者和执行者分离开来,以便实现低耦合、高内聚的系统结构。
 
 

14.解释器设计模式(Interpreter Pattern)

解释器设计模式是一种行为设计模式,它使用语法规则来解释使用者输入的数据,从而实现程序的功能。
它的原则是通过使用简单的语法规则来解释数据,以便更容易地实现程序功能。
 

15.迭代器设计模式(Iterator Pattern)

迭代器设计模式是一种行为设计模式,它使用迭代器对象来遍历容器,从而实现对容器中的元素的遍历。
它的原则是,将容器中的元素与其遍历操作分离开来,以便实现低耦合、高内聚的系统结构。
 
 

16.中介者设计模式(Mediator Pattern)

中介者设计模式是一种行为设计模式,它使用中介者对象来封装一组对象之间的交互,从而使这些对象之间的耦合度降低,提高系统的灵活性和可扩展性。它的原则是通过使用中介者对象来封装对象之间的交互,以便降低耦合度和提高系统的可扩展性。
 
 

17.观察者设计模式(Observer Pattern)

观察者设计模式是一种行为设计模式,它使用观察者对象来观察主题对象,以便当主题对象发生变化时,观察者对象可以获得通知。
它的原则是通过使用观察者对象来观察主题对象,以便当主题对象发生变化时,观察者对象可以获得通知。
 
 

18.状态设计模式(State Pattern)

态设计模式是一种行为设计模式,它使用状态对象来封装与状态有关的行为,以便使用不同的状态实现不同的行为。
它的原则是通过使用状态对象来封装与状态有关的行为,以便使用不同的状态实现不同的行为。
 

19.策略设计模式(Strategy Pattern)

策略设计模式是一种行为设计模式,它使用策略对象来封装可以互换的算法,以便使用不同的算法来解决不同的问题。
它的原则是通过使用策略对象来封装可以互换的算法,以便使用不同的算法来解决不同的问题。
 
 

20.模板方法设计模式(Template Pattern)

模板方法设计模式是一种行为设计模式,它使用模板方法来定义一个算法的基本结构,以便将一些具体的步骤延迟到子类中实现。它的原则是通过使用模板方法来定义算法的基本结构,以便将一些具体的步骤延迟到子类中实现。
 
 

21.备忘录设计模式(Memento Pattern)

备忘录设计模式是一种行为设计模式,它使用备忘录对象来存储对象的内部状态,以便在需要的时候对对象进行恢复。
它的原则是通过使用备忘录对象来存储对象的内部状态,以便在需要的时候对对象进行恢复。
 
 

22.访问者设计模式(Visitor Pattern)

访问者设计模式是一种行为设计模式,它使用访问者对象来访问某个对象,以便在不改变它的结构的情况下添加新的操作。
它的原则是通过使用访问者对象来访问某个对象,以便在不改变它的结构的情况下添加新的操作。
 
 

23.享元设计模式(Flyweight Pattern)

享元设计模式是一种用于性能优化的软件设计模式,通过共享对象来有效地支持大量细粒度的对象。
原理:享元设计模式通过共享技术实现相同或相似对象的重用。它通过为相似对象分配共享内存来节省内存,从而改善应用程序的性能。
优点:
1.可以极大提高应用程序的性能,提升应用程序的运行效率; 
2.可以减少内存的使用量,提升应用程序的可扩展性; 
3.可以提高应用程序的可维护性,减少对象的创建次数。
缺点:
1.实现享元设计模式需要分离出内部状态和外部状态,而识别内部状态需要充分的分析和编程实现; 
2.在享元设计模式中,需要关联外部状态,这将增加复杂度; 
3.使用享元设计模式会增加系统的维护复杂度,需要分离出内部状态和外部状态,而识别内部状态需要充分的分析和编程实现。