阅读 966

iOS开发小记-设计模式(持续更新)

MVC、MVP、MVVM


关于三者的比较说明,可以看Casa酱的这篇iOS应用架构谈 view层的组织和调用方案,巧神的被误解的 MVC 和被神化的 MVVM篇幅有限,也可以瞅一眼~

原型模式


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

它是一个非常简单的设计模式,基于“复制”操作。复制指用同一模具生产一系列的产品。模具所基于的物品称为原型。 此模式的最低限度是生成对象的真实副本,以用作同一环境下其他相关事物的基础。

  • 何时使用

通俗来说,如果要创建的新对象和自身差异并不大,其行为略有不同,且复制自身比手工实例化要好,就可以使用原型模式。

例如我有一个Person实例A,我需要一个跟A仅Name不同,但是其余属性均相同的新对象B,如果使用者通过新建实例并将A的所有属性赋值给B,这无疑是相当麻烦的,这时我们就可以使用B = [A clone]来复制A的属性,从而节省大量精力,提高了复用性也便于维护。

  • Cocoa中的应用

最常见的应用就是深复制,如NSMutableArray = [NSArray mutableCopy]

注:浅复制不属于原型模式,它不满足上面说到的原型模式的最低限度,仅仅是指针拷贝。
复制代码
  • 实际开发中的应用

比较常见的情况就是对自定义对象进行拷贝的时候,我们需要通过对自定义对象实现NSCopying协议及其方法- (id)copyWithZone:(NSZone *)zone

单例模式


单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。它几乎是所有设计模式中最简单的一个了。

  • 何时使用
  1. 类只能有一个实例,而且必须从一个为人熟知的访问点对其进行访问。
  2. 这个唯一的实例只能通过子类化进行扩展,而且扩展的代码不会破坏客户端的代码。 一般来讲,工程中如果需要频繁使用某一个类,而且可以只需要一个该类的实例,并且通过它共享资源,那么我们就可以考虑使用单例模式。
  • Cocoa中的应用

最常见的就是[UIApplication sharedApplication]

  • 实际开发中的应用

例如一些管理类xxManager,设置类xxConfiger等等,都是会频繁使用并且需要共享资源保存临时状态,就可以使用单例。 关于多线程下加锁保证只创建一个实例,我们可以使用@synchronized、NSLock等,更推荐使用dispatch_once,性能更优。 甚至我们还可以使用宏来实现单例。

  • Objective-C下的问题

由于Objective-C并不能像C++,Java中一样能够隐藏构造函数,小伙伴们还是可以alloc/init来创建对象,一般有两种解决方案:

  1. 从OC的对象创建角度出发,就是把创建对象的各种入口给封死了。alloc,copy等等,无论是采用哪种方式创建,我都保证给出的对象是同一个。
  2. 温柔派就直接告诉外面,alloc,new,copy,mutableCopy方法不可以直接调用。否则编译不过。

工厂方法模式


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

通俗来讲,对于有多种类似的产品,我们可以将产品的公有属性和方法抽象为一个产品基类,将生产产品的工厂抽象为一个工厂基类,并暴露返回产品对象的工厂方法,各自产品的工厂继承自工厂基类,并重载该工厂方法以返回对应的产品对象。

  • 何时使用
  1. 编译时无法准确预期要创建的对象的类。
  2. 类想让子类决定运行时创建什么。
  3. 类有若干个辅助类为其子类,而你想将返回哪个子类这一信息局部化。
  • 实际开发中的应用

例如一开始的股票详情界面,有指数、沪深、港股、美股等等,分别有一个基于TKHqBaseStockDetailViewController的详情子类,以及基于TKHqBaseStockDetailFactory的工厂子类,工厂提供了一个创建详情的方法,使用者可以使用对应的工厂子类来获取想要的详情子类。

注:我们也可以通过传递股票信息在工厂内部通过枚举来直接创建对应的详情子类,而不通过工厂子类来创建,这种就是简单工厂。
复制代码

抽象工厂模式


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

