设计原则SOLID看这一篇就够了

news2024/11/19 4:40:16

文章目录

  • 1.引言
    • 1.1. 背景
    • 1.2. 简要介绍 SOLID 原则
      • 1.1. 面向对象编程和设计的重要性
  • 2. 单一职责原则(SRP)
    • 2.1. 定义和原理
    • 2.2. SRP 的好处与目标
    • 2.3. 例子和代码展示
    • 2.4. 如何识别和解决 SRP 原则的违反
    • 2.5. 注意事项和局限性
  • 3. 开闭原则(OCP)
      • 3.1. 定义和原理
      • 3.2. OCP 的好处与目标
      • 3.3. 例子和代码展示
      • 3.4. 如何实现开闭原则:扩展、抽象、多态等
      • 3.5. 注意事项和局限性
  • 4.里氏替换原则(LSP)
    • 4.1. 定义和原理
    • 4.2. LSP的好处与目标
    • 4.3. 例子和代码展示
    • 4.4. 如何遵循 LSP:子类与基类的关系、重写与重载、抽象和多态等
    • 4.5. 注意事项和局限性
  • 5. 接口隔离原则(ISP)
    • 5.1. 定义和原理
    • 5.2. ISP 的好处与目标
    • 5.3. 例子和代码展示
      • 违反ISP的例子
      • 遵循ISP的修复
    • 5.4. 如何遵循 ISP:划分接口的原则、多接口与单接口的权衡等
    • 5.5. 注意事项和局限性
  • 6.依赖倒置原则(DIP)
    • 6.1. 定义和原理
    • 6.2. DIP 的好处与目标
    • 6.3. 例子和代码展示
      • 违反DIP的例子
      • 遵循DIP的修复
    • 6.4. 如何实现依赖倒置:依赖抽象、依赖注入、控制反转等
    • 6.5. 注意事项和局限性
  • 8. 参考资料
  • SOLID原则
    • 1. 单一职责原则(Single Responsibility Principle)
    • 2. 开放封闭原则(Open-Closed Principle)
    • 3. 里氏替换原则(Liskov Substitution Principle)
    • 4. 接口隔离原则(Interface Segregation Principle)
    • 5. 依赖倒置原则(Dependency Inversion Principle)

在这里插入图片描述
图片来源 《SOLID Principles — Concise and brief explanation》一文 https://medium.com/front-end-weekly/solid-principles-concise-and-brief-explanation-96790dc63b63
代码示例借鉴 《SOLID Principles Java》https://www.javatpoint.com/solid-principles-java

1.引言

1.1. 背景

有同学反馈最近面试被问到solid 原则。我在想什么样niubility的公司,面试已经卷到了问里氏替换原则(LSP)和依赖倒置原则(DIP)这种设计思想层面的问题了。不管咋样既然面试被问到了,说明它还是有很大的存在价值的,所以趁着周末查阅了大量文章和电子书整理了一下,从四个方面包含定义和原理、好处与目标、实现思路、以及局限性和注意事项。详细讲解一下每种原则的情况。为了方便理解我整理了一个思维导图
在这里插入图片描述

1.2. 简要介绍 SOLID 原则

