面向对象的7种设计原则
设计模式的目的
设计模式是为了让程序具有更好的代码复用性,可读性,可扩展性,可靠性,使程序实现高内聚低耦合的特性。
设计模式的原则 就是我们在编码的过程中,应该遵守的原则,也是各种设计模式的基础。
设计原则的核心思想
1.找出应用中可能需要变化之处,把它们独立出来,不要喝那些不需要变化的代码混合在一起
2.针对接口/抽象编程,而不是针对实现编程
3.为了高内聚,松耦合的代码设计而努力
1.单一职责
即一个类应该只负责一项职责。
如类A负责两个不同的职责,职责1和职责2。当职责1因为需求变更修改类A的时候,可能造成职责2执行错误,所以需要将类A的粒度拆分为 类A1-负责职责1; 类A2-负责职责2
2.开闭原则(open closed principle)
1.开闭原则是编程中最基础最重要的原则。
2.一个软件实体 比如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方),用抽象构建框架,·用实现扩展细节。
举例:画图
1.定义抽象基类(提供方):abstract class Shape{
//定义抽象方法
public abstract void draw();
}
2.各种图形类继承Shape,实现抽象方法draw
比如绘制长方形:class Rectangle extends Shape{
//实现draw方法...
}
3.用于绘制图形的类(使用方)
class Person{
public void drawShape(Shape s){
s.draw();
}
}
- main函数调用
public static void main(String[] args){
Person p = new Person;
p.drawShape(new Rectangle())
}
5.此时如果需要扩展别的图形,不需要修改使用方(Person)的代码。只需新增类,继承Shape,实现Draw函数。
3.迪米特原则
1.一个对象应该对其他对象保持最少的了解,类与类的关系越密切,耦合度越大。
2.迪米特原则又称最少知道原则,只与直接的朋友通信。
3.直接的朋友只有两个对象之间有耦合关系,就说这两个对象之间是朋友关系。
4.耦合的方式很多,依赖 、关联、 组合、 聚合等
5.出现在成员变量,方法函数,方法返回值中的类为直接朋友。而出现在局部变量中的类不是直接朋友
6.迪米特法则的核心是降低类之间的耦合。每个类或多少都有一些必要的依赖,因此迪米特法则只是要求降低类之间的耦合关系,并不是要求完全没有依赖关系。
比如类A 和 类B
class A{
B b1 = new B(); //1.成员变量的直接朋友
public void setB(B b2) //2.方法函数的直接朋友
b2.fun();
}
public B getB(){
B b3 = new B();
return b3;//3.方法返回值的直接朋友
}
public void test(){
B b4 = new B();
b4.fun()//4.局部变量中的类不是直接朋友
}
}
4.依赖倒置(倒转)原则
1.高层模块不应该依赖低层模块,二者都应该依赖其抽象
2.抽象不应该依赖细节,细节应该依赖抽象
3.依赖倒置的核心是面向接口编程
4.抽象出来的东西要保持稳定性,抽象指的是接口或者抽象类,细节就是具体的实现。
比如用户接受消息举例:
抽象出一个单独的接口 IReceive:getInfo()
消息类型Email和Weixin等类实现接口IReceive,实现getInfo函数-->比如println("hello ,接收微信消息")
用户Person类中,增加函数receive(IReceive mess){mess.getInfo}
客户端调用:Person p = new Person
p.receive(new Email()) //接收邮件消息
p.receive(new Wexin()) //接收微信消息
4.1依赖关系传递的三种方式
interface ITV{
public void play()
}
1.参数接口传递(用户开电视)
public void open(ITV tv){
tv.play()
}
2.构造方法传递
class Person{
public ITV tv;
public Person(ITV tv){ //构造方法
this.tv = tv;
}
}
3.setter方法传递
class Person{
public ITV tv;
public void setTv(ITV tv){//setter方法
this.tv = tv;
}
}
4.2依赖倒装原则的注意事项和细节
1.低模块尽量都要有抽象类或接口
2.变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和变化
5.接口隔离原则
即一个类对另外一个类的依赖应该建立在最小的接口上。
比如接口A 含有3个方法 f1,f2,f3
类B 只需要使用 f1,f2 ;类C只需要使用f3。此时如果让类B和C实现接口A。就违背了接口隔离原则。
解决的方式是对接口A进行拆分为接口A1和接口A2-->A1:f1,f2 ;A2:f3 ,类A实现接口A1,类B实现接口A2
6.里氏替换原则
1.里式替换原则是1988年由麻省理工学院的一位姓里的女士提出。实现的原理是:如果对每个类型为TI的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序p中所有的对象o1都代换成o2时,程序p的行为没有发生变化。那么类型T2是类型T1的子类型。即所有引用基类的地方必须能透明的使用其子类对象。
2.使用继承时候,遵循里氏替换原则,在子类中尽量不要重写父类的方法。
3.通俗的做法:原父类和子类都继承一个更通俗的基类,原有的继承关系去掉。
4.如果子类想要使用原父类中的方法,可以用依赖 聚合 组合等关系 代替继承。
举例
比如原 A类是父类,B是子类,公用方法f1,当子类B重写f1时,就违背里氏替换原则
解决方法:声明基类接口Base,提供函数f1;
让类A和类B 都实现接口Base,实现函数f1;
若类B 中想要使用类A 的方法,可以通过依赖,聚合,组合等关系(就是A作为参数在类B中使用)
7.合成复用原则
尽量使用合成 或者聚合的方式处理类对象之间的耦合,减少使用继承关系。
合成或者聚合的三种实现方式
比如类A 和类 B , 类B想要使用类A。
1.成员变量/构造方法
A a1 = new A()
2.方法函数
void fun(A a1){}
3.setter方法
A a1;
void setA(A a1){
this.a1 = a1;
}