设计原则

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.单一职责原则------一个类应该只有一个引起变化的原因. 虽然这样做会增加类和接口的数量,但是不这么做的话在拓展功能的时候会修改大量代码.

原文地址:https://www.cnblogs.com/lishuaiqi/p/11074933.html