43. [Java]设计模式六大原则

本文由大量内容引用自:去看原文

1)单一指责原则

2)里氏替换原则

3)依赖倒置原则

4)借口隔离原则

5)迪米特原则

6)开闭原则

43.1. 单一职责原则

每一个类只负责一个职责

如果一个类负责两个不同的职责,当其中一个职责对应的需求改变时,由此对该职责的更改可能会影响另一个职责,从而导致该类的故障。

  • 优点:

    • 降低了类的复杂度,一个类只负责一个职责

    • 提高了类的可读性、可维护性

    • 降低变更引起的风险

43.2. 里氏替换原则

所有引用基类的地方必须能透明地使用其子类的对象

由定义可知,在使用继承时,遵循里氏替换原则,在子类中尽量不要重写和重载父类的方法 继承包含这样一层含义:父类中凡是已经实现好的方法(相对抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。 继承作为面向对象三大特性之一,在给程序设计带来巨大遍历的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障。

43.3. 依赖倒置原则

  • 高层模块不应该依赖于低层模块,二者都应该依赖于其抽象

  • 抽象不应该依赖于细节,细节应该依赖于抽象

栗子🌰:

  • 类A直接依赖类B,如果要将类A改为依赖类C,则必须通过修改类A的代码来达成。此时,类A一般是高层模块,负责复杂的业务逻辑,类B和类C是低层模块,负责基本的原子操作;修改A会给程序带来风险

  • 将类A修改未依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或类C发生联系,则会大大降低修改类A的记几率

依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。

以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类,使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。依赖倒置的中心思想是面向接口编程。

  • 依赖关系的传递有三种

    • 接口传递

    • 构造方法传递

    • setter方法传递

43.4. 接口隔离原则

  • 客户端不应该依赖于它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。

classimpl

看一段代码(C++?):

这里面有依赖关系传递中的接口传递

interface I{
    void method1();
    void method2();
    void method3();
    void method4();
    void method5();
}
class A{
    public void depend1(I i){
        i.method1();
    }
    public void depend2(I i){
        i.method2();
    }
    public void depend3(I i){
        i.method3();
    }
}
class C{
    public void depend1(I i){
        i.method1();
    }
    public void depend2(I i){
        i.method4();
    }
    public void depend3(I i){
        i.method5();
    }
}
class B:I{
    public void method1(){
        Console.WriteLine("类B实现接口I的方法1");
    }
    public void method2(){
        Console.WriteLine("类B实现接口I的方法2");
    }
    public void method3(){
        Console.WriteLine("类B实现接口I的方法3");
    }
    public void method4(){}
    public void method5(){}
}
class D:I{
    public void method1(){
        Console.WriteLine("类B实现接口I的方法1");
    }
    public void method2(){}
    public void method3(){}
    public void method4(){
        Console.WriteLine("类B实现接口I的方法4");
    }
    public void method5(){
        Console.WriteLine("类B实现接口I的方法5");
    }
}
class Program
{
    static void Main(string[] args)
    {
        A a=new A();
        a.depend1(new B());
        a.depend2(new B());
        a.depend3(new B());
        
        C c=new C();
        c.depend1(new D());
        c.depend2(new D());
        c.depend3(new D());
        Console.ReadLine();
    }
}

类A依赖接口I中的方法1,方法2,方法3,类B是对类A依赖的实现;类C依赖接口I中的方法1,方法4,方法5,类D是对类C依赖的实现。对于类B和类D来说,虽然存在用不到的方法(红色标记所示),但由于实现了接口I,所以也必须要实现这些用不到的方法。

上面的接口I应该拆分为三个接口:

classimpl2

interface I{
    void method1();
    void method2();
    void method3();
    void method4();
    void method5();
}
class A{
    public void depend1(I i){
        i.method1();
    }
    public void depend2(I i){
        i.method2();
    }
    public void depend3(I i){
        i.method3();
    }
}
class C{
    public void depend1(I i){
        i.method1();
    }
    public void depend2(I i){
        i.method4();
    }
    public void depend3(I i){
        i.method5();
    }
}
class B:I{
    public void method1(){
        Console.WriteLine("类B实现接口I的方法1");
    }
    public void method2(){
        Console.WriteLine("类B实现接口I的方法2");
    }
    public void method3(){
        Console.WriteLine("类B实现接口I的方法3");
    }
    public void method4(){}
    public void method5(){}
}
class D:I{
    public void method1(){
        Console.WriteLine("类B实现接口I的方法1");
    }
    public void method2(){}
    public void method3(){}
    public void method4(){
        Console.WriteLine("类B实现接口I的方法4");
    }
    public void method5(){
        Console.WriteLine("类B实现接口I的方法5");
    }
}
class Program
{
    static void Main(string[] args)
    {
        A a=new A();
        a.depend1(new B());
        a.depend2(new B());
        a.depend3(new B());
        
        C c=new C();
        c.depend1(new D());
        c.depend2(new D());
        c.depend3(new D());
        Console.ReadLine();
    }
}

说到这里,可能会觉得接口隔离原则和之前的单一职责原则很相似,其实不然。

一,单一职责注重职责,而接口隔离原则注重对接口依赖的隔离;二,单一职责是约束类,其次是方法,针对的是程序中的实现和细节;而接口隔离原则约束的是接口,针对的是抽象,程序整体框架的构建。

43.5. 迪米特法则

一个对象应该与其他对象保持最少的理解,类与类之间的关系越密切,耦合度就越大。

对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public 方法,不对外泄露任何信息。

43.6. 开闭原则

开闭原则是最基础的原则,最根本的原则

一个软件实体如类,模块和函数应该对扩展开放,对修改关闭。用抽象构建框架,用实现扩展细节。 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。 当我们遵循前面介绍的5大原则,以及使用23种设计模式的目的就是遵循开闭原则。