设计模式

设计模式

一、策略模式——Strategy

它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。通过一个Context指定一个Strategy,通过Strategy的子类实现不同的算法。

使用方法:

1. 创建一个协议,然后将不同的策略遵守协议创建策略对象
2. 创建一个管理类,定义枚举来标记策略类型,然后创建一个包含策略类型或者遵循自定义协议的对象构造方法

iOS的使用场景:

策略模式配合简单工厂模式,同样的最初价格,返回不同的最终价格。不同的type就是不同的策略。

二、装饰模式——Decorator

动态的给一个对象添加有一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

使用方法:

1. 创建一个基类,定义方法及实现方法
2. 创建一个继承与基类的修饰子类,定义一个父类类型的属性来接收外部传进来的原始对象。(装饰对象需要装饰的原始对象)
3. 重写父类方法,在方法中使用该属性调用对应的方法(记得调用父类方法)
4. 装饰对象指定原始对象,后面就是用装饰对象。这样既有原始对象的功能,也有装饰对象的功能。

iOS的使用场景:

比如iOS的扩展和分类就是装饰模式。
比如SDWebImage的UIView、UIImageView、UIButton的各种分类,他们都把component的部分在UIView分类中,decorator部分放在UIButton、UIImageView中。

三、代理模式——Proxy

为其他对象提供一种代理以控制对这个对象的访问。

使用方法:

1. 为A对象创建一个协议
2. 让B对象去遵循该协议,并实现协议方法
3. 将A的代理指向B对象

iOS的使用场景:

UITableView的delegate和dataSource熟悉,都是通过代理对象来控制对UITableView的访问。

四、工厂方法模式——Factory method

为定义一个拥有创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。相对于简单工厂方法,工厂方法模式把工厂也抽象出来,进行接口、实现分离。这样具体工厂和具体产品可以对应着同时扩充,而不需要修改现有逻辑。当然,使用者也许在不同场景要在一定程度上自己对应的工厂选择

使用方法:

1. 构造方法

iOS的使用场景:

不同的操作类有不同的工厂类,扩展新的操作不影响现有操作
比如NSNumber调用的具体工厂NSCFBoolean和NSCFNumber也是具体的工厂类。

五、原型模式——Prototype

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

使用方法:

1. 实现-(id)copyWithZone:(NSZone *)zone,返回创建的新对象

==注:iOS中的深拷贝就是原型模式的一种应用==

六、模板方式模式——Template method

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

使用方法:

1. 在基类中实现一个方法A,在A方法里调用另一个方法B。
2. 在继承的子类中实现方法B。
3. 通过不同的子类调用A方法,实现不同的算法结果。

iOS的使用场景:

当我们要完成在某个细节层次一致的一个过程或者一系列步骤,但是其个别步骤的更详细的层次上实现可能不同时,我们通常考虑用模板方法模式来处理

七、外观模式——Facade

为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

使用方法:

1. 子系统实现统一的接口
2. 创建一个总系统类,在该类中创建所需要的子系统对象,然后实现统一的接口,在接口方法中分别调用所有子系统对象对应的接口

八、建造者模式——Builder

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

使用方法:

1. 将一个对象的构建分成几部分组成,创建一个协议(部分和整体)
2. 创建一个构造者遵守协议并实现协议,最后创建完成一个对象

==注: 建造者模式可以使用链式编程==

九、观察者模式——Observer

定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

使用方法:

1. 一个对象内部创建一个可变数组来管理添加进来的观察者
2. 实现添加、删除观察者方法
3. 当该对象的某个值或者方法调用了,将遍历数组中的每一个观察者,让观察者调用指定的方法

==注: iOS中的KVO、NSNotication都是观察者模式==

十、抽象工厂模式——Abstract Factory

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

使用方法:

1. 将具体的对象变成一个遵循协议的id对象(即通过协议来创建类)

十一、状态模式——State

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

使用方法:

1. 创建一个状态协议
2. 创建各个遵循状态协议的状态
3. 创建一个类,这个类拥有一个状态属性,使用状态类去实现协议装的方法

十二、适配器模式——Adapter

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

使用方法:

1. 创建一个类A,另一个类B
2. 创建一个新类C继承A,定义一个类型为B的属性
3. 重写C的父类方法,在方法中调用B的方法

十三、备忘录模式——Memento

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

使用方法:

1. 定义一个状态类A
2. 定义一个改变状态的类B 
3. 定义一个新类,定义状态属性,获取B的当前状态(相当于记录之前的状态,用于恢复到之前的保存状态)

iOS的使用场景:

比如iOS对对象的归档解档。

十四、组合模式——Composite

将对象组合成树形结构以表示“部分--整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。当需求中体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象,接应该考虑使用组合模式了。

使用方法:

1. 定义统一协议
2. 所有的类都是遵守该协议的类
3. 使用可变数组来组合部分

十五、迭代器模式——Iterator

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

使用方法:

