设计模式六大原则
- 1.单一职责原则:即一个类应该只负责一项职责
- 2.里氏替换原则:所有引用基类的地方必须能透明地使用其子类的对象
- 3.依赖倒转原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象
- 4.接口隔离原则:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上
- 5.迪米特法则:一个对象应该对其他对象保持最少的了解
- 6.开闭原则:对扩展开放,对修改关闭
第一:单一职责原则
开发中很多会违反这项原则,比如一个工具类,它里面既包含了正则表达式的一些封装方法,又包含了一些颜色的封装方法,甚至还有一些日期的封装方法.按照单一职责原则,他们应该分成不同类,方便以后团队开发
第二:里氏替换原则(针对继承,KVO原理)
所有引用基类的地方必须能透明地使用其子类的对象 1.继承是为了实现代码重用,也就是为了共享方法,那么共享的父类方法就应该保持不变,不能被子类重新定义(重写)。子类只能通过新添加方法来扩展功能,父类和子类都可以实例化.所以使用到父类对象的地方,都可以被子类替换掉,因为子类拥有父类所有的方法/属性等等.
2.继承的目的是为了多态,而多态的前提就是子类覆盖并重新定义父类的方法,为了符合LSP,我们应该将父类定义为抽象类,并定义抽象方法,让子类重新定义这些方法,当父类是抽象类时,父类就是不能实例化,所以也不存在可实例化的父类对象在程序里。也就不存在子类替换父类实例(根本不存在父类实例了)时逻辑不一致的可能。
第三:依赖倒转原则
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象 iOS开发Core Animation,是个很好的示例:
需要注意:当父类是抽象类时,父类就是不能实例化,通俗的讲,抽象类是不可以直接使用,我们应该使用它的子类
第四:接口隔离原则(基于接口设计考虑,如UITableviewDelegate + UITableViewDataSource)
这个主要针对后台设计接口的原则
- 1.客户端不应该依赖它不需要的接口: 通俗来理解就是,不要在一个接口里面放很多的方法,这样会显得这个类很臃肿。接口应该尽量细化,一个接口对应一个功能模块,同时接口里面的方法应该尽可能的少,使接口更加灵活轻便。
反面教材:比如客户端A页面,B页面不同功能,却调用了同一个接口,后端只给了一个接口,客户端A页面只需要部分字段,客户端B页面只需要部分字段,造成了这个接口很臃肿.我们可以看到很多App一个页面会调用好多个接口,就是因为遵守了接口隔离原则.
- 2.类间的依赖关系应该建立在最小的接口上:
这个原则有点像单一职责原则,但两个原则还是存在着明显的区别。单一职责原则是在业务逻辑上的划分,注重的是职责。接口隔离原则是基于接口设计考虑。例如一个接口的职责包含10个方法,这10个方法都放在同一接口中,并且提供给多个模块调用,但不同模块需要依赖的方法是不一样的,这时模块为了实现自己的功能就不得不实现一些对其没有意义的方法,这样的设计是不符合接口隔离原则的。接口隔离原则要求”尽量使用多个专门的接口”专门提供给不同的模块。
第五:迪米特法则
一个对象应该对其他对象保持最少的了解.其目的是降低类之间的耦合度,提高模块的相对独立性。从而提高了类的可复用率和系统的扩展性.
缺点:
过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。
案例:使用tableView展示iOS一个页面,一般做法是控制器UIViewController持有tableView,并作为tableView的代理,数据源.如果使用迪米特法则,我们可以新建一个Presenter类, 控制器只需要持有Presenter类,其他什么都不用管,控制器里面代码很干净,Presenter类持有tableView,Presenter类作为tableView的代理,数据源.类似MVP架构. 使用迪米特法则需要注意:
- 1、在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
- 2、在类的结构设计上,尽量降低类成员的访问权限。
- 3、在类的设计上,优先考虑将一个类设置成不变类。
- 4、在对其他类的引用上,将引用其他对象的次数降到最低。
- 5、不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。
- 6、谨慎使用序列化(Serializable)功能。
经典案例: 蔡徐坤与经纪人的关系实例。蔡徐坤只负责浪,经纪人负责处理日常事务,如与粉丝的见面会,与媒体公司的业务洽淡等。
在设计第三方共享库的时候,特别需要注意迪米特法则.
第六:开闭原则
软件对象(类、模块、方法等)应该对于扩展是开放的,对修改是关闭的。比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能,那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码,这要求在设计之初,就应当将客户端和服务端分开。公共部分抽象出来。
经典案例:Objective-C的分类Category
问题由来:
在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。
解决办法:
当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
开闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统。开闭原则可能是设计模式六项原则中定义最模糊的一个了,它只告诉我们对扩展开放,对修改关闭,可是到底如何才能做到对扩展开放,对修改关闭,并没有明确的告诉我们。以前,如果有人告诉我”你进行设计的时候一定要遵守开闭原则”,我会觉的他什么都没说。因为开闭原则真的太虚了。