抽象类使用工厂方法创建其私有子类或任何其他类的对象金沙js娱乐场官方网站:,并通过复制这个原型创建新的对象

首先先讲一下大概的设计模式的分类,Demo在这里,通过 copy 操作创建了 array2,并通过复制这个原型创建新的对象,通过不同的工厂类来创建不同的产品实例,Product 就是工厂方法,通过 copy 操作创建了 array2,并通过复制这个原型创建新的对象,工厂方法的最初定义好像是专注于让子类决定创建什么对象,抽象类使用工厂方法创建其私有子类或任何其他类的对象

金沙js娱乐场官方网站 28

个人博客地址:Lixuzong’s Blog

对象创建

使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象。

对象创建

背景: 深入学习一下 iOS
的设计模式,虽然都老生常谈了,但是这方面确实是自己的学习盲区,遂抽空来继续深入研究一下吧

文章目录
  • 1 对象创建
    • 1.1原型(Prototype)
    • 1.2工厂方法(Factory Method)
    • 1.3抽象工厂(Abstract Factory)
    • 1.4生成器(Builder)
    • 1.5单例(Singleton)
  • 2 接口适配
    • 2.1 适配器(Adapter)
    • 2.2 桥接(Bridge)
    • 2.3 外观(Facade)
  • 3 对象去耦
    • 3.1 中介者(Mediator)
    • 3.2 观察者(Observer)
  • 4 抽象集合
    • 4.1 组合(Composite)
    • 4.2 迭代器(Iterator)
  • 5 行为扩展
    • 5.1 访问者(Visitor)
    • 5.2 装饰(Decorator)
    • 5.3 责任链(Chain of Responsibility)
  • 6 算法封装
    • 6.1 模板方法(Template Method)
    • 6.2 策略(Strategy)
    • 6.3 命令(Command)
  • 7 性能与对象访问
    • 7.1 享元(Flyweight)
    • 7.2 代理(Proxy)
  • 8 对象状态
    • 8.1 备忘录(Memento)

大部分的Demo可以在这里查看:Demo在这里

原型(Prototype)

 NSArray *array = [[NSArray alloc] initWithObjects:@1, nil]; NSArray *array2 = array.copy;

原型(Prototype)

1 对象创建

首先先讲一下大概的设计模式的分类。

使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象。

array 就是原型了,array2 以 array 为原型,通过 copy 操作创建了 array2。

使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象。

1.1 原型(Prototype)

定义:使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象。

    NSArray *array = [[NSArray alloc] initWithObjects:@1, nil];
    NSArray *array2 = array.copy;

array 就是原型了,array2 以 array 为原型,通过 copy 操作创建了
array2。
当创建的实例非常复杂且耗时,或者新实例和已存在的实例值相同,使用原型模式去复制已经存在的实例效率更高。

  • 1、对象创建,主要是生成对象的方法。
  • 2、接口适配,为了解决两个类之间接口不吻合
  • 3、对象去耦,对象去耦合有利于代码的复用
  • 4、抽象集合,将要用的组合抽离出来,主要有组合模式
  • 5、行为扩展,在已有功能的基础上扩展功能
  • 6、算法封装

NSArray *array = [[NSArray alloc] initWithObjects:@1, nil];

当创建的实例非常复杂且耗时,或者新实例和已存在的实例值相同,使用原型模式去复制已经存在的实例效率更高。

NSArray*array = [[NSArrayalloc]
initWithObjects:@1,nil];NSArray*array2 = array.copy;

1.2 工厂方法(Factory Method)

定义:
创建对象的接口,让子类决定实例化哪一个类。工厂方法使得类的实例化延迟到其子类。

工厂方法的最初定义好像是专注于让子类决定创建什么对象,有一种变体,抽象类使用工厂方法创建其私有子类或任何其他类的对象,

金沙js娱乐场官方网站 1

image.png

如上图,+ create():Product 就是工厂方法,ConcreatFactoryA 与
ConcreateFactoryB 就是两个工厂类,ConcreateProductA 与 ConcreateProductB
就是两个工厂类对应的产品类,通过不同的工厂生产不同类型的产品,且两个产品类最终返回的是他们的父类
Product,隐藏了对象的具体类型。工厂方法模式让创建的对象拥有一组共同的接口,使我们无需关心做了不同类型接口的具体实现,只需要调用
Product 的接口就行。
工厂方法模式的扩展性也很好,新增的产品类并不需要修改客户端代码。但每新加一个产品类都需要新建一个工厂类,会造成项目中的类过多。

而在 Cocoa Touch 框架中,以 NSNumber 举例,将原有的 alloc+init 拆开写:

    id obj1 = [NSNumber alloc];
    id obj2 = [NSNumber alloc];
    id obj3 = [obj1 initWithBool:YES];
    id obj4 = [obj2 initWithChar:'1'];

发现 + alloc 后并非生成了我们期望的类实例,而是一个NSPlacehodlerNumber
的中间对象,后面的 – initWithXXXXX
消息都是发送给这个中间对象,再由它做工厂,生成真的对象。如 obj3
的实际类型为 NSCFBoolean,而 obj4 的实际类型为 NSCFNumber 。

金沙js娱乐场官方网站 2设计模式

   NSArray *array2 = array.copy;

定义创建对象的接口,让子类决定实例化哪一个类。工厂方法使得类的实例化延迟到其子类。

array 就是原型了,array2 以 array 为原型,通过 copy 操作创建了 array2。

