文章目录
- 1.开闭原则:对扩展开放,对修改关闭
- 2.里氏代换原则:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法
- 3.依赖倒转原则:高层模块不应该依赖低层模块,两者都应该依赖其抽象
- 4.接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上
- 5.迪米特原则:不直接通信,由中介转发
- 6.合成复用原则
1.开闭原则:对扩展开放,对修改关闭
需要使用接口和抽象类
在程序需要进行拓展的时候,不能去修改原有的代码
只要抽象的合理,可以基本保持软件架构的稳定,而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。
例:搜狗输入法的皮肤设计
可以为其定义一个抽象类(AbstractSkin),每个具体的皮肤(DefaultSpecificSkin和HeimaSpecificSkin)是其子类。用户窗体可以根据需要选择或者增加新的主题,而不需要修改原代码,满足开闭原则。
代码可见:com.itheima.principles.demo1
附下载链接:后续补充,可能没补
2.里氏代换原则:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法
任何基类可以出现的地方,子类一定可以出现
子类可以扩展父类的功能,但不能改变父类原有的功能
子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法
如果通过重写父类的方法完成新的功能,整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。
例:正方形不是长方形
在数学领域中,正方形毫无疑问是长方形,它是一个长宽相等的长方形,所以我们让正方形继承长方形
代码可见:com.itheima.principles.demo2.before
假如我们把一个普通长方形作为参数传入resize方法,就会看到长方形宽度逐渐增长的效果,当宽度大于长度代码就会停止,符合预期。假如我们再把一个正方形作为参数传入resize方法后,就会看到正方形的宽度和长度都在不断增长,代码会一直运行下去,知道系统产生溢出错误。所以普通的长方形是适合这段代码的,正方形不适合。
在resize方法中,Retangle类型的参数是不能被Square类型的参数所代替,因此Square类和Retangle类之间的继承关系违反了里氏代换原则,他们之间的继承关系不成立,正方形不是长方形。
改进:抽象出来一个四边形接口Quadrilateral,让Retangle类和Square类实现Quadrilateral接口
代码见:com.itheima.principles.demo2.after
3.依赖倒转原则:高层模块不应该依赖低层模块,两者都应该依赖其抽象
高层模块不应该依赖低层模块,两者都应该依赖其抽象
对抽象进行编程,不要对实现进行编程
例:组装电脑
先要组装一台电脑,需要配件cpu,硬盘,内存条。只有这些配置都有了,计算机才能正常的运行。选择cpu有很多选择,如Intel,AMD等,硬盘可以选择希捷,西数等,内存条可以选择金士顿、海盗船等
代码可见:com.itheima.principles.demo3.before
这台组装的电脑的cpu只能是Intel的,内存条只能是金士顿的,硬盘只能是希捷的,但对用户不友好,用户有了机箱想按照自己的喜好,选择自己的配件。
改进:修改Computer类,让Computer类依赖抽象(各个配件的接口),而不是依赖各个组件具体的实现类。
代码可见:com.itheima.principles.demo3.after
4.接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上
客户端不应该被迫依赖于它不使用的方法,一个类对另一个类的依赖应该建立在最小的接口上
A类声明了方法1和方法2两个方法,B类继承A类,拥有了A类方法1和方法2两个功能,但是B类只需要方法1的功能,不需要方法2的功能,B类被迫依赖于不使用的方法2。
改进:抽象出A接口和B接口。A接口声明方法1,B接口声明方法2,C类需要方法1的功能,就实现A接口就好了。
例:安全门案例
1号安全门,具有防火、防水、防盗的功能。将防火、防水、防盗的功能提取成一个接口,形成一套安全门规范。
2号安全门只具有防火、防盗功能。那么实现1号安全门抽象出的安全门规范接口就违背了接口隔离原则,被迫依赖了2号不使用的防水功能。
代码可见:com.itheima.principles.demo4.before
改进:将接口功能进行拆分,按需实现
代码可见:com.itheima.principles.demo4.after
5.迪米特原则:不直接通信,由中介转发
又叫最少知识原则。只和你的直接朋友交谈,不跟陌生人说话。比如说软件工程师受雇与软件公司,要做软件的甲方与软件公司沟通签订合约,而软件工程师与甲方之间不做接触。
如果两个软件实体无须直接通信,name就不应该发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
例:明星与经纪人的关系
明星全身心投入艺术,所以许多日常事务由经纪人负责处理,如粉丝见面会、和媒体公司的业务洽谈等。经纪人是明星的朋友,明星和粉丝与媒体公司是陌生人,他们之间由经纪人进行协调,所以适合使用迪米特法则。
代码可见:com.itheima.principles.demo5
6.合成复用原则
尽量先使用组合或聚合等关联关系来实现,其次才考虑使用继承关系来实现
类的复用分为继承复用和合成复用两种。
继承复用:
- 优点:简单,易实现
- 缺点:
- 继承复用破坏了类的封装性,因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为白箱复用。
- 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
- 限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,在运行时不可能发生变化。
合成复用:
采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能。
- 优点:
- 维持了类的封装性,因为成分对象的内部细节是新对象看不见的,所以这种复用又称为黑箱复用
- 对象间的耦合度低,可以在类的成员位置声明抽象
- 复用的灵活性高,这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象
例:汽车分类管理
汽车按“动力源”划分可分为汽油汽车、电动汽车等。按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,组合有很多。
从上图中我们可以看到使用继承复用产生了很多子类,如果现在又有新的动力源或者新的颜色的话,就需要在定义新的类。比如新出了光能汽车,白色的,红色的…类爆炸了
改进:继承复用改为聚合复用,这样颜色就只是汽车的特征属性了。