在进行软件架构工作时,需要遵循面向对象的设计原则,注意体会这些原则在解决“变化”和“依赖”问题中的效果。前人共总结出7条通用的原则(降低耦合性,提高扩展性):
系统中的每一个对象只用于完成一种职责,即“高内聚,低耦合”;对外只提供一种功能,引起类变化的原因应该只有一个(并不是说只有一个变量,而是只有一个标准);
一般情况下,我们在设计一个类的时候会把与该类有关的操作(属性和方法)都组合到这个类中。这样做的后果就是讲这个类中的某些职责会“耦合”在一起,在想要改变一个职责时,难免其他的职责不会受到影响,造成了程序的“僵硬”;
但是,如果每一个职责都单独写出一个类,无疑效率会大大降低,因此,运用单一职责原则是为了将能够“引起类变化的职责”提取出来封装,做到各司其职,不会因为一个类中的某一个职责发生了变化,要将整个类重写;
例如,在设计接口时,将属性和行为定义在一起,会造成业务对象和业务逻辑这两种职责的混合,违背了单一职责原则(SRP)
public interface Tank{
void setSpeed(int speed);//属性
void shoot(Bullet bullet);//行为
}
在这种情况下,如果setSpeed()实现方法需要改变,而接口中的多个职责之间存在“耦合”,可能变化的不仅仅是接口中setSpeed()方法,shoot()行为也会变化。这在逻辑简单的时候还可以解决,如果这两个方法很复杂,几百行代码,还有多线程,修改起来会非常麻烦。因为需要使用单一职责原则“解耦”
public interface TankSpeed{
void setSpeed(int speed);
}
public interface TankShoot{
void shoot(Bullet bullet);
}
总结:
1. 一个合理的类,应该只有一个引起它变化的原因,即单一职责;
2. 在没有变化征兆的情况下去使用SRP是不明智的;
3. 在需求发生变化时,应该应用SRP原则重构代码;
用于规范使用继承:在任何父类出现的地方都可以用它的子类替代。也就是说:父类替换为子类不会出现错误或者异常,但是子类替换为父类就会出现问题。要达到这个目标,需要遵从以下4点:
1. 子类必须完全实现父类的方法;
2. 子类可以扩展自己的特性;
3. 覆盖或者实现父类的方法时的形参范围不能缩小;
4. 覆盖或者实现父类的方法时的输出结果可以被缩小;
其实LSP的意思就是使用继承时,子类需要更具体,同时需要更宽容(可以接受的形参范围更大);
用于规范依赖关系:要依赖抽象类,不要依赖于具体的实现。因为抽象的类具有更高的可复用性和可维护性;也就是要正对接口编程,不要针对实现编程;
抽象指的是抽象类或者接口,不能被实例化;
1. 高层模块不应该依赖低层模块,两者都应该依赖于抽象;
2. 抽象不应该依赖于具体实现;
3. 具体实现应该依赖于抽象;
目的是为了实现模块间的松耦合,这个原则也是几个设计原则中最难实现的,例如开闭原则(对扩展开放,对修改关闭)就是建立在这个原则基础上的;那么如何实现注入反转原则呢?
1. 通过构造函数传递依赖对象(抽象类或者接口);
2. 通过setXXX方法传递依赖对象;
3. 通过声明接口,实现依赖对象;
例如需求是实现一辆坦克发射出一定速度的子弹:
public class Tank{
void shoot(Bullet bullet){
bullet.speed();
}
}
public class Bullet{
void speed(){
System.out.println("Shoot bullet in Speed");
}
}
//场景类
public class Battle{
public static void main(String[] args){
Tank myTank = new Tank();
Bullet myBullet = new Bullet();
myTank.shoot(myBullet);
}
}
这里,Tank和Bullet两个类之间的耦合性就太高了,Tank只能发射Bullet,以后需求多了,不能喷火,不能加导弹。。。因此,需要降低两者之间的耦合性:
public interface Tank{
void shoot(Weapon wp);//只要是个weapon都能使用。。。太强大了
}
public Interface Weapon{
void speed();//可以任意设定速度,甚至可以加入变化的速度
}
//下面就是通过这两个借口去实现具体的MyTank类和MyWeapon类,满足需求了。。。。省略
总结:依赖注入原则就是通过在实现类之前注入抽象类或者接口,将接口作为实现类之间的”解耦”桥梁。
这里的借口不仅包括类接口(通过interface关键字定义的接口),类也被当做是一种借口—相对于实例对象来说的一个借口;
ISP原则和SRP原则有些类似,不同之处在于:
单一职责原则要求的是类和接口的职责单一,是从业务逻辑上的划分;而接口分离原则要求的是接口中的方法尽量少,从而避免强迫实现接口中不需要使用的方法。
一个对象应该对其他对象尽可能少地了解;也就是说,当第一个类希望去访问第三个类中的数据时,如果存在第二个类,既与第一个类关系紧密,又和第三个类关系紧密,那么没有必要第一个类和第三个类之间再相互了解,而是应该通过第二个类。
对类的改动是通过增加代码进行的,而不是通过修改原有的代码进行的—借助于抽象,继承和多态
其实开闭原则是前面5种原则的一个抽象总结,要想实现开闭原则,还需要靠前面5个原则去完成。