1.3 抽象工厂(Abstract Factory)

定义:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

金沙js娱乐场官方网站 3

image.png

如上图,抽象工厂有一个产品族的概念,Factory1 与 Factory2 是继承
AbstractFactory 的两个产品族工厂类, 继承了父类创建 A,B
两个产品的方法,不同产品族工厂类会创建不同类型的产品,最终返回了不同的产品族对象,既
ProductA 和 ProductB。

在 Cocoa Touch 框架中,类簇是抽象工厂模式在 iOS 下的一种实现,以 NSArray
举例,将原有的 alloc+init 拆开写:

id obj1 = [NSArray alloc]; // __NSPlacehodlerArray *
id obj2 = [NSMutableArray alloc];  // __NSPlacehodlerArray *
id obj3 = [obj1 init];  // __NSArrayI *
id obj4 = [obj2 init];  // __NSArrayM *

发现 + alloc 后并非生成了我们期望的类实例,而是一个NSPlacehodlerArray
的中间对象,后面的 – init 或 – initWithXXXXX
消息都是发送给这个中间对象,再由它做工厂,生成真的对象。这里的 NSArrayI
和 __NSArrayM 分别对应 Immutable 和 Mutable(后面的 I 和 M 的意思)

于是顺着思路猜实现,__NSPlacehodlerArray 必定用某种方式存储了它是由谁
alloc 出来的这个信息,才能在 init
的时候知道要创建的是可变数组还是不可变数组。

抽象工厂将一系列的产品族统一到一起创建,增加产品族很方便,但增加产品很麻烦,需要改动太多的类的接口。

  • 原型
  • 工厂方法
  • 抽象工厂
  • 生成器
  • 单例

array 就是原型了,array2 以 array 为原型,通过 copy 操作创建了 array2。

工厂方法是针对每一种产品提供一个工厂类。通过不同的工厂类来创建不同的产品实例。

当创建的实例非常复杂且耗时,或者新实例和已存在的实例值相同,使用原型模式去复制已经存在的实例效率更高。

1.4 生成器(Builder)

定义:将一个复杂对象的构建与它的表现分离,使得同样的构建过程可以创建不同的表现。
生成器可以将构建对象的过程分为,客户 – 指导者 – 生成器 的关系,

    CharacterBuilder *characterBuilder = [[StandarCharacterBuilder alloc] init];
    ChasingGame *game = [[ChasingGame alloc] init];

    Character *player = [game createPlayer:characterBuilder];
    Character *enemy = [game createEnemy:characterBuilder];

characterBuilder 就是生成器了,而 game
就是指导者。指导者里声明了创建不同表现的对象的方法。而方法里由生成器
characterBuilder 来构建不同的 Character 类型的对象。

生成器模式将复杂的生成对象的过程交给了生成器去完成,作为客户的我们只需要根据简单的接口去生成不同表现的对象。如上述代码中的
player 以及
enemy。玩家和敌人具体的属性数值我们不需要去设置,而是交给生成器去设置。

工厂方法模式:
定义创建对象的接口,让子类决定实例化哪一个类,工厂方法使得一个类的实例化延迟到其子类。

当创建的实例非常复杂且耗时,或者新实例和已存在的实例值相同,使用原型模式去复制已经存在的实例效率更高。

金沙js娱乐场官方网站 4FactoryMethod.jpg

工厂方法(Factory Method)

1.5 单例(Singleton)

定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点
这个就老生常谈了
在 Cocoa Touch 框架中,最常见的使用了单例模式的就是 UIApplication
类了。每个应用程序有且仅有一个 UIApplication 的实例,它由
UIApplicationMain 函数在程序启动时创建为单例对象,之后,对同一
UIApplication 实例可以通过其 sharedApplication 类方法进行访问。

单例用来集中管理对类的对象所提供的资源,例如应用程序中需要用集中式的类来协调其服务,这个类就应该生成单一的实例。

单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。
解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁。(一般采用
GCD DispatchOnce)

首先先看一下类图:

工厂方法(Factory Method)

如上图,+ create():Product 就是工厂方法,ConcreatFactoryA 与
ConcreateFactoryB 就是两个工厂类,ConcreateProductA 与 ConcreateProductB
就是两个工厂类对应的产品类,通过不同的工厂生产不同类型的产品,且两个产品类最终返回的是他们的父类
Product,隐藏了对象的具体类型。工厂方法模式让创建的对象拥有一组共同的接口,使我们无需关心做了不同类型接口的具体实现,只需要调用
Product
的接口就行。工厂方法模式的扩展性也很好,新增的产品类并不需要修改业务代码。但每新加一个产品类都需要新建一个工厂类,会造成项目中的类过多。

定义创建对象的接口,让子类决定实例化哪一个类。工厂方法使得类的实例化延迟到其子类。

2接口适配

金沙js娱乐场官方网站 5工厂方法

定义创建对象的接口,让子类决定实例化哪一个类。工厂方法使得类的实例化延迟到其子类。

在 Cocoa Touch 框架中,以 NSArray 举例,将原有的 alloc+init
(相当于不同对象调用 new 方法) 拆开写:

工厂方法是针对每一种产品提供一个工厂类。通过不同的工厂类来创建不同的产品实例。

2.1 适配器(Adapter)

定义:将一个类的接口转换成客户希望的另一个接口,适配器模式可以让原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适配器模式分为类适配器模式和对象适配器模式。

