软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 软件设计模式建立在七大设计原则基础上,本文为大家梳理下设计原则和常用设计模式。
开闭原则(Open Closed Principle,OCP)
- 开闭原则是总纲,它指导我们要对扩展开放,对修改关闭。
- 对于一个类,最好不让外部修改它内部的实现,而是通过组合、继承等手段来扩展类的功能。比如对于第三方库,不要直接修改其内部实现,可以使用组合或者继承手段扩展它。
- 单一职责原则指导我们实现类要职责单一。
- 一个类实现的职责要单一,不要弄成大杂烩,可以通过拆分类的方式来精简类。
- 里氏替换原则指导我们不要破坏继承体系。
- 任何使用父类的地方都能使用子类。
- 依赖倒置原则指导我们要面向接口编程而不是实现编程。
- 面向接口编程的优势有: 1、通过暴露接口给外部使用,向外部隐藏实现细节;即使实现发生变化,外部也不需要改变调用接口。 2、通过实现多个接口,来避免多继承带来的类型爆炸问题。 3、通过接口来实现多态。 框架实现中一般会大量用面向接口编程。比如iOS框架中大量用协议来实现多继承。
- 接口隔离原则指导我们在设计接口的时候要精简单一。
- 一个类暴露的接口要尽量少。
- 每个接口实现的功能要单一,一个接口不能做好几件事,通过拆分接口来让接口功能更加单一。
- 组合/聚合复用原则指导我们优先使用组合而不是继承。
- 使用组合而不是继承,不需要了解原类的实现,即时原类发生了变化,影响也也可以降至最低。
- 在UI编程上,大量的使用组合模式,通过组合控件来实现复杂界面。
- 迪米特法则指导我们要降低耦合。
- 类的实现要高内聚、低耦合。
设计模式,在框架中经常使用到。比如iOS框架中大量使用原型模式、工厂模式、单例模式、观察者模式等模式,我们自己在设计代码时候也会使用设计模式。下面简单介绍下各个模式以及在iOS中的应用,具体的模式详解,笔者后续会在设计模式专栏中讲解。
-
使用原型实例指定创建对象的种类,并通过复制这个原型实例创建新的对象。
-
iOS中的NSCopying和 NSMutableCopying提供了实现原型模式需要遵循的模式,客户通过调用copy或者mutableCopy来实现原型复制。
-
使用单例模式来创建类的唯一对象,客户端只能通过这个唯一对象访问这个类的功能。
-
iOS中很多系统类都是使用单例模式实现的。比如UIApplication、NSFileManager。
-
实现单例注意点 1、用dispatch_once 2、把init和new标记为不可用,防止调用方直接实例化对象
/**
* Unavailable initializer
*/
+ (instancetype)new NS_UNAVAILABLE;
/**
* Unavailable initializer
*/
- (instancetype)init NS_UNAVAILABLE;
工厂模式分为简单工厂、工厂方法和抽象工厂。
- 简单工厂用一个类来创建所有产品。这个类不继承任何抽象工厂。调用方通过调用这个类来创建不同的产品。
- 工厂方法,所有工厂继承一个抽象工厂,每个抽象工厂用来创建一个特定产品。比如工厂A用来创建产品A,工厂B用来创建产品B。调用方通过调用不同的工厂来创建不同的产品。
- 抽象工厂是工厂方法的升级版。它跟工厂方面的区别是:通过把产品进行分类,使得一个工厂可以创建多个产品。比如工厂A可以创建产品A和产品C。
-
定义创建对象的接口,让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到其子类。
-
使用场景 1、编译期无法预期要创建的对象的种类。 2、类有若干辅助类为其子类,想对外部隐藏这些子类的实现。
-
提供一个创建一系列相关或者互相依赖对象的接口,而无须指定他们具体的类。
-
iOS中的类簇(Class Clusters)采用抽象工厂实现的。比如NSNumber、NSString、NSArray、NSDictionary、NSData。
-
将一个复杂对象的构建分步进行,使得同样的构建有不同的表现。 -UML 图 [图片上传失败...(image-c074f2-1607750873590)]
[图片上传失败...(image-ec5113-1607750873590)]
-
将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
-
使用场景 1、已有类的接口于需求不匹配。 2、想要一个可复用的类,该类能够跟带有不兼容接口的其他类写作。 3、需要适配一个类的几个不同子类,可是让每一个子类去子类化一个类适配器不太现实。可以使用对象适配器(委托)来适配其父类的接口。 4、可以用iOS中的delegate和Block来实现适配器模式,他们用的是对象适配器。
-
将抽象部分与它的实现部分分离,使他们都可以独立地发生变化。
-
使用场景 JSBridge的实现就用了桥接模式。我们一般把实现部分抽离出来一个单独的实现类。分别来桥接模UIWebView和WKWebView。
-
为系统中的一组接口提供一个统一的接口。外观模式定义一个高层接口,让子系统更易于使用。
-
使用场景 如果使用一个子系统的某个功能,需要调用多个类才能实现,这个时候可以用外观模式。通过提供一个接口让客户调用,这样可以隐藏实现,降低客户使用门槛。
-
用一个对象来封装一系列对象的交互方式。中介者使各个对象不需要显示地相互引用,做到高内聚、低耦合。
-
使用场景 1、CTMediator使用了中介者模式来进行模块解耦。 2、MVC模式中的VC就是中介者。
-
定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动更新。
-
使用场景 1、观察者模式能实现对象之间的彻底解耦。 2、iOS中的观察者中心和KVO就是观察中中心的实现。
-
将对象组合成树形结构以表示"部分-整体"的层次结构。组合使得用户对单个对象和组合对象的使用具有一致性。
-
UML图 [图片上传失败...(image-2901aa-1607750873589)]
-
使用场景 iOS中的UIView就是组合模式的实现。通过UI控件组合形成UI树,呈现界面给用户。
-
提供一种方法顺序访问一个集合对象的各个元素,而不需要暴露该对象的内部表示。
-
迭代器分为外部迭代器和内部迭代器。外部迭代器需要用户手动创建迭代器,内部迭代器不需要用户手动创建,由组合对象提供枚举方法。
-
使用场景 1、iOS中NSEnumerator提供了外部迭代器的实现。 2、iOS中集合类方法enumerateObjectsUsingBlock提供了内部迭代器的实现。 3、iOS中的快速枚举也是迭代器的实现,需要实现NSFastEnumeration。
-
动态地给一个对象添加一些额外的职责。就扩展功能来说,装饰模式相比生成子类更为灵活
-
使用场景 1、在没有类源码的基础上扩展类。 2、在不改变原有类行为的基础上扩展类。 3、iOS中的类别可以用来实现装饰模式。
-
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间发生耦合。此模式将这些对象连成一条链,并沿着这条链条传递下去,直到有一个对象处理它为止。
-
使用场景 iOS中的事件响应链实现了责任链模式。
-
定义一个操作中算法的骨架,而将一些步骤延迟到子类中。模版方法可以重定义算法的某些特定步骤而不改变改算法的结构。
-
使用场景 1、子类的共同行为提取出来放到基类中,以避免代码重复。差异方法定位为模版方法,子类通过重写模版方法来实现差异化。 2、iOS中drawRect为模版方法。NSArray和NSDictionary定义为模版类。
-
将请求封装成对象,以支持对请求排队、记录请求请求日志,已经支持撤销。
-
使用场景 1、NSInvocationk实现了命令模式。 2、NSUndoManager实现了命令模式。 3、Targrt-Action可以实现了命令模式。
-
在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象外保存这个状态,以便后续恢复成这个状态。
-
UML图
-
使用场景 1、文档的归档可以采用备忘录模式。 2、iOS中的Archive采用了备忘录模式。通过NSCoding实现对象的归档和反归档。。 3、plist存储、coredata也采用了备忘录模式
如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群931542608来获取一份详细的大厂面试资料为你的跳槽多添一份保障。