SOLID 是一组面向对象编程和设计的原则,它们旨在引导开发者构建高质量的、可扩展的和可维护的软件。SOLID 原则包括以下五个部分:

  1. 单一职责原则(Single Responsibility Principle, SRP):一个类应该只有一个引起它变化的原因。换句话说,一个类应该只有一个职责,这样可以提高类的内聚性。
  2. 开闭原则(Open/Closed Principle, OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着在添加新功能时,应尽量不修改现有代码,而是通过扩展的方式实现。
  3. 里氏替换原则(Liskov Substitution Principle, LSP):子类应该能够替换其基类,并且在替换后的代码中不会出现错误。这意味着子类应该遵循基类的行为规范,以确保在使用继承时保持代码的正确性。
  4. 接口隔离原则(Interface Segregation Principle, ISP):客户端不应该被迫依赖于它不使用的接口。这意味着应该将大型接口拆分为多个小型、专用接口,以减少不必要的依赖关系。
  5. 依赖倒置原则(Dependency Inversion Principle, DIP):高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。这意味着在设计软件时,应该优先考虑抽象类和接口,而不是具体实现。

1.1. 面向对象编程和设计的重要性

面向对象编程(Object-Oriented Programming, OOP)是一种编程范式,它将软件系统组织为相互交互的对象,每个对象都包含数据和对这些数据进行操作的方法。面向对象编程的主要目标是提高代码的可维护性、可扩展性和重用性。

面向对象设计(Object-Oriented Design, OOD)是在软件开发过程中定义和组织对象及其相互关系的过程。良好的面向对象设计可以帮助开发人员更好地理解问题领域,减少代码的复杂性,提高系统的可靠性和可维护性。

2. 单一职责原则(SRP)

2.1. 定义和原理

单一职责原则(Single Responsibility Principle,SRP)是SOLID原则中的一项,它指出一个类应该只有一个引起它变化的原因。换句话说,一个类应该只负责一个职责或关注点。

SRP的原理是将一个类的功能划分为若干个职责,每个职责都应该由一个单独的类来承担。这样做的好处是使类的职责更加清晰明确,提高了代码的可读性、可维护性和可测试性。

2.2. SRP 的好处与目标

SRP的使用有以下好处和目标:

  • 高内聚性:单一职责原则使得类的功能集中于一个特定的职责,从而提高了类的内聚性。每个类都应该专注于完成自己的职责,这样代码更容易理解和维护。

  • 低耦合性:通过将不同的职责分离到不同的类中,可以降低类之间的耦合性。当一个职责发生变化时,只需修改与之相关的类,而不会对其他类产生影响。

  • 可测试性:每个类只负责一个职责,使得单元测试更加容易编写。测试可以针对单个职责进行,从而提高测试的精确性和可靠性。

  • 易于扩展和重用:当需要添加新的职责时,可以创建新的类来承担该职责,而不必修改已有的类。这使得系统更加灵活,支持易于扩展和重用的设计。

2.3. 例子和代码展示

下面是一个简单的例子来说明单一职责原则:

class FileManager:
    def read_file(self, file_path):
        # 读取文件逻辑

    def write_file(self, file_path, data):
        # 写入文件逻辑

    def process_data(self, data):
        # 处理数据逻辑

    def generate_report(self, report_data):
        # 生成报告逻辑

在上述代码中,FileManager 类同时承担了文件的读写操作和数据处理、报告生成等功能。这违反了SRP原则,因为它包含了多个不同的职责。

为了遵循SRP原则,可以将不同的职责分离到不同的类中:

class FileReader:
    def read_file(self, file_path):
        # 读取文件逻辑

class FileWriter:
    def write_file(self, file_path, data):
        # 写入文件逻辑

class DataProcessor:
    def process_data(self, data):
        # 处理数据逻辑

class ReportGenerator:
    def generate_report(self, report_data):
        # 生成报告逻辑

通过将不同的职责分离到不同的类中,每个类都负责一个特定的职责,使得代码更加清晰、可维护和可扩展。

2.4. 如何识别和解决 SRP 原则的违反

以下是一些识别和解决SRP原则违反的方法:

  • 职责不明确:当一个类承担了多个职责时,可以通过审查类的方法和属性来判断违反了SRP原则。如果一个类具有多个不相关的方法或属性,可能意味着它承担了多个职责,需要进行重构。

  • 类的大小和复杂性:如果一个类变得过大和复杂,很可能是因为它承担了过多的职责。可以通过将不同的职责分离到不同的类中,将大类拆分为更小的类,从而解决SRP违反的问题。

  • 高耦合性:如果修改一个职责导致其他职责的代码也需要修改,说明类之间存在高耦合性,违反了SRP原则。可以通过将不同职责的代码分离到独立的类中,降低耦合性。

  • 单一职责的测试:如果编写测试时需要关注多个不相关的功能,可能是因为类承担了多个职责。可以通过将职责拆分到不同的类中,使得单元测试更加简单和专注。

  • 违反开放封闭原则:如果为了添加新的功能而修改一个类,可能是因为它承担了多个职责。可以通过将新功能的代码放入一个新的类中,遵循开放封闭原则。

解决SRP原则违反的方法是将不同的职责分离到不同的类中。每个类应该专注于一个职责,这样可以提高代码的可读性、可维护性和可测试性。

2.5. 注意事项和局限性

尽管SRP原则有很多优点,但在实践中也需要注意以下事项和局限性:

  • 抽象的平衡:将类的职责划分得过于细化可能会导致类过多,增加了代码的复杂性。需要在职责划分时找到合适的抽象级别,以确保代码的可理解性和维护性。

  • 上下文相关性:有时候,类的职责可能在特定的上下文中是相关的。在这种情况下,严格遵循SRP原则可能会导致过多的类和间接性的调用。应该根据具体情况进行权衡和设计。

  • 实践和经验:理解和应用SRP原则需要实践和经验。通过实际的开发项目和代码复审,开发人员可以逐渐熟悉并掌握如何划分职责和设计高内聚、低耦合的类。

综上所述,单一职责原则(SRP)是一项重要的设计原则,它鼓励将类的功能划分为单一的职责。遵循SRP可以提高代码的可读性、可维护性和可测试性,并促进代码的扩展和重用。然而,在应用SRP时需要注意抽象的平衡、上下文相关性和实践经验等因素。

3. 开闭原则(OCP)

3.1. 定义和原理

开闭原则(Open-Closed Principle,OCP) 是面向对象设计中的一个重要原则,由Bertrand Meyer在《面向对象软件构造》一书中提出。该原则指导我们设计软件实体(类、模块、函数等)时,应该对扩展开放,对修改关闭。

具体而言,开闭原则要求我们应该通过扩展现有的代码来实现新功能,而不是通过修改已有的代码。这意味着我们应该设计出易于扩展的架构,使得在添加新功能时,不需要修改已有的代码,而只需添加新的代码。

3.2. OCP 的好处与目标

开闭原则的主要目标是提高代码的可维护性、可扩展性和可复用性。以下是应用开闭原则的好处:

  1. 可维护性:通过遵循开闭原则,我们可以将修改的影响范围限制在扩展代码上,而不是已有的稳定代码。这样可以降低引入错误的风险,并更容易进行测试和调试。

  2. 可扩展性:通过添加新的代码来实现新功能,我们可以在不修改现有代码的情况下扩展系统。这样可以减少系统的耦合度,使得扩展变得更加简单和灵活。

  3. 可复用性:通过遵循开闭原则,我们可以将通用的功能封装成可复用的组件或模块。这样可以在不同的系统中重复使用,提高代码的复用率和开发效率。

3.3. 例子和代码展示

下面是一个简单的例子,展示如何遵循开闭原则:

class Shape:
    def area(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius * self.radius

在上述例子中,Shape 是一个抽象类,定义了一个 area 方法。RectangleCircle 是具体的形状类,它们都继承自 Shape 并实现了 area 方法。

如果要添加一个新的形状,比如 Triangle,只需创建一个新的类并继承自 Shape,然后实现 area 方法即可,而不需要修改已有的代码。

3.4. 如何实现开闭原则:扩展、抽象、多态等

实现开闭原则的关键在于以下几个概念和技术:

  1. 扩展:通过添加新的代码来实现新功能,而不是修改已有的代码。新功能的实现应该以一种模块化的方式进行,以便于添加、修改和移除功能。

  2. 抽象:使用抽象类、接口或基类来定义稳定的公共接口。这样可以将实现细节与接口分离,让扩展代码针对接口编程,而不是具体的实现。

  3. 多态:通过多态性,我们可以在不同的对象上调用相同的方法,实现不同的行为。多态性使得我们可以通过接口来操作对象,而不需要关心具体的对象类型。

  4. 依赖倒置原则:依赖倒置原则要求我们依赖于抽象而不是具体的实现。通过依赖倒置,我们可以通过接口或抽象类来定义依赖关系,从而使得系统更加灵活和可扩展5. 设计模式:设计模式是一种常见的实现开闭原则的方法。例如,使用策略模式可以将可变的行为封装成独立的策略类,通过替换不同的策略类来实现功能的扩展,而不需要修改原有的代码。

3.5. 注意事项和局限性

在应用开闭原则时,需要注意以下事项和局限性:

  1. 适度的抽象:过度的抽象可能会引入不必要的复杂性。在设计时要根据实际需求和系统复杂性,找到合适的抽象级别。

  2. 预留扩展点:在设计时要预留一些扩展点,以便未来能够方便地添加新功能。这需要一定的设计和规划,以避免对现有代码的大规模修改。

  3. 不可避免的修改:尽管开闭原则鼓励我们通过扩展来实现新功能,但有些情况下仍然需要对已有的代码进行修改。在某些情况下,修改可能是合理和必要的,但应该尽量限制修改的范围和影响。

  4. 平衡:开闭原则需要在可扩展性和可维护性之间进行权衡。有时为了满足某种扩展需求,可能需要引入一定的复杂性和抽象层次,这可能会增加维护的难度。

总之,开闭原则是面向对象设计中的重要原则,通过遵循该原则可以提高代码的可维护性、可扩展性和可复用性。然而,实际的软件开发中需要权衡不同的因素,并根据具体情况进行灵活的设计和调整。

4.里氏替换原则(LSP)

LSP是一项重要的原则,它强调子类的替换能力,保证了代码的可靠性和可扩展性。通过合理地使用继承、重写、抽象和多态等特性,可以更好地遵循LSP原则,并设计出高质量的面向对象的代码。

4.1. 定义和原理

里氏替换原则(Liskov Substitution Principle,简称LSP)是面向对象设计的原则之一,由Barbara Liskov提出。LSP原则定义如下:

“如果S是T的子类型,那么T类型的对象可以被S类型的对象替换(即可以使用S类型的对象代替T类型的对象),而程序的逻辑行为不会受到影响。”

简而言之,LSP原则要求子类能够完全替代父类,而不会导致程序出错或产生意外的结果。子类在继承父类的同时,应该保持父类的行为规范,并且可以通过方法重写或方法扩展来增加新的行为,但不能修改父类原有的行为。

4.2. LSP的好处与目标

LSP的主要目标是增强代码的可扩展性、可维护性和可复用性。遵循LSP原则可以带来以下好处:

  1. 代码的可替换性:子类可以无缝替换父类,而不会影响程序的正确性。
  2. 代码的可扩展性:通过添加新的子类,可以扩展系统的功能,而不需要修改现有的代码。
  3. 代码的可维护性:由于子类保持了父类的行为规范,因此对父类的修改不会影响到已有的子类。
  4. 代码的可复用性:通过继承和多态,可以重用已有的代码,并通过子类的特定实现来满足不同的需求。

4.3. 例子和代码展示

以下是一个使用Java实现的简单例子,展示了违反LSP和修复后遵循LSP的情况:
我们有一个矩形类和一个正方形类。初始实现中,正方形类继承自矩形类,但违反了LSP,因为对正方形类的设置宽度和高度的方法会导致宽度和高度不相等。

// 违反LSP的例子
class Rectangle {
    protected int width;
    protected int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int calculateArea() {
        return width * height;
    }
}

class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        this.width = width;
        this.height = width;
    }

    @Override
    public void setHeight(int height) {
        this.width = height;
        this.height = height;
    }
}