OC中已实现:

  1. 迭代器

     // 创建数组 正序 迭代器
     NSEnumerator *arrEnumer1 = [arr objectEnumerator];
         
     // 创建数组 反序 迭代器
     NSEnumerator *arrEnumer2 = [arr reverseObjectEnumerator];
         
     // 创建字典 key 迭代器
     NSEnumerator *dicKeyEnumer = [dic keyEnumerator];
         
     // 创建字典 对象 迭代器
     NSEnumerator *dicObjEnumer = [dic objectEnumerator];
         
     // 获取迭代器中下一个对象
     id obj = [arrEnumer1 nextObject];
         
     // 获取迭代器中所有对象
     NSArray *array = [arrEnumer2 allObjects];
    
  2. 数组用迭代器遍历

     NSArray *array = [NSArray arrayWithObjects:@"bei", @"jing", @"huan", @"ying", @"nin", nil];
         
     // 获取数组的正序迭代器
     NSEnumerator *enu1 = [array objectEnumerator];
         
     // 获取数组的反序迭代器
     NSEnumerator *enu2 = [array reverseObjectEnumerator];
         
     // 遍历数组
     id obj = nil;
         
     // 正序,获取下一个需要遍历的元素
     while (obj = [enu1 nextObject]) {
         NSLog(@"%@", obj);
     }
         
     // 反序,获取下一个需要遍历的元素
     while (obj = [enu2 nextObject]) {
         NSLog(@"%@", obj);
     }
    
  3. 集合用迭代器

     NSSet *set = [NSSet setWithObjects:@5, @23, @3, @8, @21, @33, @18, nil];
         
     NSEnumerator *enu = [set objectEnumerator];
         
     id obj = nil;
         
     while (obj = [enu nextObject]) {
         NSLog(@"%@", obj);
     }
    
  4. 字典用迭代器

     NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:@"value1", @"key1", @"value2", @"key2", nil];
     
     // key 迭代器
     NSEnumerator *keyEnumer = [dic keyEnumerator];
         
     id key = nil;
     while (key = [keyEnumer nextObject]) {
         NSLog(@"%@ = %@", key, [dic objectForKey:key]);
     }
         
     // 对象迭代器
     NSEnumerator *objEnumer = [dic objectEnumerator];
         
     id obj = nil;
     while (obj = [objEnumer nextObject]) {
         NSLog(@"%@", obj);
     }
    

十六、单例模式——Singleton

保证一个类仅有一个实例,使它们都可以独立地变化。

使用方法:

1. 使用dispatch_once方法创建
2. 通过线程加锁实现
3. 重写allocWithZone方法,用来保证其他人直接使用alloc和init试图获得一个新实例的时候不产生一个新实例,

注:iOS中还有很多系统的单例,
比如NSUserDefaults就是单例,
UIApplication类有一个方法叫sharedApplication也是一个单例,
NSNotificationCenter(消息中心) 、
NSFileManager(文件管理) 、
NSURLCache(请求缓存)、
NSHTTPCookieStorage(应用程序cookies池)都是系统单例

十七、桥接模式——Bridge

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

使用方法:

1. 定义一个协议
2. 创建一个遵循该协议的类A,实现方法
3. 定义抽象类B,在该类B中定义类型为A的属性,让该属性完成B中的方法

十八、命令模式——Command

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

使用方法:

1.定义一个基础命令类
2.将各个单独命令封装成一个命令对象,命令实现使用一个单例对象来实现命令
3.定义一个命令接收者,通过可以数组来添加或者删除命令
4.命令发送者,通过构造命令对象来发送给命令接受者

十九、职责链模式——Chain of Responsibility

使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这个对象连成一条链传递该请求,直到有一个对象处理它为止。

使用方法:

1. 定义一个基类
2. 定义其他子类,每个类都有他的父类
3. 如果当前可以处理方法就直接处理,如果不可以就让他的父类去处理

iOS的使用场景:

iOS事件的传递和响应就是职责链模式的实现。

二十、中介者模式——Mediator

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

使用方法:

1. 创建类A和类B,构造函数中传入一个中介者
2. 创建中介者C,在C中的方法中判断传进来的对象是A还是B,然后执行其相对应的方法

二十一、享元模式——Flyweight

使用共享技术有效地支持大量细粒度的对象。

使用方法:

1. 使用一个字典来存储一个具体的对象
2. 如果使用该对象时,从字典中获取。如果没有则创建一个新的对象,并存储到字典中。

==注: iOS中的cell使用的是该种设计模式==

二十二、解释器模式——Interpreter

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示中解释语言汇中的句子。

使用方法:

1. 定义一个解释器类,构造函数接收输入,添加一个属性或者输出方法
2. 定义一个类,构造函数里引入解释器

二十三、访问者模式——Visitor

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

使用方法:

1. 创建一个访问者A,创建方法接收类B。
2. 创建一个类B,创建一个方法接收访问者A。在B的方法中调用A的方法

六个原则

单一职责原则(SRP),就一个类而言,只做一件事。
开放-封闭原则(OCP),是说软件实体(类、模块、函数等等)应该可以拓展,但是不可修改。
依赖倒转原则(DIP),A. 高层模块不应该依赖低层模块,两个都应该依赖抽象。B. 抽象不应该依赖细节,细节应该依赖抽象。
里氏代换原则(LSP),子类型必须能够替换掉它们的父类型。
迪米特法则(LoD),如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
合成/聚合复用原则(CARP),尽量使用合成/聚合,尽量不要使用类继承。

推荐阅读更多精彩内容

  • 设计模式概述 在学习面向对象七大设计原则时需要注意以下几点:a) 高内聚、低耦合和单一职能的“冲突”实际上,这两者...
    彦帧阅读 1,037评论 0 9
  • 对象创建 原型(Prototype) 使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象。 NSArr...
    消失的BUG阅读 65评论 0 0
  • 在软件工程中,(引自维基百科)设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题...
    jackyshan阅读 10,655评论 1 68
  • 文:饶志臻(简书作者) iOS 中的 21 种设计模式 对象创建 原型(Prototype) 使用原型实例指定创建...
    sky007z阅读 15评论 0 0
  • 对象创建 原型(Prototype) 使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象。 NSArr...
    暗夜精灵_NightElf阅读 54评论 0 0