最全golang设计模式
2023-07-25 15:30:22 2 举报
AI智能生成
最全golang版本的设计模式学习脑图
作者其他创作
大纲/内容
Template Method模式
类型
子类实现
具体处理交给子类
分类
行为型模式
别名
模版方法模式
定义
定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模版方法使得子类可以不改变一个算法结构即可重新定义该算法的某些特定步骤。
功能
固定算法骨架,而让具体算法实现可扩展
好处
可以控制子类的扩展
优缺点
优点
封装不变部分,扩展可变部分
提取公共代码,便于维护
行为由父类控制,子类具体实现
缺点
增加代码复杂度
场景
需要固定定义算法,实现一个算法的不变部分,并把可变的行为留给子类来实现的场景
各个子类中具有的公共行为,抽取出来,集中在一个公共类去实现,避免代码重复
需要控制子类扩展情况
扩展
可以在模版方法模式中加入钩子函数,可以使得子类控制父类的行为
UML
角色
AbstractClass(抽象类)
负责实现模版方法
负责声明在模版方法中使用的抽象方法
ConcreteClass(具体类)
具体实现AbstractClass角色中定义的抽象方法。
Golang
相关模式
工厂模式
可以相互配合使用
策略模式
都能实现算法封装
模版方法模式封装的是算法的骨架,该骨架是不变的,变化的是算法中某些步骤的具体实现;策略模式是把某个步骤的具体实现算法封装起来,所有的封装的算法对象都是等价的,可以相互替换
Factory Method模式
类型
子类实现
将实例的生成交给子类
分类
类创造性模式
完成类实例化,并将类中的部分对象放到子类中创建,此模式在实例化过程中高效地利用了继承机制。
别名
工厂方法模式
定义
定义一个用于创建对象的接口,让其子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到其子类
本质
延迟到子类来选择实现
倒置依赖原则
功能
让父类在不知道具体实现的情况下,完成自身的功能调用,而具体的实现延迟到子类实现
场景
一个类需要创建某个接口的对象,但又不知道具体的实现,选择工厂方法模式,把创建对象的工作延迟到子类实现
一个类本身就希望由它子类创建所需的对象
想要定位被创建类,并获取相关信息时
扩展
当需要生产的产品不多且不会增加,一个具体工厂类就可以完成任务时,可以删除抽象工厂类,此时就是简单工厂模式
优缺点
优点
可以在不知道具体实现情况下编程
更容易扩展对象的新版本
连接平行层次的类层次
缺点
具体的产品对象与工厂方法耦合
UML
角色
Product(产品)
定义工厂方法所创建的对象的接口,也是实际需要使用对象的接口
AbstractFactory(抽象工厂)
创建器,声明工厂方法,工厂方法通常返回一个Product类型的实例对象,而且多是抽象方法
ConcreteProduct(具体的产品)
具体的Product接口的实现对象
ConcreteFactory(具体的工厂)
具体的创建器对象,覆盖实现Creator定义工厂方法,返回具体的Product实例
golang
Prototype模式
类别
生成实例
通过复制生成实例
别名
原型模式
定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象
本质
克隆生成对象
克隆是手段,目的是生成新的对象实例
优缺点
优点
对客户端隐藏具体的实现类型
在运行时动态改变具体的实现类型
逃避构造函数的约束
缺点
每一个原型子类都必须使用克隆操作
场景
难以根据类生成实例时
想解耦框架与生成的实例时
资源优化场景
性能和安全要求的场景
扩展
原型模式,可扩展为带原型管理器的原型模式,它在原型模式的基础上增加了一个原型管理器类。
UML
角色
Prototype(原型)
声明一个克隆自身的接口,用来约束想要克隆自己的类,要求它们都要事先该克隆方法
ConcretePrototype(具体原型)
事先Prototype接口的类,这些类真正事先克隆自身的功能
Client(使用者)
使用原型的客户端,首先要获取到原型实例对象,然后通过原型实例克隆自身来创建新的对象实例
golang
Abstract Factory模式
类别
生成实例
将关联零件组装成产品
分类
对象创造性模式
别名
抽象工厂模式
定义
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
本质
选择产品簇的实现
优缺点
优点
分离接口和实现
客户端使用抽象工厂来创建需要的对象,不需要知道具体的实现类,只是面向接口编程
产品簇内的约束是非公开状态
客户端选用不同工厂实现就相当于切换不同的产品簇
缺点
扩展新的产品困难
新加产品需要给整个产品簇新加一个新的抽象工厂
容易造成类层次复杂
在使用抽象工厂模式,如果选择层次过多,会造成整个类层次复杂
场景
系统独立于它的产品创建、组合和表示的场景
系统需要多个产品系列中的一个配置,即需要动态地切换产品簇
一个产品簇需要有系统约束
扩展
开闭原则倾斜性
当增加一个新的产品簇时只需要增加一个新的具体工厂,不需要修改源代码,满足开闭原则
当产品簇需要新加新的种类的产品时,则所有的工厂类都需要进行修改,不满足开闭原则
当系统中只存在一个等级结构的产品是,抽象工厂将退化到工厂方法模式
UML
角色
AbstractProduct(抽象产品)
定义一类产品对象接口
AbstractFactory(抽象工厂)
定义创建一系列产品对象的操作接口
ConcreteProduct(具体产品)
具体的产品实现对象,通常在具体工厂里面,会选择具体的产品实现对象,来创建符合抽象工厂定义的方法返回的产品类型对象
ConcreteFactory(具体工厂)
实现抽象工厂定义的方法,具体实现一系列产品对象的创建
golang
相关模式
工厂方法模式
工厂方法模式一般是针对单独的产品对象的创建,而抽象工厂模式注重产品簇对象的创建
单例模式
组合使用,在抽象工厂模式中,具体的工厂实现一般是单例
Strategy模式
类别
分开考虑
整体替换算法
分类
行为型模式
别名
策略模式
政策模式
定义
定义一系列的算法,把它们一个个封装起来,并使它们可以相互替换。本模式使得算法可以独立于使用它的客户端变化
本质
分离算法,选择实现
优缺点
优点
算法可以自由切换
避免使用多重条件判断
更好的扩展性
缺点
客户端需要了解每种算法的不同,对外保留了策略类
增加了对象数目,策略复用性低
只适合扁平的算法结构
场景
多个类只有在算法或行为上稍有不同的场景
算法需要自由切换的场景
需要屏蔽算法规则的场景
扩展
在使用策略模式时,如存在策略很多时,客户端管理所有的策略算法将变得复杂,此时可以使用策略工厂来管理策略类,以降低复杂度
UML
2365
角色
Strategy(策略)
策略、算法簇的抽象,定义每个策略或算法必须具有的方法和属性
ConcreteStrategy(具体的策略)
实现Strategy角色中定义的行为
Context(上下文)
起着封装效果,屏蔽高层模块对策略、算法的直接访问,分钟可能存在的变化
golang
相关模式
状态模式
策略模式是根据需要或者是客户端的要求束选择相应的实现类,各个实现类是平等的,是可以相互替换的。另外策略模式可以让客户端来选择i要使用的策略算法;而状态模式一般是由上下文,或者是在状态实现类里面来维护具体的状态数据,通常不由客户端来指定状态。
Decorator模式
类别
一致性
装饰边框与被装饰物的一致性
分类
结构型模式
别名
装饰模式
定义
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更加灵活。
要给一个对象增加一个新的功能,但是不能让该对象知道,也就是不能去改动该对象,而实现给对象透明地增加功能。
本质
动态组合
动态是手段,组合是目的
优缺点
优点
装饰类和被装饰类可以独立发展,而不会耦合
比继承更加灵活
可以动态地扩展一个实现类
简化高层定义
缺点
会产生更多细粒度对象
场景
需要扩展一个类或者给一个类增加附加功能
需要动态地给一个对象增加功能,这些功能还可以动态地撤销
需要为一批兄弟类批量改造或者加装功能,此时不适合使用继承
扩展
如果只有一个具体构建而没有抽象构建时,可以让抽象装饰继承具体构建
如果只有一个具体装饰时,可以将抽象装饰和具体装饰合并
UML
角色
Componment(组件接口)
组件对象的接口,可以给这些对象动态地添加功能
ConcreteComponment(具体组件对象)
实现组件对象接口,通常就是被装饰器装饰的原始对象,也就是给该对象添加功能
Decorator(装饰器)
所有装饰器的抽象父类,需要定义一个与组件接口一致的接口,并持有一个Compoent对象,就是持有一个被装饰的对象
ConcreteDecorator(具体的装饰器)
实现具体要想被装饰对象添加的功能
golang
相关模式
组合模式
装饰模式是为了动态地给对象增加功能;组合模式是想要管理组合对象和叶子对象,为它们提供统一一致的操作接口给客户端。
适配器模式
适配器模式是用来改变接口的,而装饰模式是为了改变对象功能
Facade模式
类别
简单化
简单窗口
别名
外观模式
门面模式
定义
为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
为了让外部减少与子系统内部多个模块交互,松耦合,让外部系统更加简单地使用子系统
本质
封装交互,简化调用
优缺点
优点
减少系统间的依赖,实现松耦合
提高灵活性,简单易用
不管子系统内部如何变化,只要不影响到外观对象,客户端调用就不需要改变
提高安全性
缺点
增加新的子系统可能需要修改外观类或客户端代码
不符合开闭原则
不能很好地限制客户端使用子系统类
场景
为一个复杂的模块或子系统提供统一的访问接口
子系统相对独立--外界对子系统的访问只要黑箱操作即可
预防低水平开发人员带来的风险扩散
扩展
在外观模式中,当增加或移除子系统时需要修改外观类,违背了“开闭原则”。如果引入抽象外观类,则在一定程度上解决了该问题
UML
角色
Facade(外观窗口)
定义子系统的多个模块对外的高层接口,通常需要调用内部多个模块,从而吧客户端的请求代理给适当的子系统对象
各个子系统模块
接口Facade对象的委派,真正事先功能,各个模块之间的可能存在交互
Facade对象知道各个模块,但是各个模块不知道Facade对象
golang
相关模式
中介模式
中介者模式主要用来封装多个对象之间相互的交互,多用在系统内部的多个模块之间;而外观模式封装的是单向的交互,是从客户端访问系统的调用,没有从系统中来访问客户端的调用。
在中介者模式的实现里面,是需要实现具体的交互功能的;而外观模式的实现里面, 一般是组合调用或是转调内部实现的功能,通常外观模式本身并不实现这些功能。中介者模式的目的主要是松散多个模块之间的耦合,把这些耦合关系全部放到中介者中去实现:而外观模式的目的是简化客户端的调用,这点和中介者模式也不同。
Observer模式
类别
管理状态
发送状态变化通知
分类
行为型模式
别名
观察者模式
发布-订阅模式
模式-视图模式
定义
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会受到通知并被自动更新
本质
触发联动
优缺点
优点
实现观察者与目标之间的抽象耦合
实现动态联动
支持广播通信
缺点
引起无效的操作
目标与观察者之间依赖关系没有完全解除,而且可能出现循环引用
场景
关联场景,其中一方的操作依赖于另一方的状态变化
事件多级触发场景
跨系统的消息交互场景
UML
角色
Subject(被观察对象)
定义被观察者必须实现的职责,他必须能够动态地增加,取消观察者。它一般为抽象类或实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者
ConcreteSubject(具体的被观察对象)
表示具体的被观察对象,当自身状态发生变更后,它会通知所有已经注册的Observer角色
Observer(观察者)
接收来自Subject角色的状态变化通知。
ConcreteObserver(具体的观察者)
具体的Observer
golang
相关模式
状态模式
观察者模式是当目标状态发生改变时,触发并通知观察者,让观察者去执行相应的操作。而状态模式是根据不同的状态,选择不同的实现,这个实现类的主要功能就是针对状态相应地操作,它不像观察者,观察者本身还有很多其他的功能,接收通知并执行相应处理只是观察者的部分功能。
Flyweight模式
类别
避免浪费
共享对象,避免浪费
别名
享元模式
定义
使用共享对象可有效地支持大量的细粒度的对象。
吧一个对象的状态分为内部状态和外部状态,内部状态是不变的,外部状态是可变的。然后通过共享不变的部分,达到减少对象数量并节约内存的目的。在享元对象需要的时候,可以从外部传入外部状态给共享的对象,共享对象会在功能处理的时候,使用自己内部的状态和这些外部状态。
本质
分离与共享
优缺点
优点
减少对象数量,节约内存空间
缺点
维护共享对象,需要额外开销
场景
系统存在大量相似对象
需要缓冲池
细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,即对象没有特定身份
扩展
单纯享元模式,该模式中的所有的具体享元类都是可以共享的,不存在非共享的具体享元类。
复合享元模式,该模式中的有些享元对象私有一些单纯享元对象组合而成,它们就是复合享元对象。虽然享元对象本身不能共享,但是分解为单纯向原对象再被共享。
UML
角色
Flyweight(享元)
被共享的类,定义出对象外部状态和内部状态的接口或实现
ConcreteFlyweight(具体的享元)
实现Flyweight角色的具体实现,该角色需要注意的是内部状态处理应该与环境无关,不能出现操作改变内部状态时,同时修改外部状态。
FlyweightFactory(享元工厂)
构造一个池容器,同时提供从池中获取对象的方法
golang
Chain of Responsibility模式
类型
访问数据结构
推卸责任
分类
行为型模式
别名
责任链模式
定义
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链路传递该请求,直到有一个对象处理它位置。
本质
分离职责,动态组合
分离职责是前提,动态组合是精华是目的
优缺点
优点
请求者与接受者松耦合
动态组合职责
增强了系统的可扩展性和给对象指派职责的灵活性
简化了对象之间的连接
责任分担,每个类只需要处理自己改处理的工作,不处理的传递给下一个对象完成,明确职责
缺点
产生很多细粒度的对象
性能较低
调式不方便
场景
有多个对象可以处理同一个请求,但是具体由哪个对象处理需要运行时刻才能确定
在不明确接受者情况下,向多个对象中的其中一个提交请求
需要动态地指定处理一个请求的对象合集
扩展
纯的职责链模式
一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。
不纯的职责链模式
允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。
UML
角色
Handler(处理者)
定义职责接口,Handler角色需要下一个处理者是谁,如果自身不能处理,则将请求转发给下一个处理者。
ConcreteHandler(具体的处理者)
处理具体请求的角色
golang
Interpreter模式
类别
用类来表现
语法规则也是类
分类
行为型模式
别名
解释器模式
定义
给定一门语言,定义他的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子
本质
分离实现,解释执行
优缺点
优点
易于扩展新的语法
易于实现语法
缺点
不适合复杂的语法
引起类的膨胀
采用递归方法
效率较低
场景
重复发生的问题
一个简单语法需要解释
UML
角色
AbstractExpression(抽象表达式)
具体的解释任务由各个实现类完成
TerminalExpression(终结符表达式)
实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。
NonterminalExpression(非终结表达式)
文法中的每条规则对应于一个非终结表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式
Context(上下文)
为解释器进行语法解析提供必要的信息
golang
设计模式简介
历史
1977年美国建筑师克里托夫-亚历山大著作《建筑模式语言:城镇、建筑、构造》描述了建筑设计问题,提出了进行设计的基本模式
1979年
克里托夫-亚历山大另一部著作《建筑的永恒之道》进一步强化了设计模式的实现,为后来的建筑设计指明了方向
1987年
肯特-贝克、沃德-坎宁安将克里托夫的模式实现应用在smalltalk中的图形接口的生成中
1990年
软件行业才关注到设计模式并举行多场研讨会
1994年
艾瑞克-伽马、理查德-海尔姆、拉尔夫-约翰森和约翰-威利斯迪斯联合出版《设计模式:可复用面向对象软件的基础》,收录了23种设计模式,这是设计模式的里程碑事件,他们称为“四人组”GoF。
概念
一套被反复使用,多数人知晓的,经过分类编目的、代码设计经验的总结。
在软件开发中,经过验证的,用于解决在特定环境下、重复出现的、特定问题的解决方案
描述了在软件设计过程中一些不断重复发生的问题,以及该问题的解决方案。设计模式解决特定问题的一系列套路,是前辈们的代码设计经验总结,具有一定的普适性,可以反复使用
目的
为了提高代码的可重用性、代码的可读性和代码的可靠性
本质
面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解
优点
提高开发人员的思维能力、编码能力和设计能力
使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提升,从而缩短开发周期
使设计的代码可重用性高,可读性强、灵活性好、可维护性高。
要素
模式名称
每一个模式都有自己的名字,可以根据模式的问题、特点、解决方案、功能和效果来命名。模式名字有助于我们理解和记忆模式,也便于讨论。
问题
问题描述了该模式的应用环境,即何时使用该模式。它解释了设计问题和问题存在的前因后果,以及必须满足的一系列先决条件
解决方案
模式问题的解决方案包括设计的组成部分、它们之间的相互关系以及各自的职责和写作方式。因为模式就是一个模版,可以应用于多种不同场合,所以解决方案并不描述一个特定而具体的设计或实现,而是提供设计问题的抽象描述和怎么样用一个具有一般意义的元素组合来解决这个问题
效果
描述了模式的应用效果以及使用该模式应该权衡的问题,即模式的优缺点。主要是对时间和空间的衡量,以及该模式对系统的灵活性、扩充性、可移植性的影响,也考虑其实现问题。显式地列出这些效果对理解和评价模式有着很大作用。
分类
口诀
工单原建(件)抽
适代(世代)桥装(乔装)外享(外乡)组
分类模式
结构型模式
描述如何将类或对象按照某种布局组成更大的结构
分类
类结构型模式
采用继承机制来组织接口和类
对象结构型模式
采用组合或聚合方式组合对象
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大灵活性
创建型模式
关注类的实例化
分类
对象创建型模式
主要完成对象创建,并将对象中部分内容放到其他对象中创建。
类创建型模式
主要完成类实例化,并将类中的部分对象放到子类中创建。此类模式在实例化过程中高效地利用了继承机制。
行为型模式
描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,他涉及算法与对象间职责分配。
分类
类型为模式
采用继承机制来在类间分派行为
对象行为模式
采用组合或聚合在对象间分配行为
设计模式与软件框架对比
UML
别名
Unified Modeing Language
统一建模语言
参考
https://www.uml.org/what-is-uml.htm#12DiagramTypes
特点
简单、统一、图形化、能表达软件设计的动态与静态信息
UML图表示的是一个系统。系统并不仅仅是一些事物的简单集合,而是一个由一组相互连接的要素构成的、能够实现某个目标的整体。从这一定义可见,任何一个系统都包括三种构成要件:要素、连接、功能或目标。
来源《系统之美》
类型
结构图
类图
类图是描述系统中的类,以及各个类之间的关系的静态视图。能够让我们在正确编写代码以前对系统有一个全面的认识。类图是一种模型类型,确切地说,是一种静态模型类型。类图表示类、接口和它们之间的协作关系。
对象图
与类图极为相似,它是类图的实例,对象图显示类的多个对象实例,而不是实际的类。它描述的不是类之间的关系,而是对象之间的关系。
构件图
描述代码构件的物理结构以及各种构建之间的依赖关系。用来建模软件的组件及其相互之间的关系,这些图由构件标记符和构件之间的关系构成。在组件图中,构件时软件单个组成部分,它可以是一个文件,产品、可执行文件和脚本等。
部署图
用来建模系统的物理部署。例如,计算机和设备,以及它们之间是如何连接的。部署图的使用者是开发人员、系统集成人员和测试人员。部署图用于表示一组物理结点的集合及结点间的相互关系,从而建立了系统物理层面的模型。
行为图
例图
描述角色以及角色与用例之间的连接关系。说明是谁要使用系统,以及他们使用该系统可以做些什么。一个用例图包含了多个模型元素,如系统、参与者和用例,并且显示了这些元素之间的各种关系,如泛化、关联和依赖。
活动图
描述用例要求所要进行的活动,以及活动间的约束关系,有利于识别并行活动。能够演示出系统中哪些地方存在功能,以及这些功能和系统中其他组件的功能如何共同满足前面使用用例图建模的商务需求。
状态
描述类的对象所有可能的状态,以及事件发生时状态的转移条件,可以捕获对象、子系统和系统的生命周期。它可以告知一个对象可以拥有的状态,并且事件(如消息的接收、时间的流逝、错误、条件变为真等)会怎么随着时间的推移来影响这些状态。一个状态图应该连接到所有具有清晰的可标识状态和复杂行为的类;该图可以确定类的行为,以及该行为如何根据当前的状态变化,也可以展示哪些事件将会改变类的对象的状态。状态图是对类图的补充。
交互图
序列图(顺序图)
序列图是用来显示参与者如何以一系列顺序的步骤与系统的对象交互的模型。顺序图可以用来展示对象之间是如何进行交互的。顺序图将显示的重点放在消息序列上,即强调消息是如何在对象之间被发送和接收的。
符号
类
具有相同属性、方法和关系的对象抽象,封装了数据和行为。
对象
类的具体实例
与类图的区别是对象名称下面有一条下划线
接口
一种特殊的类,它具有类的结构但不可被实例化,只能被子类实现。包含抽象操作,但不包含属性,描述了类或组件对外可见的动作
关系
描述类与类之间的关系
关联关系
普通关联
只要类与类之间存在关联即可使用普通关联来表示
标准表示是一条直线
递归关联
一个类与它本身有关联关系
聚合关联
普通聚合
描述类与类之间具有“整体与部分”的关系
在表示关联关系的一端加上一个空心菱形,表示具有整体的含义;另一端可以没有标示,也可以加上箭头,表示方向性。
共享聚合
如果聚合关系中,处于部分方的对象参与了多个整体方对象构成,描述成为共享聚合。
表示与普通聚合一样
复合聚合
构成整体类的部分类,完全隶属于整体类
复合聚合是一对多,“一”这端使用实心菱形表示。符合聚合表达,如果整体对象不存在,那么部分对象也就没有存在的意义。
泛化关系
描述一个通用元素的所有信息能被另外一个具体元素继承的机制
使用一个空心箭头表示,箭头指向的一端为父元素,另一端为子元素
实现关系
描述类实现接口的关系
依赖关系
描述某个对象的行为和实现,需要收到另外对象的影响
箭头指向的一端代表独立元素,另一端表示依赖元素
Iterator模式
类型
适应设计模式
分类
行为型模式
别名
迭代器模式
定义
提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露(关注)该对象的内部实现细节
聚合对象就是组合结构如list、数组等等
功能
提供对聚合对象的迭代访问
优缺点
优点
访问一个聚合对象的内容而不需要暴露其实现
遍历任务交给迭代器完成,简化聚合类
支持以不同方式遍历一个聚合,甚至自定义迭代器的子类以支持新的遍历
增加新的聚合类和迭代类都很方便,无需修改原有代码
封装性好,为遍历不同的聚合结构提供了统一的接口
缺点
增加了类的个数,增加了系统的复杂性
场景
以不同的方式遍历聚合对象,比如正序、倒序
对同一个聚合同时进行多个遍历
以不同的遍历策略遍历聚合,如是否在遍历中过滤
多态迭代:为不同聚合架构提供统一迭代接口
关键点
把聚合对象的遍历和访问冲聚合对象中分离,放入单独的迭代器中
UML
角色
Iterator(迭代器)
负责定义按顺序遍历元素的接口API。
Concretelterator(具体的迭代器)
负责实现Iterator角色定义的接口API
Aggregate(集合)
负责定义创建Iterator角色的接口
ConcreteAggregate(具体的集合)
负责实现Aggregate角色定义的接口
golang
实现类型
内部迭代器
有迭代器自身控制迭代下一个元素的步骤,客户端无法干预、
特点
简单不灵活
外部迭代器
由客户端控制迭代下一个元素的步骤,客户端必须显示调用next来迭代下一个元素。
特点
灵活但操作复杂
Adapter模式
类型
适应设计模式
加个“适配器”以便于复用
别名
适配器模式
Wrapper模式
包装模式
定义
将一个类的接口转换成为客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的哪些类型可以一起工作。
本质
转换匹配,复用功能
场景
使用一个已存在的类,但其接口不符合需求
需要复用的类与一些不兼容的类一起工作
版本升级与兼容
类型
类适配器模式
使用继承的适配器
对象适配器模式
使用委托的适配器
优缺点
优点
更好的复用性
让没有关联的类一起运行
提高类的复率
更好的可扩展性
灵活性好,不破坏原有系统
缺点
过多使用适配器,会让系统凌乱,不容易整体把控
扩展
适配器模式可扩展为双向适配器,双向适配器类既可以把适配者接口转换成目标接口,也可以把目标接口转换成适配者接口
UML
类适配器模式
对象适配器模式
角色
Target(对象)
负责定义所需的方法
Client(请求者)
负责使用Target角色定义的方法进行具体的处理
Adaptee(被适配)
一个持有既定方法的角色
Adapter(适配)
使用Adaptee角色的方法来满足Target角色的需求
golang
类适配器模式
相关模式
桥接模式
适配器模式是把两个或者多个接口的功能转换匹配;桥接模式是让接口和实现部分相分离,以便它们可以独立的变化。
装饰模式
两种设计模式在实现上都是使用对象组合,都可以在转调组合对象的功能前后进行附加的处理,但是目的和本质不一样
一般适配器适配过后需要改变接口,如果不改变这不必要适配。装饰模式是不改变接口,装饰模式支持多层递归。
代理模式
适配模式可以和代理模式组合使用。在实现适配器的时候,通过代理来调用Adaptee,增加灵活性
Singleton模式
类别
生成实例
别名
单例模式
定义
保障一个类仅有一个实例,并提供一个访问它的全局访问点
功能
保证该类在运行期间只会被创建一个类的实例
本质
控制实例数目
场景
当存在多个实例时,实例之间相互影响
对象需要被共享的场景
扩展
单例模式可以扩展为有限的多例模式,可生产有限个并保存找容器中。客户端需要时随时获取
UML
角色
Singleton(单例对象)
负责双证Singleton类自己的唯一实例
golang
Builder模式
类别
生成实例
组装复杂的实例
别名
生成器模式
建造者模式
定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
功能
构建复杂的产品,而且是细化的、分步骤的构建产品,也就是建造者模式重在一步一步解决构造复杂对象的问题
本质
分离整体构建算法和部件构造
优缺点
优点
封装性良好,松耦合
建造者独立,容易扩展
便于控制细节风险,更好的复用性
缺点
产品的组成部分必须相同,限制使用范围
产品内部变化复杂,会增加很多建造者类
场景
设计时能够决定的事与不能决定的事分离
相同的方法,不同的执行顺序,产生不同的事件结果
扩展
创建的产品种类只有一种,只需要一个具体建造者,此时可以省略抽象建造者,甚至可以省略指导者
UML
角色
Builder(建造者)
负责定义用于生成实例的接口,定义创建一个Product对象所需的各个部件的操作
ConcreteBuilder(具体的建造者)
负责实现Builder角色的接口的类。也是具体的建造者实现,实现各个部件的创建,并负责组装Product对象的各个部件,同时还提供让用户获取组件完成后的Product对象的方法。
Product(产品)
被建造者构造的复杂对象,包含多个部件。通常是实现了模版方法模式
Director(指导者)
用来使用Builder接口,以一个统一的过程来构建所需要的Product对象。其并不依赖于ConcreteBuilder角色
Client(使用者)
使用Builder模式
golang
相关模式
工厂方法模式
可以组合使用。建造者模式的Builder实现中,通常需要选择具体的部件实现。一个可行的方案就是实现成为工厂方法,通过工厂方法来获取具体的部件对象,然后进行部件装配。
抽象工厂模式
抽象工厂模式的主要目的是创建产品簇,这个产品簇里面的单个产品就相当于是构成一个复杂对象的 件对象,H家上对象创建完成后就立即返回整个产品簇,而生成器模式的主要目的是按照构造算法,一步一步来构建个复杂的产品对象,通常要等到整个构建过程结束以后,才会得到最终的产品对象。
模版方法模式
模板方法模式主要是用来定义算法的骨架,把算法中某些步骤延迟到子类中实现。再想想生成器模式,Director用来定义整体的构建算法,把算法中某些涉及到具体部件对象的创建和装配的功能,委托给具体的Builder来实现.虽然生成器不是延迟到子类,是委托给Builder.但那只是具体实现方式上的差别,从实质上看两个模式很类似,都是定义一个固定的算法骨架,然后把算法中的某些具体步骤交给其他类来完成,都能实现整体算法步骤和某些具体步骤实现的分离。
这两个模式也有很大的区别,首先是模式的目的,生成器模式是用来构建复杂对象的,而模板方法是用来定义算法骨架,尤其是一些复杂的业务功能的处理算法的骨架;其次是模式的实现,在成容模式是采用委托的方法,而模板方法采用的是继承的方式:另外从使用的复杂度上,生成器模式需要组合Director和Builder对象,然后才能开始构建,要等构建定后才能获得最终的对象,而模板方法就没有这么麻烦,直接使用子类对象即可。
Bridge模式
类别
分开考虑
将类的功能层次结构与实现层次结构分离
别名
桥接模式
桥梁模式
定义
将抽象部分与它的实现部分分离,使它们都可以独立的变化
类的接口与接口的实现相互解耦
连接类的功能层次结构和类的实现层次结构,换句话,连接抽象部分和实现部分
需要注意的是连接是单向的
本质
分离抽象和实现
功能
是的抽象和实现可以独立变化,可以分别扩展
优缺点
优点
抽象和实现分离
良好的扩展能力
实现细节对客户端透明,可动态切换
减少子类个数
不使用继承方式实现减少子类个数
缺点
由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计和编程,增加了系统的理解和设计难度
场景
不希望或不适用使用继承的场景
接口或抽象类不稳定
重用性要求高
UML
角色
Abstraction(抽象化)
定义出该角色的行为,同时保存一个实例化角色的引用,还角色一般是抽象类
RefinedAbstraction(扩展抽象化)
引用实例化角色对抽象化角色进行扩展
Implementor(实现者)
定义角色必须的行为和属性
ConcreteImplementor(具体的实现者)
实现Implementor角色定义的方法
golang
推送消息
选购场景
相关模式
策略模式
策略模式是为了封装一系列算法,使得这些算法可以相互替换;桥接模式是为了分离抽象和实现。
状态模式
状态模式是为了封装状态对应的行为,并在内部状态改变的时候只改变对象的行为
适配器模式
适配器模式可以用于让两个不兼容的类一起工作。桥接模式通过创建两个不同的继承层次将抽象化对象和实现化对象解耦
Composite模式
类别
一致性
容器和内容的一致性
分类
结构型模式
别名
组合模式
部分-整体模式
定义
将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性
功能
客户端不再区分组合对象和叶子对象,而是以一种统一方式处理
本质
统一组合对象和叶子对象
优缺点
优点
统一了组合对象和叶子对象
简化了客户端调用方式
节点自由增加
缺点
难以限制组合中的组件类型
设计较复杂,客户端需要清理类之间的层次关系
不容易用继承的方法来增加构件的新功能
场景
维护和展示部分-整体关系的场景,如数形菜单、文件夹管理
从一个整体中能够独立出部分模块或者功能的场景
UML
角色
Component(组件容器)
定义叶子对象和组合对象公共的方法和属性,,使它们具有一致性角色
Composite(组合对象)
表示容器角色,可以在在其中放入Leaf和Composite角色
Leaf(叶子对象)
表示叶子对象,其下再也没有其他的分支,也就是说不能再放入其他对象,也是最小遍历单位
golang
Visitor模式
类别
访问数据结构
访问数据结构并处理数据
分类
行为型模式
别名
访问者模式
定义
表示一个作用于某对象结构中的个元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作
本质
预留通路,回调实现
优缺点
优点
良好的扩展性
增加堆数据的操作便捷
复用性高
通过访问者来定义整个对象结构通用的功能,从而提高复用程度
分离无关行为,符合单一职责原则
通过访问者来分离无关行为,把相关的行为封装在一起,构成一个访问者。
缺点
对象结构变化困难
具体元素对访问者公布细节,破坏封装
违背依赖倒置原则
场景
对一个对象结构实施一些依赖于对象结构中具体类的操作
对一个对象结构中的各个元素进行很多不同的而且不相关的操作,为了避免这些操作使得类变得杂乱
对象结构很少变更,但是需要经常给对象结构中的元素对象定义新的操作
UML
角色
Visitor(访问者)
声明访问者可以访问哪些元素,一般为接口或抽象类
ConcreteVisitor(具体的访问者)
实现Visitor角色定义的接口,影响访问者访问到类后的具体操作
Element(元素)
声明接收哪一类访问者访问,一般为接口或者抽象类
ConcreteElement(具体的元素)
实现VElement角色定义的接口。
ObjectStructure(对象结构)
负责处理Element角色集合。ConcreteVisitor角色为每个Element角色都准备了处理方法。,一般容纳在多个不同类、不同接口的容器。
golang
相关模式
装饰模式
功能类似都是在不改变原对象结构的情况下修改原对象的功能。装饰模式更多的而是实现对已有功能的加强、修改和完全实现;访问者模式更多的是实现对象结构添加新的功能。
Mediator模式
类别
简单化
只有一个仲裁者
分类
行为型模式
别名
中介者模式
定义
用一个中介对象来封装一系列的对象交互。中介者使得各个对象不需要显式地相互应用,从而使其松耦合,而且可以独立地改变它们之间的交互。
本质
封装交互
优缺点
优点
松耦合
集中控制交互
多对多变为一对多
缺点
过度集中,会导致中介者对象变得复杂,后续难以维护和管理
场景
一组对象之间交互复杂,导致相互依赖,结构混乱
一个对象引用很多对象,并直接跟这些对象交互,导致难于复用该对象
多个类相互耦合,形成了网状结构。
扩展
简化中介者模式
不定义中介者接口,吧具体中介者对象实现成为单例
同事对象不持有中介者,而是在需要的时候直接获取中介者对象并调用
注意事项
不应当在职责混乱的时候使用。
UML
角色
Mediator(中介者)
定义与Colleague角色进行通信和交互的接口
ConcreteMediator(具体的中介者)
实现Mediator角色定义的接口,负责时间做出决定
Colleague(同事)
定义与Mediator角色进行通信的接口,每一个同事都知道中介者,而且与其他同事角色通信都通过中介者协作
ConcreteColleague(具体的同事)
具体中介者通过协调各同事角色实现协作行为,必须依赖各个同事角色
golang
x相关模式
外观模式
外观模式多用来封装一个系统内部的多个模块,目的是相关子系统外部提供简单易用的接口,即外观模式封装的是子系统外部与子系统内部模块之间的交互。中介在模式是提供多个平等的同事对象之间交互关系的封装,一般在内部实现。
Memento模式
类别
管理状态
保存对象状态
别名
备忘录模式
定义
在不破坏封装性的前提下,捕获一个对象内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
本质
保存和恢复内部状态
优缺点
优点
更好的封装性
简化原发器
窄接口和宽接口
引入窄接口和宽接口使得不同地方,对备忘录对象的访问不一样、窄接口保证了只有原发器可以访问备忘录对象的状态
缺点
导致高开销
场景
需要保存和恢复数据的相关状态场景
提供一个可回滚的操作
需要监控的副本场景
UML
角色
Originator(发起人)
记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据
Memento(备忘录)
存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态
Caretaker(备忘录管理员)
对备忘录进行管理、保存和提供备忘录
Caretaker角色是决定何时拍摄快照,何时撤销以及保存Memento角色。Originator角色职责是生成Memento角色和使用接收Memento角色恢复自身状态。Caretaker角色与Originator角色职责分离。
golang
相关模式
状态模式
备忘录模式使用实例表示状态;状态模式使用类表示状态
Proxy模式
类别
避免浪费
只在必要时生产实例
别名
代理模式
委托模式
定义
为其他对象提供一种代理以控制对这个对象的访问
本质
控制对象访问
通过代理目标对象,把代理对象插入到客户端与目标对象之前,从而为客户端和目标对象引入一定的间接性
类型
远程代理
虚代理
存在复杂“大”对象时,代理到必须创建对象时候,才会真的创建
保护代理
智能指引
优缺点
优点
职责清晰
在客户端与目标对象之间起着一个中介作用和保护对象的作用
高扩展
可以扩展目标对象的功能
低耦合
将客户端与目标对象分离,在一定程度上降低了系统的耦合度
缺点
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢
增加了系统的复杂度
场景
需要为一个对象在不同的地址空间提供局部代表时,使用远程代理
需要按照需要创建开销很大的对象时候使用虚代理
需要控制对象原始对象访问时候,使用保护代理
需要在访问对象执行一些附加操作,使用智能指引代理
为了提高系统的性能,延迟对目标的加载,使用延迟加载
注意事项
和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
UML
角色
Subject(目标接口)
定义代理和具体目标对象的接口,可以在任何使用具体目标对象的地方使用代理对象
Proxy(代理)
实现与具体的目标对象一样的接口,可以使用Proxy角色来代理具体的目标对象
RealSubject(具体的目标)
真正的实现目标接口要求的功能
golang
相关模式
适配器模式
类似
为另一个对象提供间接性访问,并且都是从自身以外的一个接口向这个对象转发请求
不同
适配器模式主要解决接口之间不匹配的问题,通常是为了适配对象提供一个不同的接口;代理模式会实现和目标对象相同的接口
装饰器模式
类似
转调其他对象前后执行一定的功能操作。
不同
装饰模式是为了不生成子类就可以给对象添加职责,也是为了动态增加功能;代理是为了控制对象的访问
Command模式
类型
用类来表现
命令也是类
分类
行为型模式
别名
命令模式
定义
将一个请求封装为一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能
本质
封装请求
优缺点
优点
类间解耦
调用者与接受者之间没有任何依赖关系
可扩展性
缺点
Command子类膨胀
场景
需要抽象出需要执行的动作,并参数化这些对象
需要在不同时刻指定。排列和执行请求
需要支持取消操作
支持命名重放模式
需要事务的系统
扩展
可将命令模式与组合模式联合使用,构成了宏命令模式,亦称为组合命令模式。宏命令包含了一组命令,它充当了具体命令与调用者的双重角色,执行它时将递归调用它所包含的所有命令。
UML
角色
Command(命令)
定义命令的接口
ConcreteCommand(具体的命令)
实现在Command角色中定义的接口
Receiver(接受者)
命令传递到这里被执行
Invoker(调用者)
接收到命令并执行命令
golang
State模式
类型
管理状态
用类表示状态
分类
行为型模式
别名
状态模式
定义
允许一个对象在其内部状态改变时改变其行为。对象看起来似乎修改了它的类
本质
根据状态来分离和选择行为
优缺点
优点
简化应用逻辑控制
避免过多使用条件和分支使用
分离状态和行为
显式化进行状态转换
状态模式为不同的状态引入独立对象,使得状态的转换更加明确。而且状态对象可以保证上下文不会发生内部状态不一致的情况。因为上下文只有一个变量记录状态。
缺点
状态类膨胀
场景
行为随状态改变而改变的场景
条件、分支判断语句的替代者
扩展
如有多个环境对象需要共享一组状态,可以引入享元模式,将这些具体状态对象放到集合中
UML
角色
State(状态)
负责对象状态定义,并且封装环境角色以实现状态切换。一般为接口或抽象类。
ConcreteState(具体的状态)
每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,即本状态下要做的事已经本状态如何过渡到其他状态
Context(上下文)
定义客户端需要的接口,并且负责具体状态的切换
golang
相关模式
观察者模式
在状态发生改变时触发行为,观察者模式的行为是固定的,通知所有观察者;状态模式是根据状态来选择不同的处理。
观察者模式的目的是在被观察者的状态发送改变的时候,触发观察者联动,具体如何处理观察者不关注;状态模式的主要目的在于根据状态来分离和选择行为。
面向对象的设计原则
开闭原则(OCP)
提出者
勃兰特-梅耶于1988年著作《面向对象软件构造》提出
定义
软件实体应当对扩展开放,对修改关闭
作用
对软件测试的影响
软件测试只需要对扩展的代码进行测试即可,原有的测试代码仍旧可以运行
可以提升代码的复用性
粒度越小,被复用的可能性就越大
提高代码的可维护性
实现方法
通过“抽象约束、封装变化”实现。即通过接口或抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装找相同的具体实现类中。
里氏替换原则(LSP)
提出者
里斯科夫女士在1987年的“面向对象技术的高峰会议”发表文章《数据抽象和层次》提出。
定义
继承必须确保超类所拥有的性质在子类中仍然成立
里氏替换原则是开闭原则的补充,是对实现抽象化的具体步骤的规范
作用
里氏替换原则是实现开闭原则的重要方式之一
它克服了继承在哪个重写父类造成的可复用性变差的缺点
它是动作正确性的保障。即类的扩展不会给已有的系统引入新的错误,降低了代码的出错可能性
实现方法
子类可以扩展父类的功能,但不能改变父类原有的功能。即子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
依赖倒置原则(DIP)
提出者
罗伯特-马丁于1996年在C++ Report上发表文章提出
定义
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。
核心就是面向接口编程,不要面向实现编程
依赖倒置原则是实现开闭原则的重要途径,他降低了客户与实现模块之间的耦合
作用
降低类间的耦合性
提高系统的稳定性
减少并行引发的风险
提高代码的可读性和可维护性
实现方法
每个类尽量提供接口或抽象类,或者两者都具备
变量的声明类型尽量是接口或者抽象类
任何类都不应该从具体类派生
使用继承时尽量遵循里氏替换原则
接口隔离原则(ISP)
提出者
罗伯特-马丁于2002年提出
定义
客户端不应该被迫依赖于它不使用的方法
一个类对另一个类的依赖应该建立在最小的接口上
要为各个类建立它们需要的专用接口,而不要视图建立一个庞大的接口供所有依赖它的类去调用
为了约束接口,降低类对接口的依赖
作用
将臃肿庞大的接口分解为多个粒度小的接口,预防外来变更的扩散,提高系统的灵活性和可维护性
接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的偶合性
如果接口粒度大小定义合理,能够保证系统的稳定性;但是如果定义过小,接口数量过多,设计复杂;定义过大,灵活性降低,无法提供定制服务。
使用多个专门的接口还能够体现对象的层次,因为通过接口的继承,实现对中接口的定义
能够减少项目工程的代码冗余
实现方法
接口尽量小,但是要有限度
一个接口只服务于一个子模块或业务逻辑
为依赖接口的类定制服务
了解环境,拒绝盲从
提高内聚,减少对外交互
使接口用最少的方法去完成最多的事
对比
单一职责原则
相同点
为了提高类的内聚性、降低它们之间的偶合性,都体现了封装思想
不同点
单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离
单一职责原则主要约束类,它针对的是程序中的实现和细节‘接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建’
单一职责原则(SRP)
提出者
罗伯特-马丁于2003年在《敏捷软件开发:原则、模式与实践》提出
定义
一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分
核心是控制类的粒度大小、将对象解耦、提高其内聚性
作用
降低类的复杂度
提高类的可读性
提高系统的可维护性
变更引起的风险降低
实现方法
发现类的不同职责并将其分离,再封装到不同的类或模块中
最少知识原则(LKP)
别名
迪米特法则
提出者
1987年伊恩-荷兰在进行迪米特项目研究时提出
定义
如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。
目的是为了降低类之间的耦合度,提高模块的相对独立性
作用
降低了类之间的耦合度,提高了模块的相对独立性
由于耦合度降低,从而提高了类的可复用率和系统的扩展性
实现方法
依赖者
只依赖应该依赖的对象
被依赖着
只暴露应该暴露的方法
合成复用原则(CRP)
别名
组合/聚合复用原则(CARP)
定义
在软件复用时,要尽量使用组合或聚合等关联关系来实现,其次才考虑继承关系来实现
如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则与里氏替换原则相辅相成,都是开闭原则的具体实现规范
作用
组合、聚合维持了类的封装性
新旧类之间的耦合度低
复用的灵活度高
实现方法
通过将已有的对象纳入新对象中,作为新对象的成员来实现的,新对象可调用已有对象的功能,从而达到复用。
总结
开闭原则是总纲,它告诉我们要对扩展开放,对修改关闭;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;单一职责原则告诉我们实现类要职责单一;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合度;合成复用原则告诉我们要优先使用组合或者聚合关系复用,少用继承关系复用。
收藏
0 条评论
下一页