*对象适配模式

金沙js娱乐场官方网站 6

image.png

上图是对象适配器模式,Adapter(适配器)遵守了
Target(目标接口)协议,拥有一个 Adaptee(被适配者)的对象 adaptee
的引用,当调用 Adapter 的 request 方法,request 方法里会去调用 adapteee
的 specificRequest 方法。
*类适配模式

金沙js娱乐场官方网站 7

image.png

类适配器模式中适配器和被适配者是继承关系。request 方法里会去调用 super
的 specificRequest 方法,达到将类的接口转换成客户希望的另一个接口。

适配器模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。

通过类图可以很清楚的看到,有一个product对象需要被创建,创建对象的方法来自于继承子类重写Creator,也就是说需要知道子类的具体类型才能够进行创建。

工厂方法是针对每一种产品提供一个工厂类。通过不同的工厂类来创建不同的产品实例。

id obj1 = [NSArray alloc]; // __NSPlacehodlerArray *id obj2 = [NSMutableArray alloc]; // __NSPlacehodlerArray *id obj3 = [obj1 init]; // __NSArrayI *id obj4 = [obj2 init]; // __NSArrayM *

金沙js娱乐场官方网站 4

2.2 桥接(Bridge)

定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化
桥接模式是软件设计模式中最复杂的模式之一,在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化。

金沙js娱乐场官方网站 9

image.png

如上图,毛笔和颜色是两个维度的变化,可以选择新建 9
个类去实现不同颜色的不同毛笔,也可以如图所示,去组合两个维度。对于客户端而言,可以针对两个维度的抽象层编程,在程序运行的时候再动态确认两个维度的子类,动态组合对象,将两个独立变化的维度完全解耦,以便能够灵活地扩充任一维度而对另一维度不造成任何影响。比如增加一种毛笔并不需要去改动图中的实现部分,增加一种颜色也不需要去改变抽象部分。(抽象部分是面向我们编程的接口部分,我们绘图的时候是调用毛笔类的绘图方法)。

桥接模式可以让抽象与实现之间不形成绑定关系,在运行时可以切换实现,也将抽象和实现完全解耦,可以独立扩展。

在《Effective Objective-C 2.0:编写高质量iOS与OS
X代码的52个有效方法》中也有提及到的虚拟工厂方法,在iOS里面叫做类族,比较典型的类就是我们经常使用的NSArray类,通常我们会认为NSArray是一个类,提供了一些方法,但是在NSArray类里面获得确实其子类的实例,这就是一个典型的工厂模式。

+ create():Product 就是工厂方法,ConcreatFactoryA 与 ConcreateFactoryB
就是两个工厂类,ConcreateProductA 与 ConcreateProductB
就是两个工厂类对应的产品类,通过不同的工厂生产不同类型的产品,且两个产品类最终返回的是他们的父类
Product,隐藏了对象的具体类型。工厂方法模式让创建的对象拥有一组共同的接口,使我们无需关心做了不同类型接口的具体实现,只需要调用
Product 的接口就行。

发现 + alloc
后并非生成了我们期望的类实例,而是一个__NSPlacehodlerArray
的中间对象,后面的 – init 或 – initWithXXXXX
消息都是发送给这个中间对象,再由它做工厂,生成真的对象。这里的
__NSArrayI 和 __NSArrayM 分别对应 Immutable 和 Mutable(后面的 I 和
M 的意思)

FactoryMethod.jpg

2.3 外观(Facade)

定义:为系统中的一组接口提供一个统一的接口。外观顶一个高层接口,让子系统更易于使用。
外观模式主要是使用一个外观类,为复杂的子系统提供一个简单的接口,而子系统的复杂调用交给外观类去做。

金沙js娱乐场官方网站 10

image.png

如上图,数据的来源可能是不同数据库,获取数据可能非常的复杂,所以使用一个外观类提供简单的获取数据的接口,复杂的操作让外观类去做。做到让子系统更加的易用。

何时使用

  • 编译时无法准确预期要创建的对象的类。
  • 类想让其子类决定在运行时创建什么。
  • 类若有若干辅助类为其子类,而你想将返回哪个子类这一信息局部化。

提供一个创建一系列相关或者相互依赖的接口的对象,而无需指定他们具体的类。

金沙js娱乐场官方网站 11抽象工厂

通过类图我们可以看出,client通过抽象工厂来创建ProductA和ProductB,但是client只要通过AbstractFactory就可以直接获得其子类,而不需要知道子类。这就是为什么是抽象工厂,在工厂方法的基础上隐藏了其实现的具体子类,在工厂方法的基础上又进行了一次抽象。

说到抽象工厂模式的话,就是要与工厂方法比较一下。

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

假设你现在正在看Demo的话,这里可以简单的说一下。其实抽象工厂就是在工厂方法的基础上将具体的类型也隐藏起来。所以工厂方法里需要知道之类的类型才能够创建对象,而抽象工厂不需要知道具体的子类的类型就可以直接通过父类的方法创建子类对象,所以是在工厂方法的基础上又抽象了一层。

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

金沙js娱乐场官方网站 12生成器模式

通过类图我们可以看出来,Director与一个builder对象相识,是通过这个Builder对象来获取对象的。

根据我的理解,就是单独使用一个类来管理生成对象,client并不需要自己创建对象,只需要告诉生成器我需要什么样的对象,这样生成器就会根据需求创建响应的对象。这样的话就会将构建与表现分离了。Demo在这里

