技术iOS架构iOS开发笔记

iOS设计模式(二)-21种设计模式

2018-08-16  本文已影响302人  陵无山

对象创建-Object Creation


1.原型-Prototype

定义
原型模式是一种简单的设计模式,客户端知道抽象Prototype类。在运行时,抽象Prototype子类的任何对象都可以按照客户端的意图来进行复制。因此无需手工创建就可以创造同一类型的多个实例。

类图

原型模式的类图

使用场景
在以下情形,会考虑使用原型模式:

使用原型模式的常见误解是“原型对象应该是一种象征性对象从来不切实用”,从功能的角度上来看,不管什么对象,只要复制自身比手工实例化要好,都可以是原型对象.在以下两种特别常见的情形,我们会考虑使用此Prototype模式:

  1. 有很多相关的类,其行为略有不同,而且主要差异在于内部属性,如名称,图像等
  2. 需要使用组合(树形)对象作为其他东西的基础,例如,使用组合对象作为组件来构建另一个组合对象.
    现实世界中还有许多状况应该应用这一模式.使用设计模式更像艺术行为而非科学行为.打破常规,用于创新,更聪明地工作.

此模式的最低限度是生成对象的真实副本(对象复制,深/浅复制),以作为同一环境下其他相关事物的基础(原型)

2.工厂方法-Factory Method

定义
工厂方法也称为虚构造器(virtual constructor).它适用的情形是"一个类无法预期需要生成那个类的对象,想让其子类来指定所生成的对象".所以工厂方法可以使得一个类的实例化延迟到其子类.

类图

工厂模式类图

使用场景
在以下情形,会考虑使用工厂方法模式:

使用这一模式的最低限度是,工厂方法能给予类在变更返回哪一种对象这一点上更多的灵活性.

3.抽象工厂-Abstract Factory

定义
抽象工厂模式提供一个固定的接口,用于创建一系列有关联或相依存的对象,而不必指定其具体类或其创建的细节.客户端与工厂得到的具体对象之间没有耦合.

类图

抽象工厂模式类图
通过上面类图所示, Client只知道AbstractFactory和AbstractProduct.每个工厂类中,结构与实际操作的细节按黑箱对待.甚至产品也不知道谁将负责创建它们.只有具体的工厂知道为客户端创建什么,如何创建.这个模式有一个有趣点是,很多时候它都是用
工厂方法模式来实现.工厂方法把实际的创建过程推迟到重载它的子类中.在类图中,方法createProductA和createProductB是工厂方法.最初的抽象方法什么也不知道.这种抽象非常通用,广泛用于任何需要抽象创建过程的场合.

抽象工厂模式常与原型模式,单例模式,享元模式等其他一些设计模式一起使用.

使用场景
抽象工厂与工厂方法模式在许多方面都非常相似.很多人常常搞不清应该在什么时候用哪个. 两个模式都用于相同的目的:创建对象而不让客户端知晓返回了什么确切的具体对象.下表为抽象工厂模式与工厂方法模式的对比.

抽象工厂 工厂方法
通过对象组合创建抽象产品 通过类继承创建抽象产品
创建多系列产品 创建一种产品
必须修改父类的接口才能支持新的产品 子类化创建者并重载工厂方法以创建新产品

4.生成器-Builder

定义
有时,构建某些对象有多种不同的方式.如果这些逻辑包含在构建这些对象的类的单一方法中,构建的逻辑会非常荒唐(列如,针对各种构建需求的一大片嵌套if-else或者switch-case语句),如果能够把构建过程分解为"客户-指导者-生成器"(client-director-builder)的关系,那么过程将更容易管理与复用.针对此类关系的设计模式称为生成器.

生成器模式: 将一个复杂对象的构建与它的表现分离,使得同样的构建过程可以创建不同的表现

类图

生成器模式类图 客户端-指导者-生成器之间交互的时序图

使用场景
在以下情形,会考虑使用生成器模式:

生成器和抽象工厂的对比
抽象工厂和生成器模式很相似,然后两者还是有很大的区别的:

表格:生成器模式和抽象工厂模式的主要差异

生成器 抽象工厂
构建复杂对象 构建简单或复杂对象
以多个步骤创建对象 以单一步骤创建对象
以多种方式构建对象 以单一方式构建对象
在构建过程的最后一步返回产品 立刻返回产品
专注一个特定产品 强调一套产品

生成器模式的整体思想是:分离"什么"和"如何",使得aDirector能把同一个"什么"(规格)应用到不同的aBuilder,而它懂得"如何"按照给定的规格构建自己特定的产品.

5.单例-Singleton

定义
面向对象应用程序中的单例类(singleton class)总是返回自己的同一个实例. 它提供了对类的对象的所提供的资源的全局访问点,与这类设计相关的设计模式称为单例模式.

类图

单例模式的静态结构图

使用场景
在以下情形,会考虑使用单例模式:

接口适配-Interface Adaptation

6.适配器-Adapter

定义

