说明:本文内容非原创,摘自网友博文以及wiki内容,回顾升华相关知识点
设计重器-设计模式道以明向,法以立本,术以立策,势以立人,器以成事
Christopher Alexander 说过:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。
模式描述:
在一定环境中解决某一问题的方案,包括三个基本元素--问题,解决方案和环境。一言概之,就是固化惰性的或不易变的,抽象异变的,以期形成可复用的软件构件,同时具备较强的适用性,隔离并控制复杂度等。模式给予了一些通用问题的通用解,不同语言有细微的实现差异;但是,总体思想一致。
编码规范:每种开发语言,都有一些公认的编码规范,同一个开发团队,可统一编码规范,有效降低代码维护成本;可有效规避一些开发语言中的“坑”,提升编码质量等等。
惯用法:针对某种开发语言,都会有一些特定的习惯用法;而模式一般具备通用性,当然针对特定语言,也衍生了一些特定的模式。
设计模式分类设计模式分为:创建模式 结构模式 行为模式三种大类:创建型设计模式主要解决“对象的创建”问题;结构型设计模式主要解决“类或对象的组合”问题;行为型设计模式主要解决的就是“类或对象之间的交互”问题。
创建模式:对类的实例化过程的抽象。一些系统在创建对象时,需要动态地决定怎样创建对象,创建哪些对象,以及如何组合和表示这些对象。创建模式描述了怎样构造和封装这些动态的决定。包含类的创建模式和对象的创建模式。抽象创建过程。
结构模式:描述如何将类或对象结合在一起形成更大的结构。分为类的结构模式和对象的结构模式。类的结构模式使用继承把类,接口等组合在一起,以形成更大的结构。类的结构模式是静态的。对象的结构模式描述怎样把各种不同类型的对象组合在一起,以实现新的功能的方法。对象的结构模式是动态的。抽象结构。
行为模式:对在不同的对象之间划分责任和算法的抽象化。不仅仅是关于类和对象的,并是关于他们之间的相互作用。类的行为模式使用继承关系在几个类之间分配行为。对象的行为模式则使用对象的聚合来分配行为。抽象行为。
设计模式六大原则:1.单一职责原则(SRP):一个类只负责一个功能区域,应该仅有一个引起它变化的原因。
个人理解:单一职责原则就是要去实现高内聚、低耦合,一个类不应该承担太多的功能。开发者需要将一个功能领域中所有的功能写入类当中而不能将其他的功能写在里面,比如:一个连接数据库的工具类当中不能有其它和数据库连接不相关的工具方法。
2.开放封闭原则(ASD):类、模块、函数等应该是可以拓展的,但是不可修改,类对扩展开放,对修该关闭。
个人理解:当需求变化的时候,一个类不能满足目前的程序要求,这个时候我们需要做的不是修该代码来满足新的需求,而是在原有的模板当中增加新的功能模块来满足需求。
3.里氏替换原则(LSP):所有引用基类(父类)的地方必须能透明地使用其子类的对象。
个人理解:在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。
4.依赖倒置原则(DIP):高层模块不应该依赖低层模块,两个都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象,要针对接口编程,而不是针对实现编程。
个人理解:spring里面的面向切面编程。
5.迪米特原则(LOD):一个软件实体应当尽可能少地与其他实体发生相互作用。
个人理解:这样做是为了防止代码之间的耦合。
6.接口隔离原则(ISP):一个类对另一个类的依赖应该建立在最小的接口上。
个人理解:每一个接口只应该归纳一类功能差不多的方法而不应该有各种各样的方法。我们在编写代码的时候为了方便喜欢将一个类所需要实现的所有方法都写在该类实现的一个接口里面,这样做是不对的,可以将该类所实现的方法进行分类写在不同的接口当中,然后这个类可以去实现多个接口。
设计模式汇总:
序号 |
类型 |
英文名称 |
中文名称 |
定义 |
1 |
创建型 |
Singleton |
单件 |
保证一个类只有一个实例,并提供一个访问它的全局访问点。 |
2 |
创建型 |
Abstract Factory |
抽象工厂 |
提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类。 |
3 |
创建型 |
Factory Method |
工厂方法 |
定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到了子类。 |
4 |
创建型 |
Prototype |
原型 |
用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。 |
5 |
创建型 |
Builder |
生成器 |
将一个复杂对象的构建与他的表示相分离,使得同样的构建过程可以创建不同的表示。 |
6 |
结构型 |
Composite |
组合模式 |
将对象组合成树形结构以表示部分整体的关系,Composite使得用户对单个对象和组合对象的使用具有一致性。 |
7 |
结构型 |
FAÇADE |
外观 |
为子系统中的一组接口提供一致的界面,facade提供了一高层接口,这个接口使得子系统更容易使用。 |
8 |
结构型 |
Proxy |
代理 |
为其他对象提供一种代理以控制对这个对象的访问 |
9 |
结构型 |
Adapter |
适配器 |
将一类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作那些类可以一起工作。 |
10 |
结构型 |
Decorator |
装饰 |
动态地给一个对象增加一些额外的职责,就增加的功能来说,Decorator模式相比生成子类更加灵活。 |
11 |
结构型 |
Bridge |
桥接 |
将抽象部分与它的实现部分相分离,使他们可以独立的变化。 |
12 |
结构型 |
Flyweight |
享元 |
享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。 |
13 |
行为型 |
Iterator |
迭代器 |
提供一个方法顺序访问一个聚合对象的各个元素,而又不需要暴露该对象的内部表示。 |
14 |
行为型 |
Observer |
观察者 |
定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知自动更新。 |
15 |
行为型 |
Template Method |
模板方法 |
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,Template Method使得子类可以不改变一个算法的结构即可以重定义该算法得某些特定步骤。 |
16 |
行为型 |
Command |
命令 |
将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队和记录请求日志,以及支持可撤销的操作。 |
17 |
行为型 |
State |
状态 |
允许对象在其内部状态改变时改变他的行为。对象看起来似乎改变了他的类。 |
18 |
行为型 |
Strategy |
策略模式 |
定义一系列的算法,把他们一个个封装起来,并使他们可以互相替换,本模式使得算法可以独立于使用它们的客户。 |
19 |
行为型 |
Chain of Responsibility |
职责链 |
使多个对象都有机会处理请求,从而避免请求的送发者和接收者之间的耦合关系 |
20 |
行为型 |
Mediator |
中介者 |
用一个中介对象封装一些列的对象交互。 |
21 |
行为型 |
Visitor |
访问者模式 |
表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这个元素的新操作。 |
22 |
行为型 |
Interpreter |
解释器 |
给定一个语言,定义他的文法的一个表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 |
23 |
行为型 |
Memento |
备忘录 |
在不破坏对象的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 |
设计模式
创建型模式一 : 单例模式(Singleton)单例模式 :Singleton的作用是保证在应用程序中,一个类Class只有一个实例存在。并提供全局访问。
二:抽象工厂模式(Abstract Factory)
抽象工厂模式: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。在正常使用中,客户端软件创建抽象工厂的具体实现,然后使用工厂的通用接口创建作为主题一部分的具体对象。 客户端不知道(或关心)它从每个内部工厂获取哪些具体对象,因为它只使用其产品的通用接口。 这种模式将一组对象的实现细节与其一般用法分开,并依赖于对象组合,因为对象创建是在工厂接口中公开的方法中实现的。
UML类图以及对象交互图如下
/*** Each distinct product of a product family should have a base interface. All* variants of the product must implement this interface.*/class AbstractProductA {public:virtual ~AbstractProductA(){};virtual std::string UsefulFunctionA() const = 0;};/*** Concrete Products are created by corresponding Concrete Factories.*/class ConcreteProductA1 : public AbstractProductA {public:std::string UsefulFunctionA() const override {return "The result of the product A1.";}};class ConcreteProductA2 : public AbstractProductA {std::string UsefulFunctionA() const override {return "The result of the product A2.";}};/*** Here's the the base interface of another product. All products can interact* with each other, but proper interaction is possible only between products of* the same concrete variant.*/class AbstractProductB {/*** Product B is able to do its own thing...*/public:virtual ~AbstractProductB(){};virtual std::string UsefulFunctionB() const = 0;/*** ...but it also can collaborate with the ProductA.** The Abstract Factory makes sure that all products it creates are of the* same variant and thus, compatible.*/virtual std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const = 0;};/*** Concrete Products are created by corresponding Concrete Factories.*/class ConcreteProductB1 : public AbstractProductB {public:std::string UsefulFunctionB() const override {return "The result of the product B1.";}/*** The variant, Product B1, is only able to work correctly with the variant,* Product A1. Nevertheless, it accepts any instance of AbstractProductA as an* argument.*/std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const override {const std::string result = collaborator.UsefulFunctionA();return "The result of the B1 collaborating with ( " result " )";}};class ConcreteProductB2 : public AbstractProductB {public:std::string UsefulFunctionB() const override {return "The result of the product B2.";}/*** The variant, Product B2, is only able to work correctly with the variant,* Product A2. Nevertheless, it accepts any instance of AbstractProductA as an* argument.*/std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const override {const std::string result = collaborator.UsefulFunctionA();return "The result of the B2 collaborating with ( " result " )";}};/*** The Abstract Factory interface declares a set of methods that return* different abstract products. These products are called a family and are* related by a high-level theme or concept. Products of one family are usually* able to collaborate among themselves. A family of products may have several* variants, but the products of one variant are incompatible with products of* another.*/class AbstractFactory {public:virtual AbstractProductA *CreateProductA() const = 0;virtual AbstractProductB *CreateProductB() const = 0;};/*** Concrete Factories produce a family of products that belong to a single* variant. The factory guarantees that resulting products are compatible. Note* that signatures of the Concrete Factory's methods return an abstract product,* while inside the method a concrete product is instantiated.*/class ConcreteFactory1 : public AbstractFactory {public:AbstractProductA *CreateProductA() const override {return new ConcreteProductA1();}AbstractProductB *CreateProductB() const override {return new ConcreteProductB1();}};/*** Each Concrete Factory has a corresponding product variant.*/class ConcreteFactory2 : public AbstractFactory {public:AbstractProductA *CreateProductA() const override {return new ConcreteProductA2();}AbstractProductB *CreateProductB() const override {return new ConcreteProductB2();}};/*** The client code works with factories and products only through abstract* types: AbstractFactory and AbstractProduct. This lets you pass any factory or* product subclass to the client code without breaking it.*/void ClientCode(const AbstractFactory &factory) {const AbstractProductA *product_a = factory.CreateProductA();const AbstractProductB *product_b = factory.CreateProductB();std::cout << product_b->UsefulFunctionB() << "\n";std::cout << product_b->AnotherUsefulFunctionB(*product_a) << "\n";delete product_a;delete product_b;}int main() {std::cout << "Client: Testing client code with the first factory type:\n";ConcreteFactory1 *f1 = new ConcreteFactory1();ClientCode(*f1);delete f1;std::cout << std::endl;std::cout << "Client: Testing the same client code with the second factory type:\n";ConcreteFactory2 *f2 = new ConcreteFactory2();ClientCode(*f2);delete f2;return 0;}
三:工厂模式(Factory)工厂模式: 定义一个用于创建对象的接口,让接口子类通过工厂方法决定实例化哪一个类。该模式用来集中创建、组装、管理对象,跟具体业务代码解耦,让程序员聚焦在业务代码的开发上。
四:原型模式(Prototype)
原型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法
五:建造模式(Builder)
建造模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到。
该模式将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。
// Using the Builder pattern makes sense only when your products// are quite complex and require extensive configuration. The// following two products are related, although they don't have// a common interface.class Car is// A car can have a GPS, trip computer and some number of// seats. Different models of cars (sports car, SUV,// cabriolet) might have different features installed or// enabled.class Manual is// Each car should have a user manual that corresponds to// the car's configuration and describes all its features.// The builder interface specifies methods for creating the// different parts of the product objects.interface Builder ismethod reset()method setSeats(...)method setEngine(...)method setTripComputer(...)method setGPS(...)// The concrete builder classes follow the builder interface and// provide specific implementations of the building steps. Your// program may have several variations of builders, each// implemented differently.class CarBuilder implements Builder isprivate field car:Car// A fresh builder instance should contain a blank product// object which it uses in further assembly.constructor CarBuilder() isthis.reset()// The reset method clears the object being built.method reset() isthis.car = new Car()// All production steps work with the same product instance.method setSeats(...) is// Set the number of seats in the car.method setEngine(...) is// Install a given engine.method setTripComputer(...) is// Install a trip computer.method setGPS(...) is// Install a global positioning system.// Concrete builders are supposed to provide their own// methods for retrieving results. That's because various// types of builders may create entirely different products// that don't all follow the same interface. Therefore such// methods can't be declared in the builder interface (at// least not in a statically-typed programming language).//// Usually, after returning the end result to the client, a// builder instance is expected to be ready to start// producing another product. That's why it's a usual// practice to call the reset method at the end of the// `getProduct` method body. However, this behavior isn't// mandatory, and you can make your builder wait for an// explicit reset call from the client code before disposing// of the previous result.method getProduct():Car isproduct = this.carthis.reset()return product// Unlike other creational patterns, builder lets you construct// products that don't follow the common interface.class CarManualBuilder implements Builder isprivate field manual:Manualconstructor CarManualBuilder() isthis.reset()method reset() isthis.manual = new Manual()method setSeats(...) is// Document car seat features.method setEngine(...) is// Add engine instructions.method setTripComputer(...) is// Add trip computer instructions.method setGPS(...) is// Add GPS instructions.method getProduct():Manual is// Return the manual and reset the builder.// The director is only responsible for executing the building// steps in a particular sequence. It's helpful when producing// products according to a specific order or configuration.// Strictly speaking, the director class is optional, since the// client can control builders directly.class Director isprivate field builder:Builder// The director works with any builder instance that the// client code passes to it. This way, the client code may// alter the final type of the newly assembled product.method setBuilder(builder:Builder)this.builder = builder// The director can construct several product variations// using the same building steps.method constructSportsCar(builder: Builder) isbuilder.reset()builder.setSeats(2)builder.setEngine(new SportEngine())builder.setTripComputer(true)builder.setGPS(true)method constructSUV(builder: Builder) is// ...// The client code creates a builder object, passes it to the// director and then initiates the construction process. The end// result is retrieved from the builder object.class Application ismethod makeCar() isdirector = new Director()CarBuilder builder = new CarBuilder()director.constructSportsCar(builder)Car car = builder.getProduct()CarManualBuilder builder = new CarManualBuilder()director.constructSportsCar(builder)// The final product is often retrieved from a builder// object since the director isn't aware of and not// dependent on concrete builders and products.Manual manual = builder.getProduct()
池(Pool)对象池(Object Pool),线程池(Thread Pool),连接池(Connection Pool)等。以C#演示简单的Object Pool,其他类型的pool类似,不过由于T可能是需要回收的资源对象,所以还需要增加回收相关的代码。
C#代码如下
结构型模式六:复合模式(Composite)
合成模式: 将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性. 合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来;将单个对象和组合对象都看作树中的节点,以统一处理逻辑,并且它利用树形结构的特点,递归地处理每个子树,依次简化代码实现。
// The component interface declares common operations for both// simple and complex objects of a composition.interface Graphic ismethod move(x, y)method draw()// The leaf class represents end objects of a composition. A// leaf object can't have any sub-objects. Usually, it's leaf// objects that do the actual work, while composite objects only// delegate to their sub-components.class Dot implements Graphic isfield x, yconstructor Dot(x, y) { ... }method move(x, y) isthis.x = x, this.y = ymethod draw() is// Draw a dot at X and Y.// All component classes can extend other components.class Circle extends Dot isfield radiusconstructor Circle(x, y, radius) { ... }method draw() is// Draw a circle at X and Y with radius R.// The composite class represents complex components that may// have children. Composite objects usually delegate the actual// work to their children and then "sum up" the result.class CompoundGraphic implements Graphic isfield children: array of Graphic// A composite object can add or remove other components// (both simple or complex) to or from its child list.method add(child: Graphic) is// Add a child to the array of children.method remove(child: Graphic) is// Remove a child from the array of children.method move(x, y) isforeach (child in children) dochild.move(x, y)// A composite executes its primary logic in a particular// way. It traverses recursively through all its children,// collecting and summing up their results. Since the// composite's children pass these calls to their own// children and so forth, the whole object tree is traversed// as a result.method draw() is// 1. For each child component:// - Draw the component.// - Update the bounding rectangle.// 2. Draw a dashed rectangle using the bounding// coordinates.// The client code works with all the components via their base// interface. This way the client code can support simple leaf// components as well as complex composites.class ImageEditor isfield all: CompoundGraphicmethod load() isall = new CompoundGraphic()all.add(new Dot(1, 2))all.add(new Circle(5, 3, 10))// ...// Combine selected components into one complex composite// component.method groupSelected(components: array of Graphic) isgroup = new CompoundGraphic()foreach (component in components) dogroup.add(component)all.remove(component)all.add(group)// All components will be drawn.all.draw()
七:门面模式(Façade)门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用,减少复杂性。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。它通过封装细粒度的接口,提供组合各个细粒度接口的高层次接口,来提高接口的易用性,或者解决性能、分布式事务等问题。
1 门面角色 2 子系统角色
八:代理模式(Proxy)
代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。
代理模式,在不改变原始类接口的条件下,为原始类定义一个代理类,主要目的是控制访问,而非加强功能,这是它跟装饰器模式最大的不同;常用在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。
九:适配器(Adapter)
适配器模式: 把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端将两个不兼容的类纠合在一起使用,属于结构型模式,需要Adaptee(被适配者)和Adaptor(适配器)两个身份。代理模式、装饰器模式提供的都是跟原始类相同的接口,而适配器提供跟原始类不同的接口。适配器模式是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。适配器模式有两种实现方式:类适配器和对象适配器。其中,类适配器使用继承关系来实现,对象适配器使用组合关系来实现。
适配器模式常常是一种事后的补救策略,常用的场景如下:
- 封装有缺陷的接口设计
- 统一多个类的接口设计
- 替换依赖的外部系统
- 兼容老版本接口
- 适配不同格式的数据
十:装饰模式(DECORATOR)
装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。
使用Decorator的理由是:这些功能需要由用户动态决定加入的方式和时机.Decorator提供了"即插即用"的方法,在运行期间决定何时增加何种功能。它主要解决继承关系过于复杂的问题,通过组合来替代继承,给原始类添加增强功能。
十一:桥梁模式(Bridge)
桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化。也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。可以将该模式理解为:将抽象和实现解耦,让它们能独立开发;或者组合优于继承。
十二:享元模式(Flyweight)
享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。
所谓“享元”,顾名思义就是被共享的单元。享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。当一个系统中存在大量重复对象的时候,我们就可以利用享元模式,将对象设计成享元,在内存中只保留一份实例,供多处代码引用,这样可以减少内存中对象的数量,以起到节省内存的目的。实际上,不仅仅相同对象可以设计成享元,对于相似对象,我们也可以将这些对象中相同的部分(字段),提取出来设计成享元,让这些大量相似对象引用这些享元。
行为型模式十三:迭代模式(Iterator)
迭代模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。
迭代器模式也叫游标模式,它用来遍历集合对象。这里说的“集合对象”,我们也可以叫“容器”“聚合对象”,实际上就是包含一组对象的对象,比如,数组、链表、树、图、跳表。迭代器模式主要作用是解耦容器代码和遍历代码。谨记,在遍历的时候,不要修改增删集合中的元素,部分高级语言有对此场景的限制:一种是遍历的时候不允许增删元素,另一种是增删元素之后让遍历报错。c++程序在编译的时候,指定相应STL的宏,在非法操作迭代器的时候,也会产生迭代器异常,否则,默认是杀死进程。
十四:观察者模式(Observer)
观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。观察者模式将观察者和被观察者代码解耦。观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者一些产品的设计思路,都有这种模式的影子,比如,邮件订阅、RSS Feeds,本质上都是观察者模式。
十五:模板方法模式(Template)
模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。
模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。这里的“算法”,我们可以理解为广义上的“业务逻辑”,并不特指数据结构和算法中的“算法”。这里的算法骨架就是“模板”,包含算法骨架的方法就是“模板方法”,这也是模板方法模式名字的由来。模板方法模式有两大作用:复用和扩展。其中复用指的是,所有的子类可以复用父类中提供的模板方法的代码。扩展指的是,框架通过模板模式提供功能扩展点,让框架用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能。
十六 : 命令模式(Command)
命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。
命令模式用到最核心的实现手段,就是将函数封装成对象;他的主要作用和应用场景,是用来控制命令的执行,比如,异步、延迟、排队执行命令、撤销重做命令、存储命令、给命令记录日志等。lambda表达式也是这个意图,一般用于callback等场景,但是要警惕在高频调用下,要用类的方法代替lambda。
十七:状态模式(State)
状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。
状态模式一般用来实现状态机,而状态机常用在游戏、工作流引擎等系统开发中。状态机又叫有限状态机,它由3个部分组成:状态、事件、动作。其中,事件也称为转移条件。事件触发状态的转移及动作的执行。不过,动作不是必须的,也可能只转移状态,不执行任何动作。
十八:策略模式(Strategy):
策略模式:针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。
策略模式定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。策略模式用来解耦策略的定义、创建、使用。实际上,一个完整的策略模式就是由这三个部分组成的。客户端代码有两种确定方法,选择使用哪个策略:编译时静态确定和运行时动态确定。其中,“运行时动态确定”是策略模式最典型的应用场景。策略模式主要的作用还是解耦策略的定义、创建和使用,控制代码的复杂度,让每个部分都不至于过于复杂、代码量过多。
十九:责任链模式(CHAIN OF RESPONSIBLEITY)
责任链模式:多个处理器依次处理同一个请求。一个请求先经过A处理器处理,然后再把请求传递给B处理器,B处理器处理完后再传递给C处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式。
在GoF的定义中,一旦某个处理器能处理这个请求,就不会继续将请求传递给后续的处理器了。当然,在实际的开发中,也存在对这个模式的变体,那就是请求不会中途终止传递,而是会被所有的处理器都处理一遍。职责链模式常用在框架开发中,用来实现过滤器、拦截器功能,让框架的使用者在不需要修改框架源码的情况下,添加新的过滤、拦截功能。这也体现了之前讲到的对扩展开放、对修改关闭的设计原则。
二十:中介者/调停者模式(Mediator)
调停者模式:包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。
中介模式的设计思想跟中间层很像,通过引入中介这个中间层,将一组对象之间的交互关系(或者说依赖关系)从多对多(网状关系)转换为一对多(星状关系)。原来一个对象要跟n个对象交互,现在只需要跟一个中介对象交互,从而最小化对象之间的交互关系,降低了代码的复杂度,提高了代码的可读性和可维护性。观察者模式和中介模式都是为了实现参与者之间的解耦,简化交互关系。两者的不同在于应用场景上。在观察者模式的应用场景中,参与者之间的交互比较有条理,一般都是单向的,一个参与者只有一个身份,要么是观察者,要么是被观察者。而在中介模式的应用场景中,参与者之间的交互关系错综复杂,既可以是消息的发送者、也可以同时是消息的接收者。
二十一:访问者模式(Visitor)
访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。
访问者模式允许一个或者多个操作应用到一组对象上,设计意图是解耦操作和对象本身,保持类职责单一、满足开闭原则以及应对代码的复杂性。
二十二: 解释器(Interpreter)
解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。
该模式代码实现的核心思想,就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。一般的做法是,将语法规则拆分一些小的独立的单元,然后对每个单元进行解析,最终合并为对整个语法规则的解析。
二十三:备忘录模式(Memento)
Memento 模式:Memento对象是一个保存另外一个对象内部状态拷贝的对象,这样以后就可以将该对象恢复到原先保存的状态。模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。模式所涉及的角色有三个,备忘录角色、发起人角色和负责人角色。
备忘录角色的作用:
(1) 将发起人对象的内部状态存储起来,备忘录可以根据发起人对象的判断来决定存储多少发起人对象的内部状态。
(2) 备忘录可以保护其内容不被发起人对象之外的任何对象所读取。
发起人角色的作用:
(1) 创建一个含有当前内部状态的备忘录对象。
(2) 使用备忘录对象存储其内部状态。
负责人角色的作用:
(1) 负责保存备忘录对象。
(2) 不检查备忘录对象的内容
备忘录模式也叫快照模式,具体来说,就是在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。这个模式的定义表达了两部分内容:一部分是,存储副本以便后期恢复;另一部分是,要在不违背封装原则的前提下,进行对象的备份和恢复。备忘录模式的应用场景也比较明确和有限,主要用来防丢失、撤销、恢复等。它跟平时我们常说的“备份”很相似。两者的主要区别在于,备忘录模式更侧重于代码的设计和实现,备份更侧重架构设计或产品设计。对于大对象的备份来说,备份占用的存储空间会比较大,备份和恢复的耗时会比较长。针对这个问题,不同的业务场景有不同的处理方式。比如,只备份必要的恢复信息,结合最新的数据来恢复;再比如,全量备份和增量备份相结合,低频全量备份,高频增量备份,两者结合来做恢复。
黑板模式(Blackboard)
黑板模式:它为系统的设计和实现提供了一个计算框架,这些系统集成了大量不同的专业模块,并实现了复杂的、非确定性的控制策略。黑板模型定义了三个主要组件:
黑板 - 一个结构化的全局内存,包含来自解决方案空间的对象;
知识来源 - 具有自己表示的专业模块;
控制组件 - 选择、配置和执行模块。
UML类图如下
二十四:MVC模式
MVC 模式:其实是个复合模式(主要有:Observer,Composite,Strategy)。它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器;它们各自处理自己的任务,相互通信。MVC 还使用了其他的设计模式,如:用来指定视图缺省控制器的 Factory Method 和用来增加视图滚动的Decorator。
并发模式(Concurrency patterns)
Name |
Description |
Active Object |
Decouples method execution from method invocation that reside in their own thread of control. The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests. |
Balking |
Only execute an action on an object when the object is in a particular state. |
Binding properties |
Combining multiple observers to force properties in different objects to be synchronized or coordinated in some way.[23] |
Compute kernel |
The same calculation many times in parallel, differing by integer parameters used with non-branching pointer math into shared arrays, such as GPU-optimized Matrix multiplication or Convolutional neural network. |
Double-checked locking |
Reduce the overhead of acquiring a lock by first testing the locking criterion (the 'lock hint') in an unsafe manner; only if that succeeds does the actual locking logic proceed.Can be unsafe when implemented in some language/hardware combinations. It can therefore sometimes be considered an anti-pattern. |
Event-based asynchronous |
Addresses problems with the asynchronous pattern that occur in multithreaded programs.[24] |
Guarded suspension |
Manages operations that require both a lock to be acquired and a precondition to be satisfied before the operation can be executed. |
Join |
Join-pattern provides a way to write concurrent, parallel and distributed programs by message passing. Compared to the use of threads and locks, this is a high-level programming model. |
Lock |
One thread puts a "lock" on a resource, preventing other threads from accessing or modifying it.[25] |
常见的体系结构模式:
- Blackboard system
- Broker pattern
- Event-driven architecture
- Implicit invocation
- Layers
- Hexagonal architecture
- Microservices
- Action–domain–responder, Model–view–controller, Presentation–abstraction–control, Model–view–presenter, and Model–view–viewmodel
- Entity component system
- Entity-control-boundary
- Multitier architecture (often three-tier or n-tier)
- Naked objects
- Operational data store (ODS)
- Peer-to-peer
- Pipe and filter architecture
- Service-oriented architecture
- Space-based architecture
- Distributed hash table
,