Design Patterns - Overview of Design Patterns (Types of Design Patterns, Seven Principles of Design Patterns)

foreword 

Recently, I have re-learned design patterns, and summarized some commonly used design pattern related knowledge in the [Design Patterns] column , which is convenient for reviewing the past to learn new things in the future, and it is only for learning reference.

Related Links:

[Design Pattern] Column: [Design Pattern] Column

Related examples can be downloaded:  Examples of common design patterns in Java

1 Introduction to Design Patterns

         Design patterns  represent best practices and are usually adopted by experienced object-oriented software developers. Design patterns are solutions to general problems that software developers face during software development. These solutions are the result of trial and error by numerous software developers over a considerable period of time.

        A design pattern is a set of repeatedly used, well-known, cataloged, code design experience summaries. Design patterns are used to reuse code, make code easier to understand by others, and ensure code reliability. There is no doubt that design patterns are win-win for oneself, others, and the system. Design patterns make code compilation truly engineering. Design patterns are the cornerstone of software engineering, just like the bricks and stones of a building. Reasonable use of design patterns in the project can perfectly solve many problems. Each pattern has a corresponding principle to correspond to it in reality. Each pattern describes a problem that occurs repeatedly around us, and the problem. The core solution, which is why design patterns can be widely used.

2 Types of Design Patterns

2.1 Differentiate according to purpose

According to the mode is used to complete what work to divide, this method can be divided into the following three modes (as shown below):

 2.1.1 Creative model

It is used to describe "how to create objects" , and its main feature is "separation of object creation and use" .

name illustrate

singleton pattern

(Singleton)

A certain class can only generate one instance, which provides a global access point for external access to the instance, and its extension is the limited multiple instance pattern.

prototype pattern

(Prototype)

Using an object as a prototype, multiple new instances similar to the prototype are cloned by duplicating it.

factory pattern

(Factory)

Define an interface for creating products, and subclasses decide what products to produce.

abstract factory pattern

(Abstract Factory)

Provides an interface for creating product families, each of which can produce a series of related products.

builder mode

(Builder)

Decompose a complex object into several relatively simple parts, then create them separately according to different needs, and finally build the complex object.

2.1.2 Structural patterns

Used to describe how classes or objects are organized into larger structures in a certain layout.

name illustrate

adapter pattern

(Adapter)

Convert the interface of a class to another interface that the client wants, so that those classes that could not work together due to incompatible interfaces can work together.

bridge mode

(Bridge)

Separating abstraction from reality so that they can vary independently. It is implemented by replacing the inheritance relationship with the composition relationship, thereby reducing the coupling degree of the two variable dimensions of abstraction and implementation.

decorator pattern

(Decorator)

Dynamically add some responsibilities to the object, that is, add its additional functions.

appearance mode

(Facade)

Provide a consistent interface for multiple complex subsystems, making these subsystems more accessible.

Flyweight mode

(Flyweight)

Use sharing technology to effectively support the reuse of a large number of fine-grained objects.

combination mode

(Composite)

Combining objects into tree-like hierarchies gives users consistent access to individual and composite objects.

Proxy mode

(Proxy)

Provides a proxy for an object to control access to that object. That is, the client accesses the object indirectly through the proxy, thereby restricting, enhancing or modifying some characteristics of the object.

2.1.3 Behavioral patterns

It is used to describe how classes or objects cooperate with each other, jointly accomplish tasks that a single object cannot accomplish alone, and how to assign responsibilities.

name illustrate

chain of responsibility model

(Chain of Responsibility)

Pass a request from one object in the chain to the next until the request is responded to. This method removes the coupling between objects.

command mode

(Command)

Encapsulating a request as an object separates the responsibility for making the request from the responsibility for executing it.

interpreter mode

(Interpreter)

Provides how to define the grammar of the language, and the method of interpreting the language sentences, that is, the interpreter.

iterator pattern

(Iterator)

提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。

