收藏work技术类

设计模式总结(C++和Python实现)

2017-01-03  本文已影响1629人  尤汐Yogy
Design Patterns

前言

GoF的23种设计模式,包括创建型、结构型和行为型,其涵盖了面向对象思想的精髓以及诸多细节。本文结合《设计模式》和《大话设计模式》,并用C++和Python实现了《大话设计模式》中的23种模式案例。原文首发于个人博客Jennica.Space

案例实现

创建型模式

工厂方法模式(Factory Method)

  1. 工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪个类。
  2. 工厂方法把简单工厂的内部判断逻辑移到了客户端代码,本来需要修改工厂类,现在是修改客户端。
  3. 简单工厂模式违背了开放-封闭原则,工厂方法模式借助多态,克服了该缺点,却保持了封装对象创建过程的优点。

抽象工厂模式(Abstract Factory)

  1. 抽象工厂模式:提供一个创建一系列相关或互相依赖对象的接口,只需要知道对象的系列,无需知道具体的对象。
  2. 在客户端中,具体工厂类只在初始化时出现一次,更改产品系列即可使用不同产品配置。
  3. 利用简单工厂类替换抽象工厂类及其子类,可以使客户端不再受不同系列的影响。
    结合反射机制,Assembly.Load(“程序集名称”).CreateInstance(“命名空间”.“类名”),可以直接通过字符串创建对应类的实例。所有在简单工厂中,都可以通过反射去除switch或if,解除分支判断带来的耦合。
  4. 反射中使用的字符串可以通过配置文件传入,避免更改代码。

单例模式(Singleton)

  1. 单例模式:让类自身保证它只有一个实例,并提供一个全局访问点。
  2. 多线程下单例模式可能失效,需要采取双重锁定的的方式,确保被锁定的代码同一时刻只被一个进程访问。
  3. 饿汉式单例:即静态初始化方式,在类初始化时产生私有单例对象,会提前占用资源;渴汉式单例:在第一次被引用时将自己初始化,会产生多线程访问安全问题,需要添加双重锁定。

建造者模式(Builder)

  1. 建造者模式:将复杂对象的创建与表示分开,使得相同的创建过程可以有不同的表示。用户只需制定需要建造的类型,不需要知道建造的过程和细节。
  2. 指挥者是建造者模式中重要的类,用于控制建造过程,也可以隔离用户与建造过程的关联。
  3. 建造者隐藏了产品的组装细节,若需要改变一个产品的内部表示,可以再定义一个具体的建造者。
  4. 建造者模式是在当前创造复杂对象的算法,独立于该对象的组成部分和装配方式时适用的模式。

原型模式(Prototype)

  1. 原型模式:用原型实例指定创建对象的种类,并通过拷贝这些原型创建对象。本质是从一个对象再创建另一个可定制的对象,并且不需要知道创建细节。
  2. 原型抽象类的关键是有一个Clone()方法,原型具体类中复写Clone()创建当前对象的浅表副本。
  3. 对.Net而言,由于拷贝太常用原型抽象类并不需要,在System命名空间中提供了ICloneable接口,其中唯一的方法就是Clone(),只要实现这个接口就可以完成原型模式。
  4. 原型拷贝无需重新初始化对象,动态获取对象的运行状态。既隐藏了对象创建的细节,又提升性能。
  5. 在具体原型类中,MemberwiseClone()方法是浅拷贝,对值类型字段诸位拷贝,对引用类型只复制引用但不会把具体的对象值拷贝过来。
  6. 比起浅拷贝,深拷贝把引用对象的变量指向新对象,而不是原被引用的对象。对于需要深拷贝的每一层,都需要实现ICloneable原型模式。
  7. 数据集对象DataSet,Clone()是浅拷贝,Copy()是深拷贝。

结构型模式

代理模式(Proxy)

  1. 代理模式:为其他对象提供一种代理以控制对这个对象的访问。实际上是在访问对象时引入一定程度的间接性。
  2. 远程代理:为一个对象在不同地址空间提供局部代表,隐藏一个对象存在于不同空间的事实。如.Net加入Web引用,引入WebService,此时项目会生成WebReference的文件夹,就是代理。
  3. 虚拟代理:根据需要创建开销很大的对象,通过它存放实例化需很长时间的真实对象。HTML中的多图,就是通过虚拟代理代替了真实图片,存储路径和尺寸。
  4. 安全代理:控制真实对象的访问权限,用于对象应该拥有不同的访问权限时。
  5. 智能指引:当调用真实对象时,代理处理一些另外的事情。通过代理在访问对象时增加一些内务处理。