为了修复违反LSP的问题,我们进行了重构。我们引入了一个抽象的Shape类,并让RectangleSquare都继承该抽象类。这样,每个类都有自己的calculateArea()方法来计算面积,而不再依赖于父类的方法。

// 遵循LSP的修复
abstract class Shape {
    public abstract int calculateArea();
}

class Rectangle extends Shape {
    protected int width;
    protected int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int calculateArea() {
        return width * height;
    }
}

class Square extends Shape {
    private int side;

    public void setSide(int side) {
        this.side = side;
    }

    public int calculateArea() {
        return side * side;
    }
}

4.4. 如何遵循 LSP:子类与基类的关系、重写与重载、抽象和多态等

遵循LSP的指导原则:

  • 子类与基类的关系:子类应该能够替换基类,并且不会破坏程序的正确性。子类可以扩展基类的功能,但不能修改基类已有的行为。
  • 重写与重载:子类可以重写基类的方法,但在重写时应该保持相同的前置条件、后置条件和约束条件,以确保替换的正确性。不应该缩小方法的前置条件,也不应该扩大方法的后置条件。
  • 抽象和多态:通过使用抽象类或接口来定义通用的行为,然后通过多态性来实现在运行时选择不同的实现。这样可以使代码更灵活,可以根据需要替换具体的实现类,而不需要修改调用方的代码。