工厂方法模式的扩展性也很好,新增的产品类并不需要修改客户端代码。但每新加一个产品类都需要新建一个工厂类,会造成项目中的类过多。

于是顺着思路猜实现,__NSPlacehodlerArray 必定用某种方式存储了它是由谁
alloc 出来的这个信息,才能在 init
的时候知道要创建的是可变数组还是不可变数组。

如上图,+ create():Product 就是工厂方法,ConcreatFactoryA 与
ConcreateFactoryB 就是两个工厂类,ConcreateProductA 与 ConcreateProductB
就是两个工厂类对应的产品类,通过不同的工厂生产不同类型的产品,且两个产品类最终返回的是他们的父类
Product,隐藏了对象的具体类型。工厂方法模式让创建的对象拥有一组共同的接口,使我们无需关心做了不同类型接口的具体实现,只需要调用
Product 的接口就行。

3对象去耦

何时使用

  • 需要创建设计各种部件的复杂对象。创建对象的算法应该独立于部件的装配方式。常见例子是构建组合对象。
  • 构建过程需要以不同的方式(例如,部件或表现的不同组合)构建对象。

单例模式: 保证一个类只有一个实例,并提供一个访问他的全局访问点。

金沙js娱乐场官方网站 13单例模式

单例模式是iOS里面比较常用的设计模式。一般我们平时要求不是很严格的时候是这样写的:

+ (instancetype)shareCharacter { static Character *character; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ character = [[self alloc] init]; }); return character;}

使用 dispatch_once
是为了线程安全,这个也可以用加锁来解决,但一般是交给GCD来实现,会做部分的优化。为什么会说是不严格的单例的,主要是以下两个方面:

  • 发起调用的对象不能以其他分配方式实例化单例对象。否则,有可能创建单例的多个实例。
  • 对单例对象实例化的限制应该与引用计数内存模型共存。

因为ARC目前已经完全取代了MRC所以第二点也就没有必要考虑了,但是仍然需要考虑创建对象的唯一性。也看到有一种方式直接在其他创建对象的方法中强制抛出异常,个人认为是不可取的。一言不合就贴代码:

static Character *character;+ (instancetype)shareCharacter { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ character = [[super allocWithZone:NULL] init]; }); return character;}+ (instancetype)allocWithZone:(struct _NSZone *)zone { return [self shareCharacter];}- (instancetype)copy { return self;}

这里如要是对内存做了限制,首先说一下生成对象的两种方式 [Character
shareCharacter]
[[Character alloc] init]
需要让两种方法都返回的是同一块内存地址,因为objective-c采用的是两段式的初始化方法,所有只要控制
*+ (instancetype)allocWithZone:(struct _NSZone )zone
返回同样的内存地址就可以了。

这里碰到了一个疑惑就是在网上看到 – (instancetype)copy {return self;}
的另一个版本是 – (instancetype)copy {return character;}
就有点困惑self的指代问题,通过 [self shareCharacter]
可以看出self指代的是当前的类,怎么在返回的时候是一个对象那?后来查了下资料并结合ios
runtime的特性,原来self是在运行的时候动态决定其指代的,可以是当前类,也可以是当前的对象。那么这里的理解就是在类方法里面就是当前类,在实例方法里面就是当前对象。

  • 适配器
  • 桥接
  • 外观

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

适配器模式在ios里面的实现分为两种,分别是类适配器和对象适配器。假设我们有A类,B类,把A类当成客户,但是A类不能直接调用B类的接口,所以需要一个适配器C类。

  • 类适配器:将适配器C类继承于B类,对A暴露相关功能的接口。

金沙js娱乐场官方网站 14类适配器

  • 对象适配器:适配器C类拥有B类的实例,对A暴露相关功能的接口。

金沙js娱乐场官方网站 15对象适配器

在使用的过程中不使用适配器模式也能实现功能,A类直接拥有B类的对象,将参数传给B类也是能够实现功能的,但是就形成了强耦合关系,使用适配器模式主要是为了解耦合。在实际应用中我发现已经不自觉地使用了该设计模式,也算是比较常用的设计模式,只是这里抽象出来了而已。

而在 Cocoa Touch 框架中,以 NSNumber 举例,将原有的 alloc+init 拆开写:

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

工厂方法模式的扩展性也很好,新增的产品类并不需要修改业务代码。但每新加一个产品类都需要新建一个工厂类,会造成项目中的类过多。

3.1 中介者(Mediator)

定义:用一个对象来封装一系列对象的交互方式,中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
我们开发的程序是由大量的类来组成的,随着程序功能的不断增加,类和类之间的依赖关系也跟着趋于复杂,而中介者模式便能解决这个问题,

金沙js娱乐场官方网站 16

image.png

如图所示,6 个 VC
类之间的交互可能特别多,如果让他们相互依赖,然后管理这些 VC
之间的关系是一件非常繁琐的事情,我们要处理各个 VC 之间的关系,每当一个
VC 要跳转到另外个 VC,我们需要包含新的 VC 的头文件。而使用中介者模式,让
VC 之间的交互变成 VC
和中介者的交互,用中介者来管理多对多的复杂的对象群,降低了各个对象之间的耦合,减少了对象之间逻辑的复杂度,但也可能导致中介者类中的实现过于复杂。

金沙js娱乐场官方网站 17

image.png

视图控制器的切换都是与 UINavigationController 做交互。由
UINavigationController 去做集中管理。