适配器模式(Adapter)

  1. 适配器模式:当系统数据和行为都一致,只有接口不符合时,将一个类的接口转化为客户端期望的另一个接口。
  2. 适配器模式用于服用一些现存的类,常用在第三方接口或软件开发后期双方都不易修改的时候。
  3. 在.Net中DataAdapter是用于DataSet和数据源间的适配器,Fill更改DataSet适配数据源,Update更改数据源适配DataSet。

外观模式(Facade)

  1. 外观模式:为子系统中一组接口提供一个一致的界面,即定义一个高层接口,增加子系统的易用性。
  2. 外观模式完美体现了依赖倒转原则和迪米特法则。
  3. 设计初期阶段,在MVC三层架构中,任意两层间建立外观Facade。
  4. 子系统会因不断演化变得复杂,增加外观Facade提供简单简单接口减少依赖。
  5. 在维护一个大的遗留系统时,新的开发又必须依赖其部分功能。此时,开发一个外观Facade类,从老系统中抽象出比较清晰的简单接口。让新系统只与Facade交互,而Facade与遗留代码交互所有的工作。

装饰模式(Decorator)

  1. 装饰模式:动态的给一个对象添加一些额外的职能,把所需功能按顺序串联起来并进行控制。
  2. 每个要装饰的功能放在单独的类中,并让这个类包装它所要修饰的对象。当需要执行特殊行为时,客户端就可以根据需要有选择的、有顺序的使用装饰功能包装对象了。
  3. 装饰模式有效的把类的核心职能和装饰功能区分开了,并且可以去除相关类中重复的装饰逻辑。

桥接模式(Bridge)

  1. 对象的继承关系编译时已确定,所以无法在运行时修改从父类继承的实现。由于紧耦合,父类中任何的改变必然会导致子类发生变化。当需要复用子类,但继承下来的方法不合适时,必须重写父类或用其他类替代。这种依赖性限制了灵活性和复用性。
  2. 合成/聚合复用原则:尽量使用合成和聚合而不是继承。可以保证每个类封装集中在单个任务上,不会出现规模太大的类及继承结构。
  3. 桥接模式:抽象类和其派生类分离,各自实现自己的对象。若系统可以从多角度分类,且每种分类都可能变化,则把多角度分离独立出来,降低耦合。

享元模式(Flyweight)

  1. 享元模式:运用共享技术有效支持大量细粒度对象。
  2. 在享元模式对象内部不随环境改变的共享部分是内部状态,不可共享需要通过调用传递进来的参数是外部状态。
  3. 使用享元模式的场景包括,一个应用程序产生了大量的实例对象,占用了大量内存开销;或对象的大多数状态为外部状态,删除内部状态后可以用较少的共享对象来取代组对象。
  4. 应用场景有正则表达式、浏览器、机器人指令集等。

组合模式(Composite)

  1. 组合模式:将对象的组合以树形的层次结构表示,对单个对象和组合结构的操作具有一致性。
  2. 透明方法:叶子和分枝对外接口无差别;安全方法:分枝具有添加删除叶子的接口,低层抽象接口和叶子没有。
  3. 基本对象组合成组合,组合又可以被组合,不断递归下去,在任何用到基本对象的地方都可以使用组合对象。

行为型模式

职责链模式(Chain of Responsibility)

  1. 职责链模式:使多个对象都有机会处理请求,解除请求发送者和接收者的耦合。将对象连成一条链,并沿这条链传递请求直到请求被解决。
  2. 请求交付给最小接受者,职责链中每一环保存后继的引用,使得请求有序沿链传递。
    通过合理设置后继以及分支关系,避免一个请求到了链末端依旧无法被处理,或因配置错误得不到处理的情况。

策略模式(Strategy)

  1. 面向对象中并非类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。
  2. 策略模式:定义算法家族并分别封装,他们完成的工作相同,只是实现不同,可以互相替换。继承有助于析取这些算法的公共功能。此模式让算法的变化不会影响到使用算法的用户。
  3. 策略与工厂模式结合,使客户端需要认识的类减少,耦合度更加降低。
  4. 策略模式可以简化单元测试,因为每个算法可以通过自己的接口单独测试。
  5. 只要在不同时间内应用不同的业务规则,就可以考虑用策略模式来处理这种变化的可能性。