4.5. 注意事项和局限性

遵循LSP需要注意以下事项:

  • 父类和子类之间的关系应该是"is-a"的关系,即子类应该是父类的一种特殊情况。如果子类不满足父类的行为约定,那么它可能不适合作为父类的替代品。
  • 强制要求遵循LSP可能会导致代码中出现过度的抽象和层次结构,增加了系统的复杂性。在设计时需要权衡好可维护性和扩展性之间的平衡。
  • LSP只是面向对象设计中的一个原则,它并不能解决所有设计问题。在实际应用中,还需要考虑其他原则和设计模式来达到更好的设计效果。

5. 接口隔离原则(ISP)

一句话通俗理解: ISP要求将臃肿的接口拆分为更小、更专注的接口,以避免客户端依赖于不需要的接口。

5.1. 定义和原理

接口隔离原则是面向对象设计中的一个原则,由Robert C. Martin提出。它定义为:客户端不应该强制依赖于它们不使用的接口。接口应该被划分为更小的、更具体的部分,以便客户端只需知道和使用它们所需的接口。

在这里插入图片描述

5.2. ISP 的好处与目标

ISP的主要目标是促进代码的灵活性、可维护性和可重用性。遵循ISP有以下好处:

  • 解耦和减少依赖:通过将接口划分为更小的部分,可以减少客户端与不相关接口的依赖,从而降低耦合度。
  • 提高可维护性:当需要修改接口时,只需修改与其相关的部分,而不会影响到其他部分的代码。
  • 促进代码重用:通过定义更小的、更具体的接口,可以提高接口的可重用性,使其更适用于不同的场景和客户端需求。