delegate是对象适配器

如果用delegate来实现适配器模式的话,那么从分类上来说应该是对象适配器,Demo这里就没有写,一般我们使用delegate的时候很大部分情况是适配器模式。

id obj1 = [NSNumber alloc];

金沙js娱乐场官方网站 18AbstractFactory.jpg

在 Cocoa Touch 框架中,以 NSArray 举例,将原有的 alloc+init
(相当于不同对象调用 new 方法) 拆开写:

3.2 观察者(Observer)

定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
在 Cocoa Touch 框架中通知和 KVO
都实现了观察者模式。通知是由一个中心对象为所有观察者提供变更通知,KVO
是被观察的对象直接向观察者发送通知。

金沙js娱乐场官方网站 19

image.png

如上图,Subject 的值改变时,通知观察者
ObserverA,ObserverB,ObserverC,我的数据改变了,依赖我的你们需要更新状态了。

被观察者不需要知道有多少个观察者和观察者的更新细节,降低被观察者和观察者之间的耦合。

Block实现对象适配器

使用delegate的方法都是可以用block来改写了,那么这里就不多说了。

   id obj2 = [NSNumber alloc];

如上图,抽象工厂有一个产品族的概念,Factory1 与 Factory2 是继承
AbstractFactory 的两个产品族工厂类, 继承了父类创建 A,B
两个产品的方法,不同产品族工厂类会创建不同类型的产品,最终返回了不同的产品族对象,既
ProductA 和 ProductB。

idobj1 = [NSArrayalloc];// __NSPlacehodlerArray *idobj2 =
[NSMutableArrayalloc];// __NSPlacehodlerArray *idobj3 = [obj1
init];// __NSArrayI *idobj4 = [obj2 init];// __NSArrayM *

4 抽象集合

何时使用

  • 已有类的接口与需求不匹配。
  • 想要一个可复用的类,该类能够同可能带有不兼容接口的其他类协作。
  • 需要适配一个类的几个不同子类,可是让每一个子类去子类化一个类适配器又不实现。那么可以使用对象适配器来适配其父类的接口。

将抽象部分与他的实现部分分离,使他们可以独立的变化。

金沙js娱乐场官方网站 20桥接模式

桥接模式简单的说就是将抽象部分与实现部分分离,但是根据不同的需求有很多不同的是实现方式,使用起来比较灵活。Demo地址在这。与书上的代码稍微有些差异,因为通过抽象父类来调用具体子类的方式上有疑问,这里是给父类添加了一个初始化方法,在这个方法里面直接返回子类的对象。。

   id obj3 = [obj1 initWithBool:YES];

抽象工厂将一系列的产品族统一到一起创建,增加产品族很方便,但增加产品很麻烦,需要改动太多的类的接口。

发现 + alloc
后并非生成了我们期望的类实例,而是一个__NSPlacehodlerArray
的中间对象,后面的 – init 或 – initWithXXXXX
消息都是发送给这个中间对象,再由它做工厂,生成真的对象。这里的
__NSArrayI 和 __NSArrayM 分别对应 Immutable 和 Mutable(后面的 I 和
M 的意思)

4.1 组合(Composite)

定义:将对象组合成树形结构以表示“部分-整体”的层次结构。组合使得用户对单个对象和组合对象的使用具有一致性。
在 Cocoa Touch 框架中,UIView 被组织成一个组合结构。每个 UIView
都可以将其它 UIView
设置为自己的子视图,形成一个树形结构,让客户端可以对单个 UIView 或者对
UIView 组合统一对待。

既平移一个 UIView,可以做到平移这一个 UIView 组合,且操作方法与平移单个
UIView 一致。

何时使用

  • 不想在抽象与其实现之间形成固定的绑定关系(这样就能在运行时切换实现)。
  • 抽象及其实现都应通过子类化独立进行扩展。
  • 对抽象的实现进行修改不应该影响客户端代码。
  • 如果每个实现需要额外的子类以细化抽象,则说明有必要把他们分成两个部分。
  • 想要带有不同抽象接口的多个对象之间共享一个实现。

为系统中的一组接口提供一个统一的接口。外观定义一个高层接口,让子系统更容易使用。

简单的说就是将一组功能抽象出来,只对外暴露一个接口。高度的抽象。

   id obj4 = [obj2 initWithChar:’1′];

将一个复杂对象的构建与它的表现分离,使得同样的构建过程可以创建不同的表现。

于是顺着思路猜实现,__NSPlacehodlerArray 必定用某种方式存储了它是由谁
alloc 出来的这个信息,才能在 init
的时候知道要创建的是可变数组还是不可变数组。

4.2 迭代器(Iterator)

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

迭代器分为两种:外部迭代器和内部迭代器

  • 外部迭代器
    • 外部迭代器让客户端直接操作迭代过程,如上面代码就是使用一个 while
      循环去迭代
      在 Cocoa Touch 中的 NSEnumerator 就实现了迭代器模式,如以下代码

NSArray *anArray = @[@"this", @"is", @"a", @"test"];
    NSEnumerator *itemEnumerator = [anArray objectEnumerator];

    NSString *item;
    while (item = [itemEnumerator nextObject]) {
        NSLog(@"%@", item);
    }
  • 内部迭代器
    客户端不需要知道实现迭代的方式。只要对每个元素进行处理就行。