类图
按照实现适配器模式的两种方式分为两种

类适配和对象适配器的对比
类适配器与对象适配器是实现适配器模式的不同方式,但是达成的目的相同.设计中采用适配器模式时应该选择哪一种呢? 请看下表:类适配器和对象适配器的特征对比

类适配器 对象适配器
只针对单一的具体Adaptee类,把Adaptee适配到Target 可以适配多个Adaptee及其子类
易于重载Adaptee的行为,因为是通过直接的子类化进行适配 难以重载Adaptee的行为,需要借助于子类的对象而不是Adaptee本身
只有一个Adapter对象,无需额外的指针间接访问Adaptee 需要额外的指针以间接访问Adaptee并适配其行为

提示,OC中委托模式(Delegate)属于对象适配器

使用场景
在以下情形,会考虑使用适配器模式:

适配器模式:将一个类的接口转换成客户希望的另一个接口,适配器模式使得原来由于接口不兼容而不能一起工作的那些类可以一起工作.

7.桥接-Bridge

定义
桥接模式的目的是把抽象层次结构从器实现中分离出来,使其能够独立变更.抽象层定义了供客户端使用的上层的抽象接口.实现层次结构定义了供抽象层次使用的底层接口.实现类的引用被封装于抽象层的实例中时,桥接就形成了.

类图

桥接模式类图

作用

桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化

使用场景
在以下情形,会考虑使用桥接模式:

8.外观-Facade

定义
很多旧的面向对象应用程序中,可能有许多类分散于带有各种功能的系统之中.要把这些类用于某个功能,需要知道全部细节才能在一组算法中使用它们.如果从逻辑上将其中一些类组合成一个简化的接口,可以让这些类更易于使用.为子类系统中一组不同接口提供统一接口的一种方式称为外观模式.

结构图

一个外观实现的示例结构图

例子:乘客打车去目的地,只要发送一个指令"我要去xx地",就可以简化司机开车,开什么车,怎样开车,走什么路线这样一个复杂的过程. 下面是这个列子的使用外观模式实现的类图

出租车例子的类图

使用场景
在以下情形,会考虑使用外观模式:

外观模式:为系统中的一组接口提供一个统一的接口. 外观定义了一个高层接口,让子系统更易于使用.

对象去耦-Decoupling of Objects

9.中介者-Mediator ※※※

定义

中介者模式:用一个对象来封装一系列对象的交互方式. 中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互

类图

一个中介者设计模式例子的类图

运行时中介者模式的一种可能的对象结构图:


对象图显示了运行时中介者模式的对象结构

使用场景
在以下情形,会考虑使用中介者模式:

10.观察者-Observer

定义

观察者模式: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.

类图

观察者类图 显示subject和其他observer之间交互的时序图

使用场景
在以下情形,会考虑使用观察者模式:

抽象集合-Abstract Collection

11.组合-Composite

定义

一个典型的组合对象结构 组合结构模式的类图

组合模式: 将对象组合成树形结构以表示"部分-整体"的层次结构. 组合使得用户对单个对象和组合对象的使用具有一致性

使用场景
在以下情形,会考虑使用组合模式:

12.迭代器-Iterator

定义

类图

说明List与ListIterator之间关系的类图 更进一步表示抽象列表与迭代器之间关系的类图

迭代器: 提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示

外部迭代器与内部迭代器

外部迭代器 内部迭代器
客户端需要知道外部迭代器才能使用,但是它为客户端提供了更多的控制 客户端不需要知道任何外部迭代器,而是可以通过集合对象的特殊接口,或者一次访问一个元素,或者向集合中的每个元素发送消息
客户端创建并维护外部迭代器 集合对象本身创建并维护它的内部迭代器
客户端可以使用不同的外部迭代器实现多种类型的遍历 集合对象可以在不修改客户端代码的情况下,选择不同的内部迭代器

使用场景
在以下情形,会考虑使用迭代器模式:

行为扩展-Behavioral Extension

13.访问者-Visitor

定义
在软件设计中,如果架构师为了扩展类的功能而往一个类里塞进了太多方法,类就会变得极为复杂.更好的做法是创建外部的类来扩展它,而对原始代码不做太多改动.

类图

显示访问者模式的静态结构的类图 承包商例子的类图,其中承包商是访问者

访问者模式的这个承包商版本,反映了从房屋承包商的例子角度的一种实现.

访问者模式:表示一个作用于对某象结构中的各元素的操作,它让我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作

使用场景
在以下情形,会考虑使用访问者模式:

14.装饰-Decorator

定义

在面向对象软件设计中,向对象添加"东西"(行为),而不破坏其原有风格,因此增强了的对象是同一类的加强版(如带相框的照片).任何"增强"(相框)均可以动态添加和删除,我们把这种设计模式叫做"装饰",装饰对象可以附加到另一个装饰对象,也可以附加到原始对象,对其功能进行扩展,同时保留原始行为不受影响.

类图

装饰模式类图 由装饰器扩展的功能