5.3. 例子和代码展示

使用Java实现的简单例子,展示了违反ISP和修复后遵循ISP的情况:

我们有一个机器接口(Machine),定义了打印(print)、扫描(scan)和传真(fax)三个方法。然后我们有一个全能机器类(AllInOneMachine),实现了机器接口。

然而,这种设计违反了ISP,因为并非所有客户端都需要使用全部的功能。修复违反ISP的问题,我们将机器接口拆分为打印机接口(Printer)、扫描仪接口(Scanner)和传真机接口(FaxMachine)。然后我们的全能机器类实现这三个接口,这样客户端只需依赖于需要的接口,而不会强制依赖于不需要的接口。

违反ISP的例子

// 违反ISP的例子
interface Machine {
    void print(); // 打印
    void scan(); // 扫描
    void fax(); // 传真
}

class AllInOneMachine implements Machine {
    @Override
    public void print() {
        System.out.println("Printing...");
    }

    @Override
    public void scan() {
        System.out.println("Scanning...");
    }

    @Override
    public void fax() {
        System.out.println("Faxing...");
    }
}

遵循ISP的修复

// 遵循ISP的修复
interface Printer {
    void print();
}

interface Scanner {
    void scan();
}

interface FaxMachine {
    void fax();
}

class AllInOneMachine implements Printer, Scanner, FaxMachine {
    @Override
    public void print() {
        System.out.println("Printing...");
    }

    @Override
    public void scan() {
        System.out.println("Scanning...");
    }

    @Override
    public void fax() {
        System.out.println("Faxing...");
    }
}

5.4. 如何遵循 ISP:划分接口的原则、多接口与单接口的权衡等

以下是一些遵循ISP的指导原则:

  • 划分接口的原则:将接口划分为更小、更具体的部分,每个接口应该代表一个单一的职责或角色。这样可以使客户端只需依赖于其需要的接口,而不会依赖于其他不需要的接口。
  • 多接口与单接口的权衡:在划分接口时,需要权衡单一接口的简洁性和多接口的灵活性之间的平衡。如果单一接口足够满足大多数客户端的需求,并且没有过多的冗余方法,那么可以选择使用单一接口。但是,如果存在不同的客户端需要不同的功能子集,或者未来可能有新的功能扩展需求,那么使用多个小接口更合适。

5.5. 注意事项和局限性

在应用ISP时,需要注意以下事项和局限性:

  • 避免过分细化接口:划分接口时要注意不要过分细化,导致接口数量过多或接口之间的耦合度增加。接口的划分应该有意义,并且符合系统的特定需求。
  • 接口的稳定性:划分接口后,需要确保接口的稳定性和一致性。如果接口频繁变动,可能会导致客户端的改动和维护成本增加。
  • 需要适度的抽象:接口应该抽象出通用的行为和功能,而不是过于具体化。过度具体化的接口可能导致客户端的依赖过于细节,降低了代码的灵活性和可重用性。

6.依赖倒置原则(DIP)

