设计模式的六大原则分别是:单一职责原则、开放封闭原则、里氏替换原则、依赖倒置原则、迪米特原则、接口隔离原则
单一职责原则
现在智能手机发展到今天已经非常强大了,集拍摄、MP3、视频、网络等等一系列工具于一身,给人们的生活带来很大的便捷,一定程度上替代某些产品,把众多功能产品整合于一机的思想是好的,然而在某些功能方面目前还是无法媲美专一功能的产品,比如在拍照摄影方面,还是要用专业的单反设备进行工作;从而说明单一职责的产品有者自己独特的优势。
在设计模式中的单一职责原则:就定义一个类而言,应该仅有一个引起它变化的原因。通俗的讲就是不要在一个类中做过多的功能实现。如果一个类承担功能职责过多,会增加代码间耦合性,导致后期维护、扩展极其困难,可能修改一处会破坏多处的代码设计。
开放封闭原则
现在很多互联网公司的上班时间是弹性制度,所谓弹性制度,就是早来早下班,晚来晚下班,或者一个月内考勤允许多少次迟到……一系列灵活措施,然而弹性上班的前提是一个必须保证8个小时工作时间,这是硬性要求,而有些公司对上班多少小时并不是十分看重,看重是每个月的业绩是否能达到目标或者超额完成。从中可以看出工作时长或业务成绩在公司一般是不轻易修改关闭的,在达到目标的前提,可以针对时间制度进行变化开放。
在设计模式中的开放封闭原则:面对需求的变化,类、模块、函数等应该都是可以扩展的,但不能轻易被修改。在开发中,往往需求会不断的变化,如果每次变化都要把类重新修改一次,这是一件很头庝的事,此时我们可以通过抽象或接口隔离设计最初功能点,当需求变化的时候,可以用继承的方式重写需要实现的功能。简而言之,就是面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。
里氏替换原则
据野史传说中记载,当年宋哲宗最宠爱的妃子是刘德妃。刘德妃虽然深受皇上宠爱,但是却久久不能生育。刘德妃为竞争皇后之位,提高自己的身价,便想出了“借腹怀孕”的诡计。她打算利用身旁的一个长得有些姿色的姓李的侍女,引诱宋真宗上钩。一次在刘德妃沐浴之时,真宗果真临幸了这个李姓侍女,不久这个侍女就怀孕了,这使刘德妃也装作怀孕的样子,其实侍女怀孕是真,刘德妃只是安排计策假装而已。等到十月分娩的时候,“两个”龙种先后呱呱落地。刘德妃采取了狸猫换太子的卑劣手段,让真宗将李姓侍女打入冷宫,可是自己却如愿以偿地登上了梦寐以求的皇后宝座。
用面向对象的思想可以这样理解:
先创建“父类”– 孕妇,孕妇有两大特征:怀孕、生娃。
然后创建两个孕妇的实例:刘德妃和李姓侍女。
最后在运行时,用自己替换了真正的孕妇李侍女,成功欺骗了皇上。
之所以能够在运行时动态替换,是因为刘德妃的行为里完全继承了孕妇的特征。
在设计模式中的里氏替换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象。在开发中将一个基类对象替换成子类对象,程序是不会产生任何错误和异常。在实现的过程中子类的所有方法必须在父类中声明,或子类必须实现父类中的所有方法,如果一个方法在子类中独自存在,没有在父类中声明,则无法以父类的名义调用该方法。下面通过代码加深里氏替换原则的理解。
public class Test { public static void main(String[] args) { Test test = new Test(); LiuFei liuFei = test.new LiuFei(); LiNv liNv = test.new LiNv(); //传入的都是Woman的子类,而getChild方法接受的是父类,这就是替换原则 test.getChild(liuFei); test.getChild(liNv); } public void getChild(Woman w){ w.child(); } abstract class Woman { // 怀孕 void pregnancy() { }; // 生孩子 void child() { }; } class LiuFei extends Woman { @Override void pregnancy() { System.out.println("刘德妃假怀孕"); } @Override void child() { System.out.println("刘德妃假怀孕,不存在生孩子"); } } class LiNv extends Woman { @Override void pregnancy() { System.out.println("李女真怀孕"); } @Override void child() { System.out.println("李女生孩子了"); } } }
依赖倒置原则
想必大家对PC机已经很了解了,基本每天都在使用。PC机的组装就是一个典型模块组件的应用,把每个组件进行分离,不相互依赖;如果其中CUP、主板、内存条、硬盘等某个组件坏了,可以快速替换掉,同时不会其他组件的使用。之所以如此方便,是因为组件间没有任何的细节依赖,都是通过对插式进行连接的,所以PC中每个组件都会开放一个或多个插入式接口供使用者。
在设计模式中的依赖倒置原则:高层模块不应该依赖底层模块,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
在java中,抽象一般是指接口或抽象类,它们不会被实例化;细节就是实现类,实现接口或抽象的方法来实现细节。高层模块就是调用者,低层模块就是具体实现类。模块间的依赖通过抽象发生,同样的实现类间也不能直接发生任何依赖,也需通过接口或者抽象类产生依赖。简单理解就是每个模块或者实现类之间不应该发生依赖,需要通过对插式的方式进行关联,正确方式下图:
迪米特原则
在设计模式中迪米特原则:称之为最少知识原则,就是在类的结构设计上,每一个类都应该尽量降低成员的访问权限,也就是说,一个类包装好自己的private状态,不需要让别的类知道字段或行为就不要公开出来。如果两个类不需彼此之间进行通信,那么两个类就不应该发生直接的相互作用。如果其中一个类需要调用另一个类的某个方法的话,可以通过第三者转发这个调用来降低对象之间的耦合度。总而言之,迪米特原则就是尽量避免的让两个对象间发生相互作用。
接口隔离原则
在很多课程辅导中,而对于同一知识点,老师可能也要进行不同的课程设计来满足不同层级的学生,往往会分为基础、进阶知识点。
很多时候我们就会把两个不同级别的实现定义成一个公共的接口,这样省事又简单:
public interface CourseLevel { //基础课程1 void baseCourse1(); //基础课程2 void baseCourse2(); //进阶课程1 void advancedCourse1(); //进阶课程2 void advancedCourse2(); }
按照上面的定义,如果使用过程中,没有进行分类,老师不小心把进阶课程讲给了基础学生,岂不是糟了,基础学生能听得懂吗?为了解决上面类似问题,引进了接口隔离原则
在设计模式中接口隔离原则:一个类对另外一个类的依赖应该建立在最小的接口上。感觉好拗口,简单一点就是,建立单一接口,不要建立臃肿庞大的接口。再通俗的一点讲:接口尽量细化,同时接口中的方法尽量的少。我们要为每个类建立专用的接口,而不用试图建立一个庞大而全的接口让类去实现。
针对上面课程的例子,我们按照接口隔离原则进行改进,把基础,进阶课程分为两个不同接口,避免低级问题再次出现。
public interface BaseCourseLevel{ //基础课程1 void baseCourse1(); //基础课程2 void baseCourse2(); } public interface AdvancedCourseLevel{ //进阶课程1 void advancedCourse1(); //进阶课程2 void advancedCourse2(); }
单一职责原则与接口隔离原则,两者定义要求分离,但分离的内容是不一样的。
单一职责原则强调是职责,所谓职责就是需求的实现要进行分离,把不同需求封装成不同的模块,没有要求减少接口的方法标准。
接口隔离原则强调是接口单一,尽量为每个类提供专门的接口,就是为不同模块不同功能提供对应的接口,而不是建立庞大的接口。
感谢:
《大话设计模式》、《设计模式之禅》、张拭心之设计模式专栏