NSArray *anArray = @[@"this", @"is", @"a", @"test"];
    NSString *string = @"a";
    [anArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"%@", obj);
        if ([obj isEqualToString:string]) {
            *stop = YES;
        }
    }];

何时使用

  • 子系统正在逐渐变得复杂。应用模式过程中演化出许多类。可以使用外观为这些子系统提供给一个较简单的接口。
  • 可以使用外观对子系统分层。每个子系统级别有一个外观作为入口点。让它们通过其外观进行通信,可以简化他们的依赖关系。

  • 中介者
  • 观察者

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

金沙js娱乐场官方网站 21中介者模式

简单的说就是单独使用一个对象C来管理A对象和B对象之间的交互或者说将A和B中较为复杂的逻辑抽象出来。减少A和B之间的耦合,便于A和B对象的重用。

发现 + alloc 后并非生成了我们期望的类实例,而是一个NSPlacehodlerNumber
的中间对象,后面的 – initWithXXXXX
消息都是发送给这个中间对象,再由它做工厂,生成真的对象。如 obj3
的实际类型为 NSCFBoolean,而 obj4 的实际类型为 NSCFNumber 。

生成器可以将构建对象的过程分为,客户 – 指导者 – 生成器 的关系,

抽象工厂(Abstract Factory)

5 行为扩展

何时使用中介者模式

  • 对象之间的交互虽然定义明确但是非常复杂,导致一组对象项目依赖并且难以理解。
  • 因为对象引用了许多其他对象并与其通信,导致对象难以复用。
  • 想要定制一个分布在多个类中的逻辑或行为,又不想生成太多的子类。

定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动更新。

金沙js娱乐场官方网站 22观察者模式

观察者模式在iOS里面的使用频率比较多,框架也已经帮我们实现了方便使用的方式。首先是NSNotificationCenter,再者还有KVO,所以基本已经可以满足我们的需求。目的就是解除观察者和被观察者之间的关系。

  • 组合
  • 迭代器

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

金沙js娱乐场官方网站 23树形结构

我们可以看到如上图所示的一个树形就够就是组合模式,在这个树形结构当中,线段既包含节点,也包换线段,但是client使用的时候并不关心Stroke包含什么,只是想要统一的处理。所以组合模式可以对外操作保持一致性。

在CocoaTouch中一个非常典型的例子就是UIView。UIView可以嵌套UIView,Client只需要对父View做响应的处理,父View就可以递归的调用子View,像渲染视图的命令,父View接收到只之后就会逐级向下传递。

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

苹果公司用自己的命名规则“枚举器/枚举”改写了迭代器模式,用于相关基础类的方法。基础框架中的NSEnumerator类实现了迭代器模式。NSArray,NSSet,NSDictionary这样的集合类,定义了返回与集合的类型想响应的NSEnumerator子类实例的方法。用我这种比较通俗的说法就是,提供了一种遍历集合类的方法。也就是迭代器模式。

基本上有两种迭代器:外部迭代器和内部迭代器。外部迭代器让client直接操作迭代过程,所以client需要知道外部迭代器才能使用。另一种情况是,集合对象在其内部维护并操作了一个外部迭代器。提供内部迭代器的典型的集合对象为client定义一个接口,或者从底层集合一次访问一个元素,或者向每个元素发送消息。比如NSArray
– makeObjectsPerformSelector:aSelector
就是内部迭代器。就是不需要知道迭代器,在内部都遍历并实现操作。

抽象工厂(Abstract Factory)

 CharacterBuilder *characterBuilder = [[StandarCharacterBuilder alloc] init]; ChasingGame *game = [[ChasingGame alloc] init]; Character *player = [chasingGame createPlayer:characterBuilder]; Character *enemy = [chasingGame createEnemy:characterBuilder];

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

5.1访问者(Visitor)

定义:表示一个作用于某对象结构中的各元素的操作,它让我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
当一个复杂的对象结构包含很多其他的对象,每个对象都有不同的接口,这个时候如果想添加新的接口进行新的操作,就得修改该对象的类,如果每个对象都需要添加新操作,就需要修改更多的类。而访问者模式就是用来不修改原有类添加新的操作。

访问者模式涉及两个关键元素,访问者和被访问对象。访问者遵从访问协议,访问协议里声明了访问方法。访问方法类似下面

- (void)visitEngine:(NimoEngine *)engine;
- (void)visitWheel:(NimoWheel *)wheel;

访问者模式流程,直接调用访问者里的访问方法,访问方法里实现了新添加的操作,engine
与 wheel
既被访问对象,达到了将新操作集中在访问者里处理的效果。如果再需要新添加一系列对各个元素的操作,只需要再添加一个访问者类就行。

访问者能访问复杂元素里的每一个元素,然后由访问者对这些元素进行行为扩展。

何时使用

  • 需要访问组合对象的内容,而又不暴露内部表示。
  • 需要通过多种方式暴露组合对象。
  • 需要提供一个统一的接口,来遍历各种类型的组合对象。

  • 访问者模式
  • 装饰
  • 责任链

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

金沙js娱乐场官方网站 24访问者模式

解释一下访问者模式,就是将自己不熟悉的业务承包出去。比如说我们的家是一个类,对于里面的下水道我们就有各种使用的方法,但是关于修理下水道我们要承包出去,找专业的人来对他进行操作,而这个维修的操做不能影响我们现在的使用。所以这就是访问者模式,承包商就是访问者。这样比较容易理解。从类图上可以看出来,Element调用
acceptVister:
方法将自身传递给vister,vister接收之后就可以对其进行操作。从而可以获得行为的扩展。

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