一句话通俗理解 DIP(依赖倒置原则)是指高层模块不应该依赖于低层模块,而应该依赖于抽象接口,从而使得系统各部分之间的依赖关系更加灵活,降低耦合度。DIP就是让高层和低层模块都依赖于一个统一的标准,这样就避免了它们之间的紧密联系,整个系统更容易改动和适应变化。

切记 应用依赖倒置时需要谨慎权衡,避免过度设计和引入不必要的复杂性。不要为了使用设计模式 而生搬硬套。导致画蛇添足。给原本简单的系统搞的很庞大,本来一个工具类解决的问题,制造出了8个接口和实现类。

6.1. 定义和原理

依赖倒置原则是面向对象设计中的一个原则,由Robert C. Martin提出。它定义为:高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。DIP要求通过抽象来解耦高层模块和低层模块之间的依赖关系,并且依赖关系应该倒置,即高层模块和低层模块都应该依赖于抽象。

6.2. DIP 的好处与目标

DIP的主要目标是降低模块之间的耦合度,提高代码的灵活性、可扩展性和可维护性。遵循DIP有以下好处:

  • 解耦模块间的依赖关系:通过引入抽象接口或抽象类,高层模块和低层模块之间的依赖关系通过抽象进行连接,而不是直接依赖于具体的实现细节。
  • 提高代码的可扩展性:由于模块之间的依赖关系是通过抽象进行定义的,当需要增加新的实现时,只需实现抽象接口或继承抽象类,并注入到高层模块中,而无需修改高层模块的代码。
  • 促进单元测试和模块的独立性:通过依赖抽象,可以更容易地对模块进行单元测试,因为可以使用模拟对象替代具体实现。

6.3. 例子和代码展示

我们有一个前端开发者类(FrontendDeveloper),它包含一个写JavaScript代码的方法。然后我们有一个项目类(Project),它在构造函数中创建了一个前端开发者对象,并在实现功能的方法中调用了开发者的方法。

然而,这种设计违反了DIP,因为项目类直接依赖于具体的前端开发者类。修复违反DIP的问题,我们引入了一个开发者接口(Developer),其中包含一个开发方法。我们的前端开发者类实现了该接口,并且项目类通过接口来依赖开发者,而不是具体的实现类。

违反DIP的例子

// 违反DIP的例子
class FrontendDeveloper {
    void writeJavaScript() {
        System.out.println("Writing JavaScript code...");
    }
}

class Project {
    private FrontendDeveloper developer;

    Project() {
        this.developer = new FrontendDeveloper();
    }

    void implementFeature() {
        developer.writeJavaScript();
    }
}

遵循DIP的修复

// 遵循DIP的修复
interface Developer {
    void develop();
}

class FrontendDeveloper implements Developer {
    @Override
    public void develop() {
        System.out.println("Writing JavaScript code...");
    }
}

class Project {
    private Developer developer;

    Project(Developer developer) {
        this.developer = developer;
    }

    void implementFeature() {
        developer.develop();
    }
}

6.4. 如何实现依赖倒置:依赖抽象、依赖注入、控制反转等

  • 依赖抽象:定义抽象接口或抽象类来描述模块之间的依赖关系,高层模块和低层模块都应该依赖于抽象。通过依赖抽象,可以将模块之间的依赖关系与具体的实现细节解耦。

  • 依赖注入(Dependency Injection):通过将依赖对象注入到需要它们的对象中,实现对依赖关系的解耦。依赖注入可以通过构造函数、方法参数、属性注入等方式进行。

  • 控制反转(Inversion of Control):控制反转是一种实现依赖注入的方式,它将对象的创建和依赖关系的管理交给外部容器或框架来完成。通过控制反转,对象不再负责自己的依赖关系的获取和管理,而是交给外部来处理。

具体的实现方法和工具库可能因编程语言和框架而异,常见的依赖注入框架包括Spring(Java)、Dagger(Java/Kotlin)和Angular(JavaScript/TypeScript)等。

6.5. 注意事项和局限性

在实践中,使用依赖倒置原则时需要注意以下事项和局限性

  • 抽象的设计需要考虑合理性和可维护性,过度抽象可能导致代码复杂化和不必要的抽象层次。

  • 应该权衡依赖倒置的成本和收益。对于简单的应用程序或小规模项目,可能不需要引入复杂的依赖注入框架。

  • 依赖注入容器的配置和管理可能需要一些额外的学习和开发成本。

  • 依赖注入可能会增加代码的复杂性和理解难度,特别是在大型项目中,需要合理地组织和管理依赖关系。

  • 依赖倒置并非适用于所有情况,某些特定场景下可能需要根据实际情况进行权衡和取舍。

