单一职责原则
用于控制类的粒度,就是一个类只做一件事。从编码方面一个类的职责越多,该类修改越频繁更不用谈复用并且类之间的耦合高。单一职责原则并不是让类的职责变少,而是在保证类的低耦合的情况下实现高内聚。例如,在做一个图形化数学应用时不符合单一职责原则的类如下:
class Rectangle{
public int perimeter(){}//周长
public int area(){}//面积
public void draw(){}//画线
public void full(){}//填色
}
因为画线、填色是GUI需要负责的,而不属于长方形的职责范围所以需要将这两个方法独立出来作为一个工具类来使用。
开放-封闭原则
对扩展开放,对修改关闭。就是对原来的类尽量少甚至不作修改,达到功能的扩展。这就需要通过一个接口类来实现了,原理就是封闭变化。例如拿手开门:
class Hand{
public void do(OldDoor door){
if(door.open){
door.open=false;//懒得写set、get,不要模仿
}else{
door.open=true;
}
}
}
class OldDoor{
public boolean open=false;
}
现在要开一个NewDoor:
class NewDoor{
public boolean open=false;
}
此时hand的do方法就要修改源码,这就不符合“封闭"。此时需要加入一个接口来封闭变化。
public interface Action{
public void toggle();
}
class Hand{
private Action action=new Action();
public void do(){
action.toggle();
}
}
class NewDoor implements Action{
public boolean open=false;
public void toggle(){
if(this.open){
this.open=false;
}else{
this.open=true;
}
}
}
现在所有的门只需要实现了这个接口那么都能正常使用,并且符合对修改封闭,对扩展开放了。
里氏替换原则
用子类替换父类不会对程序的正确性造成影响。这就意味着子类的方法不会对父类有影响,同时父类不会有子类难以继承的方法或成员。例如
class Quadrangle{
private int width;
private int length;
public int getWidth(){}
public int getLength(){}
}
class Square extends Quadrangle{public boolean test(int w,int l){}}
//正方形没有宽高只有边长,并且要求父类宽高一直需要加上约束
class Rectangle extends Quadrangle{}//正常
此时,就需要父类尽量减少会受到约束的成员变量与方法,甚至成为一个接口,子类完全实现父类方法。
class Quadrangle{
public int getWidth(){}
public int getLength(){}
}
class Square extends Quadrangle{
private int side;//边长
public int getSide(){}
}
class Rectangle extends Quadrangle{
private int width;
private int length;
}
依赖倒置原则
传统的,由高层模块调用底层模块的方法。这样当底层模块方法变化高层也需要修改。依赖倒置原则就规定高层模块定义接口、底层模块依赖于抽象(中间层/接口)。这样高层不依赖于底层的接口而依赖于抽象(中间层/接口)。例如
class Client{
public void do(Server server){}
}
abstract class Server{
public abstract void service(){}
}
class Waiter extends Server{
@Override
public void service(){}
}
接口隔离原则
一个接口干一件事,避免肥接口。一个接口好比一个演员,你不能让一个演员分饰多角(人格分裂当我没说)。
比如一个销售系统,分为门户(查询)、前台(下单)、后台(出货)。那么肥接口如下:
public Order(Something something){
if(query){
//查询
}else if(insert){
//下单
}else{
//出货
}
}
正常情况应该把if中的判断分为几个接口。
合成复用原则
通常类的复用分为继承复用和合成复用两种。继承有它的优点但是存在如下问题:
- 破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
- 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
- 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
组合的优点如下:
- 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
- 新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
- 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。
总结就是继承耦合高。继承像妻子,离婚手续多,组合像情妇,下一个更乖。
think in java也说虽然处处强调继承,但是应该先考虑用组合。
迪米特法则
类应该与尽量少的类进行交互,降低耦合。也叫知识最少原则。
- 只和必须直接调用的类交流
- 和类的交流尽量少,比如A需要调用B类的1、2、3方法应该把三个方法封装为一个供A调用
- 一个类公开的public属性或方法越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大。所以,我们开发中尽量不要对外公布太多public方法和非静态的public变量,属性或方法尽量内敛。