使用场景
在以下情形,会考虑使用装饰模式:

装饰模式: 动态地给一个对象添加一些额外的职责. 就扩展功能来说,装饰模式相比生成子类更为灵活.

改变对象的"外表"和"内容"

15.责任链-Chain of Responsibility

定义

责任链模式的类图 运行时的请求处理程序链的一种典型结构

使用场景
在以下情形,会考虑使用责任链模式:

责任链模式: 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间发生耦合. 此模式将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止

算法封装-Algorithm Encapsulation

16.模板方法-Template Method

定义
模板方法模式是面向对象软件设计中一种非常简单的设计模式.其基本思想是在抽象类的一个方法中定义"标准"算法.在这个方法中调用的基本操作应由子类重载予以实现.这个方法被称为"模板",因为方法定义了的算法缺少一些特有的操作.

模板方法模式: 定义一个操作中算法的骨架,而将一些步骤延迟到子类中.模板方法使子类可以重定义算法的某些特定步骤而不改变该算法的结构.

类图

模板方法模式的类图

因为方法定义的算法缺少一些特有的操作.上图显示了抽象类与具体子类之间的关系----抽象类定义模板,子类重载基本操作以提供独特操作模板方法调用.

使用场景
在以下情形,会考虑使用模板方法:

钩子操作给出了默认行为,子类可对其扩展. 默认行为通常什么都不做.子类可以重载这个方法,为模板算法提供附加的操作.
模板方法模式中的控制结构流程是倒转的,因为父类的模板方法调用其子类的操作,而不是子类调用父类的操作.

模板方法会调用5种类型的操作:

17.策略-Strategy

定义

策略模式: 定义一系列算法,把它们一个个封装起来,并且使它们可相互替换.该模式使得算法可以独立与使用它的客户端而变化.

类图

策略模式的结构类图
模型-视图-控制器中的策略模式
模型-视图-控制器模式中,控制器决定视图对模型数据进行显示的时机和内容. 视图本身知道如何绘图,但需要控制器告诉它要显示的内容. 同一个视图如果与不同的控制合作,数据的输出格式可能一样,但数据 的类型和格式可能随不同控制器的不同输出而不同.因此这种情况下的控制器是视图的策略.在前面的几节提到过,控制器与视图之间是一种基于策略模式的关系.

使用场景
在以下情形,会考虑使用策略模式:

18.命令-Command

定义

命令模式: 将请求封装为一个对象,从而可用不同的请求对客户进行参数化,对请求排对或记录请求日志,以及支持可撤销的操作

类图

展示命令模式结构的类图

使用场景
在以下情形,会考虑使用命令模式:

性能与对象访问-Performance and Object Access

19.享元-Flyweight

定义
在面向对象软件设计中,利用公共对象不仅能节省资源还能提高性能. 比方说,某个任务需要一个类的一百万个实例,但我们可以把这个类的一个实例让大家共享,而把某些独特的信息放在外部,节省的资源可能相当可观(一个实例与一百万个实例的差别).共享的对象只提供某些内在的信息,而不能用来识别对象.专门用于设计可共享对象的一种设计模式叫做享元模式(Flyweight pattern)

享元模式: 运用共享技术有效地支持大量细粒度的对象

类图

享元模式类图 运行时享元对象如何共享的对象图

使用场景
在以下情形,会考虑使用享元模式:

20.代理-Proxy

定义
有以下几种代理:

代理模式:为其他对象提供一种代理以控制对这个对象的访问,通常,代理是一种替代或者占位,它控制对另一些对象的访问,而这些对象可能是远程对象,创建开销较大的对象,或者是对安全性要求高的对象

类图

代理模式类图 代理模式在运行可能的一种对象结构

使用场景
在以下情形,会考虑使用代理模式:

对象状态-State of Object

21.备忘录-Memento

定义
在响应某些事件时, 应用程序需要保存自身的状态,比如当用户保存文档或程序退出时.例如,游戏退出之前,可能需要保存当前会话的状态,如游戏等级,敌人数量,可用武器的种类等.游戏再次打开时,玩家可用从离开的地方接着玩.很多时候,保存程序的状态真的不需要什么特别奇妙的方法.任何简单有效的方法都可以,但是同时,保存信息应该只对原始程序有意义.原始程序应该是能够解码它所保存文档中的信息的唯一实体.这就是备忘录模式应用于游戏/文字处理等程序的软件设计中的方式,这些程序需要保存当前上下文的复杂状态的快照并在以后恢复.

备忘录模式: 在不破坏封装的前提下,捕获一个对象内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态

类图

备忘录模式结构类图

这个模式中有三个关键角色:原发器(originator)/备忘录(memento)和看管人(caretaker).其思想非常简单,原发器创建一个包含其状态的备忘录,并传给看管人.看管人不知如何与备忘录交互,但会把备忘录放在安全之处保管好

备忘录模式的时序图

使用场景
在以下情形,会考虑使用备忘录模式:

上一篇下一篇

猜你喜欢

热点阅读