状态模式(State)

  1. 拥有过多分支的过长方法违背了单一职责原则,而且当需求变化时修改代码往往会违背开放-封闭原则,应该将分支变成一不同小类,将状态的判断逻辑转移到小类中。
  2. 状态模式:一个对象可能拥有多种状态,当内在状态改变时允许改变行为。
  3. 状态模式的好处是将与特定状态有关的行为局部化,并将不同状态的行为分隔开。

观察者模式(Observer)

  1. 观察者模式:多个观察者对象同时监听某一主题(通知者)对象,当该主题对象状态变化时会通知所有观察者对象,使它们能更新自己。
  2. 具体观察者保存一个指向具体主题对象的引用,抽象主题保存一个抽象观察者的引用集合,提供一个可以添加或删除观察者的接口。
  3. 抽象模式中有两方面,一方面依赖另一方面,使用观察者模式可将两者独立封装,解除耦合。
  4. 观察者模式让主题和观察者双方都依赖于抽象接口,而不依赖于具体。
  5. 委托就是一种引用方法类型。委托可看作函数的类,委托的实例代表具体函数。在主题对象内声明委托,不再依赖抽象观察者。
    一个委托可以搭载多个相同原形和形式(参数和返回值)的方法,这些方法不需要属于一个类,且被依次唤醒。

迭代器模式(Iterator)

  1. 迭代器模式:提供一种方法顺序遍历一个聚集对象,为不同的聚集结构提供遍历所需接口,而不暴露对象内部的表示。
  2. 在高级编程语言如c#、c++、java等,都已经把迭代器模式设计进语言的一部分。
  3. 迭代器模式分离了对象的遍历行为,既不暴露内部结构又可以让外部代码透明的访问集合内部的数据。

备忘录模式(Memento)

  1. 备忘录模式:不破坏封装,获取对象内部状态并在其之外保存该对象,以便其未来恢复到当前状态。
  2. Orginator负责创建Memento,Memento封装Originator状态细节,Caretaker负责保管和交付Memento。
  3. 备忘录模式适用于需要维护历史状态的对象,或只需要保存原类属性中的小部分。

命令模式(Command)

  1. 命令模式:将请求分装为对象,将请求和执行分开,可以用不同的请求对客户参数化。可以对请求排队、通过或否决、记录日志、撤销或重做。
  2. 基于敏捷开发原则,不要给代码添加基于猜测而实际不需要的功能,在需要的时候通过重构实现。

模板方法模式(Template Method)

  1. 模板方法模式:定义一个操作中的算法框架,将一些步骤延迟到子类中。子类在不改变框架的前提下就可以重新定义某些特定步骤。
  2. 当不变和可变的行为在子类中混到一起时,可以通过把重复的行为移到同一地方,帮助子类摆脱重复不变行为的纠缠。

中介者模式(Mediator)

  1. 中介者模式:用一个中介对象来封装一系列对象间的交互。
  2. 中介者模式在系统中易用也容易被误用,当系统中出现了多对多的交互复杂的对象群时,更应考虑设计的问题。
  3. 由于控制集中化,中介者模式将交互复杂性变成了中介者的复杂性,中介者类会比任何一个同事类都复杂。
  4. 中介者模式应用的场合有,一组对象以定义良好但复杂的方式进行通信,以及想定制一个分布在多个类中的行为却不想产生太多子类。

解释器模式(Interpreter)

  1. 解释器模式:给定一种语言,定义它文法的一种表示,再定义一个解释器,使用该表示来解释语言中的句子。
  2. 如果一种特定类型发生的频率足够高,就可以将其实例表达为一个句子,构建解释器来解析。
  3. 解释器模式就是用“迷你语言”来表现程序要解决的问题,将句子抽象为语法树。由于各个节电的类大体相同,便于修改、扩展和实现。
  4. 解释器为文法中的每条规则定义了一个类,当文法过多时将难以维护,建议使用其他技术如语法分析程序或编译器生成器处理。

访问者模式(Visitor)

  1. 访问者模式:在不改变各元素的前提下定义作用于这些类的新的操作。
  2. 访问者模式使用双分派,将数据结构和作用于结构上的操作解耦,意味着执行的操作决定于请求的种类和接收者的状态。
  3. 如果系统具有较为稳定的数据结构,又有易于变化的算法操作,则适合使用访问者模式。

对比总结

上一篇下一篇

猜你喜欢

热点阅读