通俗来讲,我们可以提供一个工厂基类,其中包含了创建一系列产品的工厂方法,并且通过使用预处理或者枚举来生成不同的工厂子类,这些工厂子类中都实现了基类的工厂方法,并根据自己的需求在创造产品的工厂方法中获取想要的具体的产品子类。

  • 与工厂方法的区别
  1. 抽象工厂使用对象组合来创建抽象产品,工厂方法是通过继承来创建抽象产品。
  2. 抽象工厂用于创建多系列产品,工厂方法用于创建一个产品。
  3. 如果要支持新的产品,抽象工厂需要修改基类,工厂方法则是增加新的工厂子类。
  • 何时使用

当我们需要的不仅仅是一个产品,而是一个包含了多个相关联或相依存的不同产品结构的产品族,且我们不想指定具体的类或者创建的细节。

  • Cocoa中的应用

最常见的就是NSNumberNSStringNSArrayNSDictionary。其中NSNumber中的numberWithBool:生成的实际类型是NSCFBoolean类型,而其他的大多数生成的是NSCFNumber类型,并且都实现了抽象超类NSNumber的所有定义。这里的NSCFBooleanNSCFNumber就相当于两个工厂子类,超类的所有定义就相当于一系列产品,Fundation框架中抽象工厂的这种特点,被称为类簇

类簇是Fundation框架中的一种非常常见的设计模式,基于抽象工厂的思想。它将若干个私有具体工厂子类集合到一个公有的抽象超类之下。例如NSNumber有一系列公有API,定义了各种类型数据的共有行为,我们在使用时不用关心NSNumber实例的具体类型。

注:类簇中往往提供了许多生成工厂子类的方法,这些方法不应该在具体的工厂子类中重载,工厂子类只应该关注产品的创建。
复制代码
  • 实际开发中的应用

例如前面说到的股票详情,一旦详情页面的变得复杂,往往需要由多种界面元素来组成,并且随着股票种类的增加,我们可能并不想要在关注我们用什么具体的工厂子类去实例化详情,这里我们就可以使用抽象工厂。

可以将详情大致划分为四层:Nav信息层、基础盘口层、图表层和资讯层。定义一个抽象超类详情TKHqStockDetailViewController,定义一系列基于这四层的公有API,并且声明+ (TKHqStockDetailViewController *)detailWithStock:(TKHqStockModel *)stockModel的类工厂方法,该方法中通过股票类型创建了不同的“详情工厂”,最终在各自的工厂子类中实现一系列的API。

适配器模式


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

基本上有两种实现适配器的方式:类适配器和对象适配器。

类适配器:通过多继承来实现,在OC中可以通过协议和单继承模拟多继承。协议用于规范统一的接口,父类用于实现具体的新接口,子类通过重载协议的目标接口,在接口内部调用父类的新接口来响应处理。

对象适配器:与类适配器不同,它不需要继承被适配者,仅仅需要实现统一的协议,并持有一个被适配者的引用,在重载的协议接口中,调用该引用的新接口来响应处理。

两种方式的区别如下:

  1. 类适配器只针对单一具体的被适配者,不适用于被适配者的子类,否则需要新建适配器;对象适配器可以适配多个被适配者,如果要适配它们的子类,声明一个子类引用即可。
  2. 类适配器易于重载被适配者的行为,而对象适配器不能重载,否则需要新建被适配者的一个子类,再持有这个子类实例的引用。
  3. 类适配器无需使用额外的指针间接访问被适配者,而对象适配器需要。
注:一般来说,对象适配器适用范围更广泛,对于未来可能新增的需要适配的新接口,也更容易扩展。
复制代码
  • 何时使用对象适配器
  1. 已有类的接口与需求不匹配。
  2. 想要一个可复用的类,该类能够同可能带有不兼容接口的其他类协作。
  3. 需要适配一个类的几个不同子类,可是让每一个子类去子类化一个类适配器又不现实。
  • 实际开发中的使用

例如详情的设置页面,我们发现Cell的结构基本差不多,可以由Title、SubTitle、Detail、AccessoryView四部分组成,其中某些Cell可能显示SubTitle有的不显示;有的显示Detail有的不显示;AccessoryView内容可能是个箭头,有的可能是个SwitchBar。如果我们对每种样式去定制化一个Cell,无疑是难以维护和扩展的,这里我们就可以通过适配器模式,将每种设置的数据适配成规范数据来统一处理。