中介者模式

(Mediator)

定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。

备忘录模式

(Memento)

在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。

观察者模式

(Observer)

多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。

状态模式

(State)

允许一个对象在其内部状态发生改变时改变其行为能力。

策略模式

(Strategy)

定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。

模板方法模式

(Template Method)

定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

访问者模式

(Visitor)

在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。

2.2 根据作用范围来区分

根据主要用于类上还是主要用于对象上来分,这种方式可分为:

类模式:

用于处理类与子类之间的关系,这些关系通过继承来建立,是静态的,在编译时便确定下来了。

对象模式:

用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以变化的,更具动态性。

2.3 总览

3 设计模式七大原则

设计模式的七大原则包括:

  1. 单一职责原则(Single Responsibility Principle)

  2. 开闭原则(Open Close Principle)

  3. 里氏代换原则(Liskov Substitution Principle)

  4. 依赖倒转原则(Dependence Inversion Principle)

  5. 接口隔离原则(Interface Segregation Principle)

  6. 迪米特法则(Demeter Principle)

  7. 合成复用原则(Composite Reuse Principle)

3.1 单一职责原则(Single Responsibility Principle)

一个类只干一件事,实现类要单一

目的:

便于理解,提高代码的可读性

单一职责原则(Single Responsibility Principle,SRP)又称单一功能原则,由罗伯特·C.马丁(Robert C. Martin)于《敏捷软件开发:原则、模式和实践》一书中提出的。这里的职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分(There should never be more than one reason for a class to change)。

该原则提出对象不应该承担太多职责,如果一个对象承担了太多的职责,至少存在以下两个缺点:

  1. 一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力;

  2. 当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。

优点

单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有以下优点。

  • 降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多。

  • 提高类的可读性。复杂性降低,自然其可读性会提高。

  • 提高系统的可维护性。可读性提高,那自然更容易维护了。

  • 变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响。

3.2 开闭原则(Open Close Principle)

对扩展开放,对修改关闭

目的:

降低维护带来的新风险

开闭原则(Open Closed Principle,OCP),由勃兰特·梅耶(Bertrand Meyer)提出,他在 1988 年的著作《面向对象软件构造》(Object Oriented Software Construction)中提出:软件实体应当对扩展开放,对修改关闭(Software entities should be open for extension,but closed for modification),这就是开闭原则的经典定义。

即:

当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。

开闭原则的作用

开闭原则是面向对象程序设计的终极目标,它使软件实体拥有一定的适应性和灵活性的同时具备稳定性和延续性。具体来说,其作用如下。

1. 对软件测试的影响

软件遵守开闭原则的话,软件测试时只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍然能够正常运行。

2. 可以提高代码的可复用性

粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性。

3. 可以提高软件的可维护性

遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护。

3.3 里氏替换原则(Liskov Substitution Principle)

不要破坏继承体系,子类重写方法功能发生改变,不应该影响父类方法的含义

目的:

防止继承泛滥

里氏替换原则(Liskov Substitution Principle,LSP),由麻省理工学院计算机科学实验室的里斯科夫(Liskov)女士在 1987 年的“面向对象技术的高峰会议”(OOPSLA)上发表的一篇文章《数据抽象和层次》(Data Abstraction and Hierarchy)里提出来的,她提出:继承必须确保超类所拥有的性质在子类中仍然成立(Inheritance should ensure that any property proved about supertype objects also holds for subtype objects)。

里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。

里氏替换原则的作用

里氏替换原则的主要作用如下。:

  1. 里氏替换原则是实现开闭原则的重要方式之一。

  2. 它克服了继承中重写父类造成的可复用性变差的缺点。

  3. 它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。

  4. 加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性、可扩展性,降低需求变更时引入的风险。

里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。

3.4 依赖倒置原则(Dependence Inversion Principle)

高层不应该依赖低层,要面向接口编程

目的:

更利于代码结构的升级扩展