8. 参考资料

https://zh.wikipedia.org/wiki/SOLID_(面向对象设计)
https://blog.csdn.net/zhengzhb/article/details/7296921

https://medium.com/front-end-weekly/solid-principles-concise-and-brief-explanation-96790dc63b63

SOLID原则

1. 单一职责原则(Single Responsibility Principle)

  • 一个类只应该有一个引起它变化的原因。换句话说,一个类应该只有一个职责。

2. 开放封闭原则(Open-Closed Principle)

  • 软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。即软件实体应该通过新增代码来扩展功能,而不是直接修改现有代码。

3. 里氏替换原则(Liskov Substitution Principle)

  • 子类应该能够替换掉其父类并且不产生任何错误或异常。换句话说,父类中定义的行为在子类中应该保持一致。

4. 接口隔离原则(Interface Segregation Principle)

  • 客户端不应该强迫依赖它们不需要的接口。应该将接口细分为更小的接口,以便客户端只需知道他们需要使用的方法。

5. 依赖倒置原则(Dependency Inversion Principle)

  • 高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于具体细节,具体细节应该依赖于抽象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1014485.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Centos7.9 一键脚本部署 LibreNMS 网络监控系统

前言: LibreNMS 是个以 PHP/MySQL 为基底的自动探索网络监控系统 LibreNMS 官网 版本23.8.2-52-g7bbe0a2 - Thu Sep 14 2023 22:33:23 GMT0700数据库纲要2023_09_01_084057_application_new_defaults (259)Web 服务器nginx/1.20.1PHP8.1.23Python3.6.8DatabaseMa…

Killer!永久禁用WindowsDefender

工具介绍 WinDefenderKiller,使用C写的通过注册表项永久禁用Windows Defende的一个工具。 关注【Hack分享吧】公众号,回复关键字【230726】获取下载链接 编译使用 执行以下命令编译: # x86_64-w64-mingw32-g -O2 disableWinDef.cpp -o win…

JS 原型和原型链

原型和原型链 1. 了解原型和原型链1.1 原型1.2 原型链 2. 原型2.1 prototype2.2 __proto__ 隐式原型 3. 原型链 1. 了解原型和原型链 1.1 原型 原型: prototype 又称显示原型 1、原型是一个普通对象 2、只有构造函数才具备该属性 3、公有属性可操作 1.2 原型链 原…

数据治理-元数据管理-元数据类型

定义 元数据,定义和描述其它数据的数据。 类型 业务元数据、技术元数据和操作元数据。在图书馆或信息科学中,可分为描述元数据、结构元数据、管理元数据。 业务元数据 主要关注数据的内容和条件,另包括与数据治理相关的详细信息。业务元数据…

logback异步appender日志源码详解

背景: 日常打印日志时,使用logback的异步写日志几乎是标准的配置方式,本文从源码上看看异步写日志的整个流程 异步Appender日志 一般日志的配置如下所示 appender(“ASYNC-LOG”, AsyncAppender) { neverBlock true queueSize 10000 } 这…

前端需要知道的计算机网络知识----网络安全,自学网络安全,学习路线图必不可少,【282G】初级网络安全学习资源分享!

网络安全(英语:network security)包含网络设备安全、网络信息安全、网络软件安全。 黑客通过基于网络的入侵来达到窃取敏感信息的目的,也有人以基于网络的攻击见长,被人收买通过网络来攻击商业竞争对手企业&#xff0c…

CH07_封装