characterBuilder 就是生成器了,而 game
就是指导者。指导者里声明了创建不同表现的对象的方法。而方法里由生成器
characterBuilder 来构建不同的 Character 类型的对象。

金沙js娱乐场官方网站 18

5.2装饰(Decorator)

定义:动态地给一个对象添加一些额外的职责。就扩展功能来说,装饰模式相比生成子类更为灵活。
Category 就是实现了装饰的设计模式。Category 是 Objective-C
的语言功能,通过它可以给类添加方法的接口与实现,而不必子类化。
从这个设计模式的描述联想到 Category,就没什么难理解了。

何时使用访问者模式

  • 一个复杂的对象结构包含很多其他的对象,它们有不同的接口,但是相对这些对象实施一些依赖其具体类型的操作。
  • 需要对一个组合结构中的对象进行很多不相关的操作,但是不想让这些操作“污染”这些对象的类。可以将相关的操作集中起来,定义在一个访问者类中,并在需要在访问者中定义的操作时使用它。
  • 定义复杂结构的类很少作修改,但是经常需要向其添加新的操作。

抽象工厂有一个产品族的概念,Factory1 与 Factory2 是继承 AbstractFactory
的两个产品族工厂类, 继承了父类创建 A,B
两个产品的方法,不同产品族工厂类会创建不同类型的产品,最终返回了不同的产品族对象,既
ProductA 和 ProductB。

生成器模式将复杂的生成对象的过程交给了生成器去完成,作为客户的我们只需要根据简单的接口去生成不同表现的对象。如上述代码中的
player 以及
enemy。玩家和敌人具体的属性数值我们不需要去设置,而是交给生成器去设置。

AbstractFactory.jpg

5.3责任链(Chain of Responsibility)

定义:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间发生耦合。此模式将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
Cocoa Touch
中的事件处理流程–响应者链就实现了责任链模式。以点击为例,首先通过
hit-test view
的流程找到被点击的视图,被点击的视图如果不处理点击事件,则沿着响应者链向上回溯,比如给父视图发消息,让父视图去处理,父视图不处理则继续沿着响应者链向上回溯,直到有对象处理它为止,如果都不处理,则该事件丢弃。

在 Cocoa Touch 框架中,类簇是抽象工厂模式在 iOS 下的一种实现,以 NSArray
举例,将原有的 alloc+init 拆开写:

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

如上图,抽象工厂有一个产品族的概念,Factory1 与 Factory2 是继承
AbstractFactory 的两个产品族工厂类, 继承了父类创建 A,B
两个产品的方法,不同产品族工厂类会创建不同类型的产品,最终返回了不同的产品族对象,既
ProductA 和 ProductB。

6算法封装

id obj1 = [NSArray alloc]; // __NSPlacehodlerArray *

在 Cocoa Touch 框架中,最常见的使用了单例模式的就是 UIApplication
类了。每个应用程序有且仅有一个 UIApplication 的实例,它由
UIApplicationMain 函数在程序启动时创建为单例对象,之后,对同一
UIApplication 实例可以通过其 sharedApplication 类方法进行访问。

抽象工厂将一系列的产品族统一到一起创建,增加产品族很方便,但增加产品很麻烦,需要改动太多的类的接口。

6.1 模板方法(Template Method)

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

模板方法可以提高可扩展性与可复用性,比如 UIView 类中的定制绘图,UIView
的结构不改变,只是继承 UIView,再重载 –
(void)drawRect:(CGRect)rect方法。所以 – (void)drawRect:(CGRect)rect
就是模板方法,默认什么都不做或者只是做了部分操作,缺少特性操作,用来给子类选择重载与实现的方法。

id obj2 = [NSMutableArray alloc];  // __NSPlacehodlerArray *

单例用来集中管理对类的对象所提供的资源,例如应用程序中需要用集中式的类来协调其服务,这个类就应该生成单一的实例。

生成器(Builder)

6.2 策略(Strategy)

定义:定义一系列算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
举一个常见的例子,验证 UITextField
输入是否有效。有两个算法分别是验证邮箱的和验证电话号码的。可以通过 if
else
这样的判断代码来决定执行哪个算法。也可以通过策略模式,将算法封装起来,如下图

金沙js娱乐场官方网站 26

image.png

Strategy 是这一系列算法的父类,ConcreteStrategyA, B, C。是三种算法,给
Context 对象添加一个 Strategy 类型的属性,里面存放着 ConcreteStrategyA
或者 B,C。然后 Context
对象就知道去执行哪个算法。也就知道自己需要执行什么策略。

策略模式首先将算法都封装起来了,易于理解,且易于切换和扩展。

id obj3 = [obj1 init];  // __NSArrayI *

单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。
解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁。

将一个复杂对象的构建与它的表现分离,使得同样的构建过程可以创建不同的表现。

6.3 命令(Command)

定义:将请求封装为一个对象,从而可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
Cocoa Touch 框架中的 NSInvocation 就是实现了命令模式。