依赖倒置原则(Dependence Inversion Principle,DIP)是 Object Mentor 公司总裁罗伯特·马丁(Robert C.Martin)于1996 年在 C++Report 上发表的文章。

依赖倒置原则的原始定义为:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象(High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions)。其核心思想是:要面向接口编程,不要面向实现编程。

依赖倒置原则是实现开闭原则的重要途径之一,它降低了客户与实现模块之间的耦合。

依赖倒置原则的作用

依赖倒置原则的主要作用如下:

  • 依赖倒置原则可以降低类间的耦合性

  • 依赖倒置原则可以提高系统的稳定性

  • 依赖倒置原则可以减少并行开发引起的风险

  • 依赖倒置原则可以提高代码的可读性和可维护性

3.5 接口隔离原则(Interface Segregation Principle)

一个接口只干一件事,接口要精简单一

目的:

功能解耦,低耦合、高内聚

接口隔离原则(Interface Segregation Principle,ISP)要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。

接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:

  • 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。

  • 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

接口隔离原则的优点

接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下 5 个优点。

  1. 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性

  2. 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。

  3. 如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。

  4. 使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。

  5. 能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。

接口隔离原则实现的方法

在具体应用接口隔离原则时,应该根据以下几个规则来衡量。

  • 接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。

  • 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。

  • 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑。

  • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

3.6 迪米特法则(Demeter Principle)

不该知道的不要知道,一个类应该保持对其它对象最少的了解,降低耦合度

目的:

只和朋友交流,不和陌生人说话,减少代码臃肿

迪米特法则(Law of Demeter,LoD)又叫作最少知识原则(Least Knowledge Principle,LKP),产生于 1987 年美国东北大学(Northeastern University)的一个名为迪米特(Demeter)的研究项目,由伊恩·荷兰(Ian Holland)提出,被 UML 创始者之一的布奇(Booch)普及,后来又因为在经典著作《程序员修炼之道》(The Pragmatic Programmer)提及而广为人知。

迪米特法则的定义是:只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。

迪米特原则的优点

迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点。

  1. 降低了类之间的耦合度,提高了模块的相对独立性。

  2. 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。

但是,过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

迪米特原则实现方法

从迪米特法则的定义和特点可知,它强调以下两点:

  1. 从依赖者的角度来说,只依赖应该依赖的对象。

  2. 从被依赖者的角度说,只暴露应该暴露的方法。

所以,在运用迪米特法则时要注意以下 6 点。

  1. 在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。

  2. 在类的结构设计上,尽量降低类成员的访问权限。

  3. 在类的设计上,优先考虑将一个类设置成不变类。

  4. 在对其他类的引用上,将引用其他对象的次数降到最低。

  5. 不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。

  6. 谨慎使用序列化(Serializable)功能。

3.7 合成复用原则(Composite Reuse Principle)

尽量使用组合或者聚合关系实现代码复用,少使用继承

目的:

降低代码耦合

合成复用原则(Composite Reuse Principle,CRP)又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。

合成复用原则的重要性

通常类的复用分为继承复用合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点

  1. 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。

  2. 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。

  3. 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

When using combination or aggregate reuse , existing objects can be incorporated into new objects to make them part of new objects, and new objects can invoke functions of existing objects, which has the following advantages .

  1. It maintains the encapsulation of the class. Because the internal details of the constituent objects are invisible to the new objects, this type of reuse is also known as "black box" reuse.

  2. Low coupling between old and new classes. This reuse requires fewer dependencies, and the only way for new objects to access constituent objects is through the constituent object's interface.

  3. The flexibility of reuse is high. This reuse can be done dynamically at runtime, and new objects can dynamically reference objects of the same type as the constituent objects.

conclusion

If the above content is incorrect or needs to be supplemented, please ask for more advice, and it will be updated in time~

Comments are welcome~ Thank you for your praise~

Guess you like

Origin blog.csdn.net/pjymyself/article/details/121921417