我们可以创建一个Protocol来声明目标接口,包括了Title、SubTitle、Detail、AccessoryView四部分内容,然后定义一个Adapter实现该协议,将使用到各种被适配者(在这里也就是不同设置类型原来使用的Model)持有引用,然后目标接口中判断哪种引用不为nil,然后根据当前被适配者的接口来获取相应的数据,这样外部在使用不同设置类型时,只需要将原来的Model传给Adapter,Cell便能通过Adapter来获取统一规范的数据。

注:也可以通过实现Protocol 、集成各自Model的具体子类的类适配器方式来实现上述功能,这样子Adapter中就只处理父类Model的数据,在使用时不同设置给Cell设置不同的子Adapter即可。
复制代码

委托模式


委托模式:主要是适配器模式,它实际上同样是把类的接口变换为客户端要求的另一种接口,通过Protocol来要求其他类适配它需要的接口,而它本身就是个适配器。

  • 何时使用

当我们需要其他类来做一些当前类无法完成的事情,或者需要使用者来处理某些个性化操作。 一般可以通过delegate或者block来实现。

  • Cocoa中的使用

最常见的就是UITableView中delegate和datasource的使用,用来使用者自定义某些设置和响应事件。

  • 实际开发中的应用

例如需要传递上一层数据、需要使用者来定义某些设置、View需要VC来响应push/pop等等。

代理模式


代理模式:为其他对象提供一种代理以控制对这个对象的访问。其思想是使用一个基本上跟实体对象行为相同的代理。

  • 何时使用
  1. 需要一个远程代理,为位于不同地址空间或网络中的对象提供本地代表。
  2. 需要一个虚拟代理,来根据要求创建重型的对象。
  3. 需要一个保护代理,来根据不同访问权限控制对原对象的访问。
  4. 需要一个智能引用代理,通过对实体对象的引用进行计数来管理内存。也能用于锁定实体对象,让其他对象不能修改它。
  • Cocoa中的应用

Objective-C中的delegate属于委托模式,但也基于了代理模式的思想,如第四点所说,也相当于一个智能引用代理,同时通过协议接口的控制,也一定程度上相当于控制了对原对象的访问权限。

  • 实际开发中的应用

NSProxy

桥接模式


桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。 通俗来讲,如果实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,较少耦合。

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

观察者模式


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

  • 何时使用
  1. 有两种抽象类型相互依赖。将它们封装在各自的对象中,就可以对他们单独进行改变和复用。
  2. 对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。
  3. 一个对象必须通知其他对象,而它又不知道需知道其他对象是什么。
  • Cocoa中的应用

有两种技术都使用了观察者模式:通知和KVO。

中介者模式


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

  • 何时使用
  1. 对象间的交互虽定义明确然而非常复杂,导致一组对象彼此相互依赖而且难以理解。
  2. 因为对象应用了许多其他对象并与其通讯,导致对象难以复用。
  3. 想要定制一个分部在多个类中的逻辑或者行为,又不想生成太多子类。
  • 实际开发中的使用

在多模块的开发中,由于不同产品模块由不同小组开发,相互之间的代码相对封闭,但是由于业务关联,往往又需要跟其他模块交互,比如A模块打开B模块页面,B模块又需要查询C模块的数据,一般实现可能直接A调用B模块相关代码,B模块调用C模块相关代码,这样造成的问题一是模块之间相互耦合,独立开发十分困难,需要屏蔽相关代码;二是一旦其他模块的API修改,相关模块全都得改动。

针对这个问题,可以使用中介者模式,框架定义了一个中介者基类baseMediator,并实现了一套协议用于接收外部事件,A模块如果想要与B模块交互,可以通过一个通知方法告诉BMediator,然后BMediator根据不同参数来处理相应事件,这样避免了模块之间的强耦合,同时外部模块也不在关心它的内部实现,只需要关心其暴露出来的事件定义。

关注下面的标签,发现更多相似文章
评论