欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

编码基础指南--七项设计原则(上)

最编程 2024-04-01 17:06:21
...

这是我参与新手入门的第3篇文章

简述

我们总想写出正确的代码,而设计原则就是我们写出正确代码可依据的准则。因为,原则是经过长期经验总结所得出的合理化的现象。

我们常用的七大设计原则(也称为设计模式的七大原则或面向对象的七大设计原则):

  1. 单一职责原则 (Single Responsibility Principle, SRP)
  2. 接口分隔原则 (Interface Segregation Principle, ISP)
  3. 依赖倒转原则 (Dependence Inversion Principle, DIP)
  4. 组合/聚合复用原则 (Composite/Aggregate Reuse Principle, CARP)
  5. 开放-关闭原则 (Open-Closed Principle, OCP)
  6. 里氏替换原则 (Liskov Substitution Principle, LSP)
  7. 迪米特法则(Law Of Demeter, LoD)

一般地,可以把这七个原则分成了以下两个部分:

设计方法:单一职责原则、接口分隔原则、依赖倒置原则、组合/聚合复用原则

设计目标:开闭原则、里氏代换原则、迪米特原则

这篇文章先聊聊「设计方法」相关的原则。

单一职责原则 (Single Responsibility Principle, SRP)

顾名思义,单一职责原则就是一个类/接口/方法有且仅有一个职责。

更官方的说法是,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分(There should never be more than one reason for a class to change)。 可以这样理解,如果类职责不「单一」,每个职责的变化都有各自的原因。

单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性

遵循单一职责原则有以下优点:

  1. 降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多。
  2. 提高类的可读性。当类遵循单一职责时,你自然很容易知道此类是干什么的。
  3. 增加可复用性。如果一个方法是多个职责的,你只想使用其中的一个职责,这个方法就是不可复用的。比如,修改用户信息。用户信息有用户名,信息,密码,家庭地址等等。如果你把所有的信息修改放到一个方法中,那么修改密码功能就不能复用这个方法了。
  4. 降低变更引起的风险。当一个类有多个职责时,你改变其中一个职责,就有可能影响其他职责。

接口分隔原则 (Interface Segregation Principle, ISP)

概念理解:接口的设计应该遵循最小接口原则,即不能强迫用户去依赖那些他们不使用的接口。

比如,接口A,定义了三个方法:

public interface A {
    
    void method1();
    
    void method2();
    
    void method3();
}

而类B,只依赖A的两个方法:

public class B implements A{
    @Override
    public void method1() {
        System.out.println("do method 1");
    }

    @Override
    public void method2() {
        System.out.println("do method 2");
    }

    @Override
    public void method3() {
        // do nothing
    }
}

这样,A接口就是没有遵循最小接口原则。按接口分隔原则,就应该将接口拆分成使B类不会依赖那些他们不使用的接口。

// 将接口A拆成A1和A2
public interface A1 {
    
    void method1();
    
    void method2();
}

public interface A2 {
    
    void method3();
}

// B只需要依赖A1
public class B implements A1{
    @Override
    public void method1() {
        System.out.println("do method 1");
    }

    @Override
    public void method2() {
        System.out.println("do method 2");
    }
}

依赖倒转原则 (Dependence Inversion Principle, DIP)

依赖倒置原则定义

A. 上层模块不应该依赖底层模块,它们都应该依赖于抽象(High level modules should not depend upon low level modules. Both should depend upon abstractions)

B. 抽象不应该依赖于细节,细节应该依赖于抽象(Abstractions should not depend upon details. Details should depend upon abstractions)

什么是底层模块?我们可以把不可分隔的逻辑(最基础的方法)当成底层模块,而组装或使用他们的逻辑就是上层模块。「上层模块和底层模块都应该依赖于抽象」,可以用代码这样理解:

示例1(不遵循依赖倒置原则):

// 底层模块
public class LowLevel{
    public void methodLow(){
        System.out.println("底层方法");
    }
}

// 上层模块,直接依赖底层模块
public class HighLevel {
    public void methodHigh(LowLevel lowLevel) {
        System.out.println("上层模块");
        lowLevel.methodLow();
    }
}

示例2(遵循依赖倒置原则)

// 抽象接口
public interface AbstractLevel {
    void method();
}

// 底层模块依赖抽象
public class LowLevel implements AbstractLevel{
    @Override
    public void method(){
        System.out.println("底层方法");
    }
}

// 上层模块依赖抽象
public class HighLevel {
    public void methodHigh(AbstractLevel abstractLevel) {
        System.out.println("上层模块");
        abstractLevel.method();
    }
}

依赖倒转(倒置)的中心思想就是针对接口编程,不要针对实现编程

组合/聚合复用原则 (Composite/Aggregate Reuse Principle, CARP)

组合/聚合复用原则定义:

组合/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分; 新的对象通过向这些对象的委派达到复用已有功能的目的。

简单来说就是多用组合,少用继承。

在面向对象设计中,有两种基本的办法可以实现复用:第一种是通过组合/聚合,第二种就是通过继承。而两种方式以「Has-A」(组合)和「Is-A」(继承)区分。

直接继承基类,会破坏封装,因为继承将基类的实现细节暴露给子类;如果基类的实现发生了改变,则子类的实现也不得不改变。

小结

本篇文章主要介绍了「单一职责原则」、「接口分隔原则」、「依赖倒置原则」、「组合/聚合复用原则」,这些原则可以理解为我们编码所遵循的方法。而下一篇文章,我们会介绍「开闭原则」、「里氏代换原则」、「迪米特原则」,这三个原则是我们编码所要实现的目标。

推荐阅读