# design_pattern_learning **Repository Path**: hongmiaoliao/design_pattern_learning ## Basic Information - **Project Name**: design_pattern_learning - **Description**: 设计模式学习,将《研磨设计模式》中的Java代码改为我目前工作需要用的golang的代码 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-08-11 - **Last Updated**: 2024-12-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 研磨设计模式 [toc] ## 一、设计模式 设计模式:是指在软件开发中,经过验证的,用于解决在特定环境下,重复出现的、特定问题的解决方案。 1. 理解功能、结构、标准实现 2. 实际开发使用 3. 回头感悟思考 4. 重复2、3步 理论指导实践、实践反过来加深对理论的理解 ## 二、简单工厂(Simple Factory) 提供一个创建对象实例的功能,而无须关系具体实现。被创建实例的类型可以是接口、抽象类,也可以是具体的类 优点: 1. 帮助封装 2. 解耦 缺点: 1. 可能增加客户端复杂度 2. 不方便扩展子工厂 ***本质:选择实现*** 何时选用: * 想要完全封装隔离具体实现,让外部只能通过接口来操作封装体 * 想要把对外创建对象的职责集中管理和控制 ## 三、外观模式(Facade) 为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用 优点: 1. 松散耦合 2. 简单易用 3. 更好地划分访问的层次 缺点: 1. 过多或不合理的Facade容易让人迷惑 ***本质:封装交互,简化调用*** 何时选用: * 希望为一个复杂的子系统提供一个简单接口的时候 * 想要让客户程序和抽象类的实现部分松散耦合 * 构建多层结构的系统 ## 四、适配器模式(Adapter) 将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作 优点: 1. 更好的复用性 2. 更好的扩展性 缺点: 1. 过多使用会让系统非常凌乱,不容易整体把握 ***本质:转换匹配,复用功能*** 何时选用: * 想要使用一个已经存在的类,但是它的接口不符合你的需求 * 想创建一个可以复用的类,这个类可能和一些不兼容的类一起工作 * 想使用一些已经存在的子类,但是不可能对每一个子类都进行适配 ## 五、单例模式(Singleton) 保证一个类仅有一个实例,并提供一个访问它的全局访问点 优缺点: 1. 懒汉式是时间换空间,饿汉式是空间换时间 2. 不加同步的懒汉式是线程不安全的(可使用双重检查加锁实现),饿汉式是线程安全的 ***本质:控制实例数目*** 何时选用: * 需要控制一个类的实例只能有一个,而且客户只能从一个全局访问点访问它时 ## 六、工厂方法模式(Factory Method) 定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到子类 优点: 1. 可以在不知具体实现的情况编程 2. 更容易扩展对象的新版本 3. 连接平行的类层次 缺点: 1. 具体产品对象和工厂方法的耦合性 ***本质:延迟到子类来选择实现*** 何时选用: * 一个类需要创建某个接口的对象,但是又不知道具体的实现 * 一个类本身就希望由它的子类来创建所需要的对象的时候 ## 七、抽象工厂模式(Abstract Factory) 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类 优点: 1. 分离接口和实现 2. 使得切换产品簇变得容易 缺点: 1. 不太容易扩展新的产品 2. 容易造成类层次复杂 ***本质:选择产品簇的实现*** 何时选用: * 希望一个系统独立于它的产品的创建、组合和表示的时候 * 一个系统要由多个产品系列中的一个来配置的时候 * 要强调一系列相关产品的接口,以便联合使用它们的时候 ## 八、生成器模式(Builder) 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 优点: 1. 松散耦合 2. 可以很容易地改变产品的内部表示 3. 更好的复用性 缺点: ***本质:分离整体构建算法和部件构造*** 何时选用: * 创建对象的算法,应该独立于该对象的组成部分以及它们的装配方式时 * 同一个构建过程有着不同的表示时 ## 九、原型模式(Prototype) 通过原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。 优点: 1. 对客户端隐藏具体的实现类型 2. 在运行时动态改变具体的实现类型 缺点: 1. 每个原型的子类都必须实现clone的操作,尤其在包含引用类型的对象时,clone方法会比较麻烦 ***本质:克隆生成对象*** 何时选用: * 一个系统想要独立于它想要使用的对象时 * 需要实例化的类是在运行时刻动态指定时 ## 十、中介者模式(Mediator) 用一个中介对象来封装一系列的对象交互。中介者使得各对象不需要显示地相互引用,从而使其耦合松散,可以独立地改变它们之间的交互 优点: 1. 松散耦合 2. 集中控制交互 3. 多对多变成一对多 缺点: 1. 过度集中化 ***本质:封装交互*** 何时选用: * 一组对象之间的通信方式比较复杂,导致相互依赖、结构混乱 * 一个对象引用很多的对象,并直接跟这些对象交互,导致难以复用该对象 ## 十一、代理模式(Proxy) 为其他对象提供一种代理以控制对这个对象的访问 特点: 1. 远程代理:隐藏了一个对象存在于不同的地址空间的事实,也即是客户通过远程代理去访问一个对象 2. 虚代理:可以根据需求来创建“大”对象,只有到必须创建对象的时候,虚代理CIA会创建对象,从而大大加快程序运行速度,并节省资源 3. 保护代理:可以在访问一个对象前后,执行很多附加的操作(权限控制,业务相关处理,增加功能等) 4. 智能指引:和保护代理类似,允许访问一个对象前后,执行附加操作(如引用计数) ***本质:控制对象的访问*** 何时选用: * 需要为一个对象在不同的地址空间提供局部代表时,可用远程代理 * 需要按照需求创建开销很大的对象时,可用虚代理 * 需要控制对原始对象的访问时,可用保护代理 * 需要在访问对象执行一些附加操作时,可以使用智能指引代理 ## 十二、观察者模式(Obaserver) 定义对象间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新 优点: 1. 实现了观察者和目标之间的抽象耦合 2. 实现类动态联动 3. 支持广播通信 缺点: 1. 可能会引起无谓的操作 ***本质:触发联动*** 何时选用: * 当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化 * 如果在更改一个对象的时候,需要同时连带改变其他对象,而且不知道究竟应该有多少对象需要被连带改变 * 当一个对象必须通知其他的对象,但是你又希望这个对象和其他被它通知的对象是松散耦合的 ## 十三、命令模式(Command) 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作 优点: 1. 更松散的耦合 2. 更动态的控制 3. 很自然的复合命令 4. 更好的扩展性 ***本质:封装请求*** 何时选用: * 需要抽象出需要执行的动作,并参数化这些对象 * 需要在不同的时刻指定、排列和执行请求 * 需要支持取消操作 * 需要支持系统崩溃时,能将系统的操作功能重新执行一遍 * 需要事务的系统 ## 十四、迭代器模式(Iterator) 提供一种方法顺序访问一个聚合对象中的各个元素,而又不需暴露该对象的内部表示 优点: 1. 更好的封装性 2. 可以以不同的遍历方式来遍历一个聚合 3. 可以使聚合对象的内容和具体的迭代算法分离开 4. 迭代器简化了聚合的接口 5. 简化客户端调用 ***本质:控制访问聚合对象中的元素*** 何时选用: * 希望提供访问一个聚合对象的内容,但是又不想暴露它的内部表示的时候 * 希望有多重遍历方式可以访问聚合对象 * 希望为遍历不同的聚合对象提供一个统一的接口 ## 十五、组合模式(Composite) 将对象组合成树型结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性 优点: 1. 定义了包含基本对象和组合对象的类层次结构 2. 统一了组合对象和叶子对象 3. 简化了客户端调用 4. 更容易扩展 缺点: 1. 很难限制组合中的组件类型 ***本质:统一叶子对象和组合对象*** 何时选用: * 想表示对象的部分--整体层次结构 * 希望统一地使用组合结构中的所有对象 ## 十六、模板方法模式(Template Method) 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 优点: 1. 实现代码复用 缺点: 1. 算法骨架不容易升级 ***本质:固定算法骨架*** 何时选用: * 需要固定定义算法骨架,实现一个算法的不变部分,并把可变的行为留给子类来实现的情况 * 各个子类中具有公共行为,应该抽取出来 * 需要控制子类扩展的情况 ## 十七、策略模式(Strategy) 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化 优点: 1. 定义一系列算法 2. 避免多重条件语句 3. 更好的扩展性 缺点: 1. 客户必须了解每种策略的不同 2. 增加了对象数目 3. 只适合扁平的算法结构 ***本质:分离算法,选择实现*** 何时选用: * 出现有许多相关的类,仅仅是行为有差别的情况下,可以使用策略模式来使用多个行为中的一个来配置一个类的方法,实现算法动态切换 * 出现同一个算法,有很多不同实现的情况下,可以使用策略模式来把这些“不同的实现”实现成为一个算法的类层次 * 需要封装算法中,有与算法相关数据的情况,可以使用策略模式来避免暴露这些跟算法相关的数据结构 * 出现抽象一个定义了很多行为的类,并且是通过多个if-else语句来选择这些行为的情况下,可以使用策略模式来代替这些条件语句 ## 十八、状态模式(State) 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。 优点: 1. 简化应用逻辑控制 2. 更好地分离状态和行为 3. 更好的扩展性 4. 显式化进行状态转换 缺点: 1. 一个状态对应一个状态处理类,会使得程序引入太多的状态类,使程序变得杂乱 ***本质:更具状态来分离和选择行为*** 何时选用: * 如果一个对象的行为取决于它的状态,而且它必须在运行时刻根据状态来改变它的行为,可以使用状态模式,来把状态和行为分离开 * 如果一个操作中含有庞大的多分支语句,而且这些分支依赖于该对象的状态,可以使用状态模式,把各个分支的处理分散包装到单独的对象处理类中 ## 十九、备忘录模式(Memento) 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。 优点: 1. 更好的封装性 2. 简化了原发器 3. 窄接口和宽接口 缺点: 1. 可能导致高开销 ***本质:保存和恢复内部状态*** 何时选用: * 必须保存一个对象在某一个时刻的全部或者部分状态,方便在以后需要的时候,可以把改对象恢复到先前的状态 * 需要保存一个对象的内部状态,但是如果用接口来让其他对象直接得到这些需要保存的状态,将会暴露对象的实现细节并破坏封装性 ## 二十、享元模式(Flyweight) 运用共享技术有效地支持大量细粒度的对象 优点: 1. 减少对象数量,节省内存空间 缺点: 1. 维护共享对象,需要额外开销 ***本质:分离与共享*** 何时选用: * 一个应用使用了大量的细粒度对象,可用享元模式减少对象数量 * 由于使用大量的对象,造成很大的存储开销,可用享元模式减少对象数量,节约内存 * 对象的大多数状态都可以转变为外部状态,或是从外部传入等,可用享元模式来实现内部状态和外部状态的分离 * 不考虑对象的外部状态,可以用相对较少的共享对象取代很多组合对象 ## 二十一、解释器模式(Interpreter) 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子 优点: 1. 易于实现语法 2. 易于扩展新的语法 缺点: 1. 不适合复杂的语法 ***本质:分离实现,解释执行*** 何时选用: * 当一个语言需要解释执行,并且可以将该语句中的句子表示为一个抽象语法树的时候 * 需要考虑:语法相对应该比较简单,效率要求不是很高 ## 二十二、装饰模式(Decorator) 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更为灵活 优点: 1. 比继承更加灵活 2. 更容易复用功能 3. 简化高层定义 缺点: 1. 会产生很多细粒度对象 ***本质:动态组合*** 何时选用: * 需要在不影响其他对象的情况下,以动态、透明的方式给对象添加职责 * 如果不适合使用子类来进行扩展的时候,可以考虑使用装饰模式 ## 二十三、职责链模式(Chain of Responsibility) 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止 优点: 1. 请求者和接收者松散耦合 2. 动态组合职责 缺点: 1. 产生很多细粒度对象 2. 不一定能被处理 ***本质:分离职责,动态组合*** 何时选用: * 有多个对象可以处理同一个请求,但是具体由哪个对象来处理该请求,是运行时刻动态确定的 * 想在不明确指定接收者的情况下,向多个对象中的一个提交请求 * 想要动态指定处理一个请求的对象集合 ## 二十四、桥接模式(Bridge) 将抽象部分与它的实现部分分离,使它们都可以独立地变化 优点: 1. 分离抽象和实现部分 2. 更好的扩展性 3. 可动态地切换实现 4. 可减少子类的个数 ***本质:分离抽象和实现*** 何时选用: * 不希望在抽象部分和实现部分采用固定的绑定关系 * 出现抽象部分和实现部分都能够扩展的情况 * 希望实现部分的修改不会对客户产生影响 * 如果采用继承的实现方案,会导致产生很多子类 ## 二十五、访问者模式(Visitor) 表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作 优点: 1. 好的扩展性 2. 好的复用性 3. 分离无关行为 缺点: 1. 对象结构变化很困难 2. 破坏封装 ***本质:预留通路,回调实现*** 何时选用: * 想对一个对象结构实施一些依赖于对象结构中具体类的操作 * 想对一个对象结构中的各个元素进行很多不同的而且不相关的操作 * 对象结构很少变动,但是需要经常给对象结构中的元素对象定义新的操作 ## 附录 常见面向对象设计原则(尽量但不需要教条般完全准守) 1. 单一职责原则SRP(Single Responsibility Principle) * 一个类应该仅有一个引起它变化的原因 2. 开放-封闭原则OCP(Open-Closed Principle) * 一个类应该对扩展开放,对修改封闭 3. 里氏替换原则LSP(Liskov Substitution Principle) * 子类型必须能够替换掉它们的父类型 4. 依赖倒置原则(Dependence Inversion Principle) * 要依赖于抽象,不依赖于具体类 * 高层模块不应该依赖于底层模块,二者都应该依赖于抽象 * 抽象不应该依赖于具体实现,具体实现应该依赖于抽象 5. 接口隔离原则ISP(Interface Segregation Principle) * 不应该强迫客户依赖于他们不用的方法 6. 最少知识原则LKP(Least Knowledge Principle) * 应该尽量减少对象之间的交换 7. 其他原则 1. 面向接口编程 2. 优先使用组合,而非继承