NSMethodSignature*signature = [ViewController instanceMethodSignatureForSelector:@selector(sendMessageWithNumber:WithContent:)];
  //1、创建NSInvocation对象
  NSInvocation*invocation = [NSInvocation invocationWithMethodSignature:signature];
  invocation.target = self;
  //invocation中的方法必须和签名中的方法一致。
  invocation.selector = @selector(sendMessageWithNumber:WithContent:);
  /*第一个参数:需要给指定方法传递的值
         第一个参数需要接收一个指针,也就是传递值的时候需要传递地址*/
  //第二个参数:需要给指定方法的第几个参数传值
  NSString*number = @"1111";
  //注意:设置参数的索引时不能从0开始,因为0已经被self占用,1已经被_cmd占用
  [invocation setArgument:&number atIndex:2];
  NSString*number2 = @"啊啊啊";
  [invocation setArgument:&number2 atIndex:3];
  //2、调用NSInvocation对象的invoke方法
  //只要调用invocation的invoke方法,就代表需要执行NSInvocation对象中制定对象的指定方法,并且传递指定的参数
  [invocation invoke];

将行为封装成对象,而不是直接触发行为,因为是对象,所以可以很容易的设计一个命令队列,也可以方便的记录进日志里,以及实现行为的撤销。(因为行为对象可以记录进日志里,所以可以根据日志得知上一个操作做了什么,从而进行撤销)。

id obj4 = [obj2 init];  // __NSArrayM *

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

生成器可以将构建对象的过程分为,客户 – 指导者 – 生成器 的关系,

7性能与对象访问

发现 + alloc 后并非生成了我们期望的类实例,而是一个NSPlacehodlerArray
的中间对象,后面的 – init 或 – initWithXXXXX
消息都是发送给这个中间对象,再由它做工厂,生成真的对象。这里的 NSArrayI
和 __NSArrayM 分别对应 Immutable 和 Mutable(后面的 I 和 M 的意思)

适配器模式分为类适配器模式和对象适配器模式。

    CharacterBuilder *characterBuilder = [[StandarCharacterBuilder
alloc] init];

7.1 享元(Flyweight)

定义:利用共享技术有效地支持大量细粒度的对象。
tableViewCell 的重用机制就是实现了享元模式。在要使用一个 Cell
的时候,会先去重用池里看看 tableView 有没有可以重用的 cell,如果有重用该
cell,没有创建一个,这就是享元模式。

享元模式主要有两个关键组件,可共享的享元对象和保存它们的享元池。

举另一个实现例子,画面上需要显示 100
个相同的图案,可以只生成一个包含该图案 image 的 imageView。其它 99
个只需要去享元池里去拿这个 imageView
实例的信息,然后在页面里直接绘制图案,这样就不需要生成 100 个图案实例。

享元模式通过共享一部分必须的对象,减少对象的创建,节省大量的内存。

于是顺着思路猜实现,__NSPlacehodlerArray 必定用某种方式存储了它是由谁
alloc 出来的这个信息,才能在 init
的时候知道要创建的是可变数组还是不可变数组。

金沙js娱乐场官方网站 27适配器模式Image.png

    ChasingGame *game = [[ChasingGame alloc] init];

7.2代理(Proxy)

定义:为其它对象提供一种代理以控制对这个对象的访问。
代理设计模式的英文名是 Proxy pattern,和我们常见的 delegate(委托)
没关系。

iOS
中常见的代理模式例子为引用计数,当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少内存用量。典型做法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。

当然,上面的代理模式中的代理者什么都没做,代理对象作为 A 和 C
中间的协调者,可以多做点操作,可以理解为 VPN
中的代理者可以对传输数据加密,而 A 和 C 中的代理者,也可以隐藏 C
的信息,做到对 C 的保护。

抽象工厂将一系列的产品族统一到一起创建,增加产品族很方便,但增加产品很麻烦,需要改动太多的类的接口。

上图是对象适配器模式,Adapter遵守了 Target协议,拥有一个 Adaptee的对象
adaptee 的引用,当调用 Adapter 的 request 方法,request 方法里会去调用
adapteee 的 specificRequest 方法。

    Character *player = [chasingGame createPlayer:characterBuilder];

8对象状态

生成器(Builder)

类适配模式

    Character *enemy = [chasingGame createEnemy:characterBuilder];

8.1备忘录(Memento)

定义:在不破坏封装的情况下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。
Cocoa Touch 框架中归档可以实现备忘录模式,Cocoa
的归档是对对象及其属性还有同其他对象间的关系进行编码,形成一个文档,该文档可以保存于文件系统,也可在进程或网络间传输,最后又可以通过解档将文档解码成与该对象归档时状态一致的对象。

既将对象保存一个备份放置到其它地方,可以随时使用备份将该对象恢复到原先保存的状态,用来储存关键对象的关键状态。

参考资料:
《Objective-C
编程之道》
本书可自行下载研究
饶神博客

将一个复杂对象的构建与它的表现分离,使得同样的构建过程可以创建不同的表现。

金沙js娱乐场官方网站 28适配器模式Image2.png

characterBuilder 就是生成器了,而 game
就是指导者。指导者里声明了创建不同表现的对象的方法。而方法里由生成器
characterBuilder 来构建不同的 Character 类型的对象。

生成器可以将构建对象的过程分为,客户 – 指导者 – 生成器 的关系,

类适配器模式中适配器和被适配者是继承关系。request 方法里会去调用 super
的 specificRequest 方法,达到将类的接口转换成客户希望的另一个接口。

生成器模式将复杂的生成对象的过程交给了生成器去完成,作为客户的我们只需要根据简单的接口去生成不同表现的对象。如上述代码中的
player 以及
enemy。玩家和敌人具体的属性数值我们不需要去设置,而是交给生成器去设置。