封装记录(Encapsulate Record | 162) 曾用名:以数据类代替记录(Replace Record with Data Class) organization {name: "Acme Gooseberries", country: "GB"};class Organization {constructor(…

HTML整站规划与规范

文章目录 命名规则命名命名书写 包含样式规范样式重置样式引入页面结构页面宽度页面高度与背景页面设计 网址图标 命名规则 命名 根据每块元素的主题、功能、页面上的位置命名,便于后期更改与维护。 另外:如果所有样式放在同一文件下,可以给…

计算如何剥出艺术品

背景: 今天给大家介绍一篇中国科学技术大学发表的论文《Computational Peeling Art Design》。论文要解决是:如何把球状三维物体的表面连续展开成一些艺术画面,要求是展开表面要占三维物体表面整个面积,展开表面要和艺术体形状尽可…

[CISCN 2022 初赛]online_crt

文章目录 涉及知识点代码审计解题过程 涉及知识点 CVE-2022-1292漏洞OpenSSLssrf 代码审计 app.py源码 import datetime import json import os import socket import uuid from cryptography import x509 from cryptography.hazmat.backends import default_backend from …

LeetCode(力扣)55. 跳跃游戏Python

LeetCode20. 有效的括号 题目链接代码 题目链接 https://leetcode.cn/problems/jump-game/ 代码 class Solution:def canJump(self, nums: List[int]) -> bool:if len(nums) < 1:return Truecover 0for i in range(len(nums)):if i < cover:cover max(cover, i …

【NVIDIA CUDA】2023 CUDA夏令营编程模型(四)

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

性能测试结果评估与展示

面向性能测试部门 对测试资产进行集中管理以及从项目或系统维度进行汇总展示是两种行之有效的管理手段。这些测试资产包括脚本、缺陷描述、测试记录、测试报告、项目需求等资料,通过对这些资料进行分类,当原有人员缺失的情况下,新接手的测试工程师能快速了解关键信息。 使…

Android RecyclerView BaseSectionQuickAdapter实现分组功能

详情网站&#xff1a;手把手教你使用BaseSectionQuickAdapter实现分组功能&#xff0c;史上最详细Adapter使用教程_basequickadapter 分组_杨阿程的博客-CSDN博客 //加入二个包implementation com.android.support:recyclerview-v7:26.0.0-beta1implementation com.github.Cym…

SAP 选择屏幕动态通过Radio Button 显示与隐藏以及控制是否必输

如何在选择屏幕上进行动态展示屏幕字段&#xff0c;并且进行必输项检查控制 1. 选择屏幕定义 SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.SELECTION-SCREEN BEGIN OF LINE.PARAMETERS: p_r1 TYPE c RADIOBUTTON GROUP grp USER-COMMAND uc DEFAULT X. &q…

【推荐】赴日IT课程 做赴日IT我该学什么?

许多想要做赴日IT的朋友问我说&#xff0c;我都该准备什么&#xff0c;或者我该学些什么才能达到可以做赴日程序员的水平呢&#xff1f;今天我就来跟大家聊一下这个问题。要说做准备&#xff0c;你需要有全日制大专及以上的学历才能获得赴日的资格&#xff0c;如果没有我们就先…

【Linux】线程的概念

文章目录 &#x1f4d6; 前言1. 线程的引入1.1 执行流&#xff1a;1.2 线程的创建&#xff1a;1.3 线程的等待&#xff1a; 2. 查看线程2.1 链接线程库&#xff1a;2.2 ps -aL&#xff1a; 3. 页表的认识3.1 二级页表&#xff1a;3.2 页表的实际大小&#xff1a; 4. 再看线程4.…

全面深入理解TCP协议(超详细)

目录 前言 TCP协议格式 确认应答机制(ACK) 理解可靠性 确认应答的机制 16位窗口大小 缓冲区 流量控制 6个标志位 16位紧急指针 ★三次握手&#xff0c;四次挥手 如何理解连接 如何理解三次握手 如何理解四次挥手 TCP可靠性机制 确认应答机制(补充) ​编辑…

70、Spring Data JPA 的 自定义查询(全手动,自己写完整 SQL 语句)

1、方法名关键字查询&#xff08;全自动&#xff0c;既不需要提供sql语句&#xff0c;也不需要提供方法体&#xff09; 2、Query查询&#xff08;半自动&#xff1a;提供 SQL 或 JPQL 查询&#xff09; 3、自定义查询&#xff08;全手动&#xff09; ★ 自定义查询&#xff08…

前端开发中,文本单行或多行溢出使用省略号显示

1.文本单行溢出使用省略号显示 关键代码如下&#xff1a; .box1{width: 200px;height: 30px;line-height: 30px;margin: 0 auto;background-color: rgba(220, 220, 220, 0.751);/* 单行文本超出隐藏 用省略号代替 */white-space: nowrap;overflow: hidden;text-overflow: ellip…