设计原则
1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起.------分离变化与不变化的部分,框架实际就是把业务与其余不变的代码进行分离,好让程序员更好的进行 if else 操作.
2.针对接口编程,不针对实现编程.------面向抽象,不面向具体的实现,代码如果依赖于具体的实现可拓展性比较差,牵一发而动全身.
java分为编译期和运行期,在编译期如果编写的代码依赖与具体的实现,后期拓展功能会频繁的更改代码.
但是当编译期依赖于抽象的时候,在运行期会根据传入具体的类型执行代码,所以不需要频繁更改代码.
举个例子:
定义一个接口Animal,会发出声音
public interface Animal { void makeSound(); }
定义两个具体的类Cat,Dog,实现接口Animal
public class Cat implements Animal{ @Override public void makeSound() { System.out.println("喵喵喵"); } }
public class Dog implements Animal{ @Override public void makeSound() { System.out.println("汪汪汪"); } }
编写测试类
public class AnimalTest { public static void main(String[] args) { Cat cat = new Cat(); test(cat); } private static void test(Cat cat) { cat.makeSound(); } }
可以看到,如果test类依赖于具体的cat类型,如果这时候需要传入dog参数,就需要在改写一遍代码如:
public class AnimalTest { public static void main(String[] args) { Cat cat = new Cat(); Dog dog = new Dog(); test(cat); test(dog); } private static void test(Cat cat) { cat.makeSound(); } private static void test(Dog dog) { dog.makeSound(); } }
如果后期在有别的动物出现,就需要不停的重载test方法,其实我们这边关心的只是动物发出的声音,完全可以用Animal类来替代具体的类,比如
public class AnimalTest { public static void main(String[] args) { Cat cat = new Cat(); Dog dog = new Dog(); test(cat); test(dog); } private static void test(Animal animal) { animal.makeSound(); } // private static void test(Cat cat) { // cat.makeSound(); // } // private static void test(Dog dog) { // dog.makeSound(); // } }
这边参数直接声明成Animal接口,在运行期根据传入的具体类型来执行子类的实现方法,这种操作也叫向上转型.
这样实现的话代码就比较简洁,而且代码拓展性与可维护性也比较好,就算在来100只动物,test类不需要更改.
3.多用组合,少用继承. ------ 继承会强制子类继承某些不需要的方法或者实现,但是组合的话可以自由添加与删除.继承设计子类是在编译时静态决定,所有子类会具有相同行为.组合能拓展对象的行为,可以在运行时动态拓展.通过组合也能实现代理模式拓展功能,无需修改原来的代码,也符合以下的开闭原则,对于修改关闭.
4.为了交互对象之间的松耦合设计而努力. ---------还是解耦,降低类与类之间的依赖,只要接口的规定被遵守,整个系统就更有弹性,话说如果需求涉及到接口改变怎么办,这时候估计就考验架构师的功底和经验了.
5.类应该对拓展开放,对修改关闭.-------理解的话就是字面的意思,在不修改源代码的基础上拓展功能,和代理有点像,但是不应该为了设计而设计,这只是一个原则, 应该在最可能修改的地方应用这些原则,如果大量应用,可能会使项目变得很复杂,要设计的完美,还需要大量的实践,看来编程还是挺吃工作经验的,没几个项目经验设计不出好的项目.
6.要依赖抽象,不要依赖具体的类(依赖倒置原则).------'依赖'可以理解成实例化一个类,当A的方法或者变量中实例化类B的时候,我们就可以说类A依赖于类B.要想达成依赖倒置原则,就需要依赖于抽象,而不是具体的实现类,画图解释.
在类A中创建了类B,类C,类D,我们就可以说A依赖于B,C,D,当B,C,D改变的时候或者我们需要在A中增加新的类的时候我们就需要修改A中的代码
当使用接口或者抽象类的时候,A依赖于抽象类或者接口,因为B,C,D需要实现抽象类或者接口,所以B,C,D依赖于抽象类或者接口,我们可以看到整个样子是呈现出倒置状态,这个时候就是我们所说的依赖倒置.
这里A就是高层组件,高层组件指的是其他底层组件定义其行为的类,比如A的行为就是B,C,D的方法定义的,所以A是高层组件,B,C,D是低层组件.
用高层组件和低层组件来解释依赖倒置原则就是:
不能让高层组件依赖于低层组件,高层组件和低层组件都应该依赖于抽象.
三个方针来实现这一个目标:
1.变量不可以持有具体类的引用.
2.不要让类派生具体类.
3.不要覆盖基类中已经实现的方法.
还是老样子,设计模式会增加系统的复杂度和类的数量,过度设计不一定是好事,还是要把握好那个度来达到依赖倒置原则.
7.最少知识原则.------系统之间的类相互依赖不要太多,外观模式就达到了这一点,通过定义一个中间类,来解耦客户端和系统之间的依赖.
在平常的编码中,我们应该调用以下对象的方法:
1.该对象本身
2.参数传递的对象
3.方法创建(比如工厂方法)或者实例化的对象
4.对象的任何组件
8.好莱坞原则------别找我,我会找你. 用模板方法为例子,底层组件可以通过模板方法倒钩到高层组件上,在高层组件需要的时候直接调用即可.具体参考模板方法.
spring框架中也应用了这个原则,spring 的 IOC 控制反转就是这个原则,把类的创建以及类于类之间的依赖都交给spring 去维护和管理,当类依赖于另一个类的时候,不需要去向spring申请,而是
spring 通过 配置 以及 注解 的方式来找到类于类之间的依赖,通过 DI 来进行注入, 所以类和类 之间的依赖通过 用户来定义,整个模块处于一个可控制的范围中.
9.单一职责原则------一个类应该只有一个引起变化的原因. 虽然这样做会增加类和接口的数量,但是不这么做的话在拓展功能的时候会修改大量代码.