JavaRush /Java 博客 /Random-ZH /Java 类设计 (SOLID) 的五个基本原则
Ve4niY
第 14 级

Java 类设计 (SOLID) 的五个基本原则

已在 Random-ZH 群组中发布
类是构建应用程序的块。就像建筑物中的砖块一样。写得不好的课程有一天可能会引起问题。 Java 类设计 (SOLID) 的五个基本原则 - 1要了解一个类是否编写正确,可以检查“质量标准”。在Java中,这些就是所谓的SOLID原则。我们来谈谈他们吧。

Java 的坚实原则

SOLID 是由 OOP 和设计的前五个原则的大写字母组成的缩写。这些原理是由罗伯特·马丁 (Robert Martin) 在 2000 年代初发明的,这个缩写词后来由迈克尔·费瑟斯 (Michael Feathers) 创造。SOLID 原则包括以下内容:
  1. 单一责任原则。
  2. 开闭原则。
  3. 里氏替换原理。
  4. 接口隔离原则。
  5. 依赖倒置原则。

单一职责原则(SRP)

这一原则规定,改变一个类的理由永远不应该超过一个。 每个对象都有一个职责,完全封装在一个类中。所有班级服务都旨在确保这一责任。如果需要的话,这样的类总是很容易更改,因为很清楚该类负责什么,不负责什么。也就是说,可以进行更改并且不必担心后果 - 对其他对象的影响。而且这样的代码更容易测试,因为您可以通过与所有其他功能隔离的测试来覆盖一项功能。想象一个处理订单的模块。如果订单格式正确,则会将其保存在数据库中并发送电子邮件以确认订单:
public class OrderProcessor {

    public void process(Order order){
        if (order.isValid() && save(order)) {
            sendConfirmationEmail(order);
        }
    }

    private boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // save the order to the database

        return true;
    }

    private void sendConfirmationEmail(Order order) {
        String name = order.getCustomerName();
        String email = order.getCustomerEmail();

        // Sending a letter to the client
    }
}
这样的模块可能会因三个原因而改变。首先,订单处理逻辑可能不同,其次,保存方法(数据库类型),第三,发送确认信的方法(例如,您需要发送短信而不是电子邮件)。单一职责原则意味着这个问题的三个方面实际上是三个不同的职责。这意味着它们必须位于不同的类或模块中。组合可能在不同时间和出于不同原因发生变化的多个实体被认为是糟糕的设计决策。最好将模块分为三个独立的模块,每个模块执行一个功能:
public class MySQLOrderRepository {
    public boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // save the order to the database

        return true;
    }
}

public class ConfirmationEmailSender {
    public void sendConfirmationEmail(Order order) {
        String name = order.getCustomerName();
        String email = order.getCustomerEmail();

        // Sending a letter to the client
    }
}

public class OrderProcessor {
    public void process(Order order){

        MySQLOrderRepository repository = new MySQLOrderRepository();
        ConfirmationEmailSender mailSender = new ConfirmationEmailSender();

        if (order.isValid() && repository.save(order)) {
            mailSender.sendConfirmationEmail(order);
        }
    }

}

开闭原理 (OCP)

这个原则简洁地描述如下:软件实体(类、模块、函数等)必须对扩展开放,但对变更封闭。这意味着应该可以更改类的外部行为,而无需对类本身进行物理更改。遵循这个原则,开发类,为了使类适应特定的应用条件,只需扩展它并重新定义一些功能即可。因此,系统必须是灵活的,能够在不改变源代码的情况下在变化的条件下工作。继续我们的订单示例,假设我们需要在处理订单之前和发送确认电子邮件之后执行一些操作。我们不会改变类本身OrderProcessor,而是扩展它并在不违反 OCP 原则的情况下实现手头问题的解决方案:
public class OrderProcessorWithPreAndPostProcessing extends OrderProcessor {

    @Override
    public void process(Order order) {
        beforeProcessing();
        super.process(order);
        afterProcessing();
    }

    private void beforeProcessing() {
        // Perform some actions before processing the order
    }

    private void afterProcessing() {
        // Perform some actions after order processing
    }
}

芭芭拉·利斯科夫替换原理 (LSP)

这是前面讨论的开放/封闭原则的变体。可以这样描述:程序中的对象可以被其继承者替换,而不改变程序的属性。这意味着通过扩展基类开发的类必须以不破坏客户端观点的功能的方式重写其方法。也就是说,如果开发人员扩展您的类并在应用程序中使用它,他不应该更改重写方法的预期行为。从客户端的角度来看,子类必须以不破坏功能的方式重写基类方法。可以使用以下示例详细检查这一点。假设我们有一个类负责订单验证并检查所有订单商品是否都有库存。此类有一个isValid返回truefalse的方法:
public class OrderStockValidator {

    public boolean isValid(Order order) {
        for (Item item : order.getItems()) {
            if (! item.isInStock()) {
                return false;
            }
        }

        return true;
    }
}
我们还假设某些订单需要进行不同的验证:检查订单中的所有商品是否都有库存以及所有商品是否都已包装。为此,我们OrderStockValidator用以下类扩展了该类OrderStockAndPackValidator
public class OrderStockAndPackValidator extends OrderStockValidator {

    @Override
    public boolean isValid(Order order) {
        for (Item item : order.getItems()) {
            if ( !item.isInStock() || !item.isPacked() ){
                throw new IllegalStateException(
                     String.format("Order %d is not valid!", order.getId())
                );
            }
        }

        return true;
    }
}
然而,在这个类中我们违反了 LSP 原则,因为如果订单没有通过验证,我们的方法不会返回falseIllegalStateException ,而是会抛出异常。此代码的客户端并不期望这样:他们期望返回truefalse。这可能会导致程序出错。

接口分割原理(ISP)

其特点是以下陈述:不应强迫客户实现他们不会使用的方法。接口分离的原则是,过于“厚”的接口需要被分成更小、更具体的接口,这样小接口的客户端只知道其工作所必需的方法。因此,当更改接口方法时,不使用该方法的客户端不应更改。让我们看一个例子。开发人员 Alex 创建了“报告”界面并添加了两个方法:generateExcel()generatedPdf()。现在客户A想要使用这个界面,但他只想使用PDF报告,而不是Excel。他对这个功能满意吗?不。他必须实施两种方法,其中一种基本上是不必要的,并且要感谢软件设计师亚历克斯才能存在。客户端将使用不同的界面或将 Excel 字段留空。那么解决办法是什么呢?它包括将现有界面分为两个较小的界面。第一个是 PDF 格式的报告,第二个是 Excel 格式的报告。这将使用户有机会仅使用他所需的功能。

依赖倒置原则(DIP)

Java中的SOLID原则描述如下:系统内的依赖关系是建立在抽象的基础上的。顶层模块独立于下层模块。抽象不应该依赖于细节。细节必须依赖于抽象。软件的设计需要确保各个模块是自治的,并使用抽象相互连接。Spring 框架是这一原则的典型应用。在 Spring 框架内,所有模块都实现为可以协同工作的单独组件。它们是如此独立,以至于可以在 Spring 框架之外的其他软件模块中轻松使用。这是通过封闭和开放原则的依赖来实现的。所有模块仅提供对可在另一个模块中使用的抽象的访问。让我们尝试用一个例子来证明这一点。谈到单一责任原则,我们考虑了一些OrderProcessor。我们再看一下这个类的代码:
public class OrderProcessor {
    public void process(Order order){

        MySQLOrderRepository repository = new MySQLOrderRepository();
        ConfirmationEmailSender mailSender = new ConfirmationEmailSender();

        if (order.isValid() && repository.save(order)) {
            mailSender.sendConfirmationEmail(order);
        }
    }

}
在此示例中,我们的OrderProcessor依赖于两个特定的类MySQLOrderRepositoryConfirmationEmailSender。我们还提供了这些类的代码:
public class MySQLOrderRepository {
    public boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // save the order to the database

        return true;
    }
}

public class ConfirmationEmailSender {
    public void sendConfirmationEmail(Order order) {
        String name = order.getCustomerName();
        String email = order.getCustomerEmail();

        // Sending a letter to the client
    }
}
这些类远不能称为抽象。从 DIP 原则的角度来看,从创建一些抽象开始,让我们将来可以使用它们,而不是具体的实现,会更正确。让我们创建两个接口MailSenderOrderRepository,它将成为我们的抽象:
public interface MailSender {
    void sendConfirmationEmail(Order order);
}

public interface OrderRepository {
    boolean save(Order order);
}
现在让我们在已经为此做好准备的类中实现这些接口:
public class ConfirmationEmailSender implements MailSender {

    @Override
    public void sendConfirmationEmail(Order order) {
        String name = order.getCustomerName();
        String email = order.getCustomerEmail();

        // Sending a letter to the client
    }

}

public class MySQLOrderRepository implements OrderRepository {

    @Override
    public boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // save the order to the database

        return true;
    }
}
我们已经完成了准备工作,以便我们的类OrderProcessor不依赖于具体细节,而是依赖于抽象。让我们通过在类构造函数中引入依赖项来对其进行更改:
public class OrderProcessor {

    private MailSender mailSender;
    private OrderRepository repository;

    public OrderProcessor(MailSender mailSender, OrderRepository repository) {
        this.mailSender = mailSender;
        this.repository = repository;
    }

    public void process(Order order){
        if (order.isValid() && repository.save(order)) {
            mailSender.sendConfirmationEmail(order);
        }
    }
}
我们的类现在依赖于抽象而不是具体的实现。您可以通过在创建实例时注入所需的依赖项来轻松更改其行为OrderProcessor。我们研究了 Java 中的 SOLID 设计原则。更多有关 OOP 的一般信息以及这种编程语言的基础知识 - 并不乏味,需要数百小时的练习 - 在 JavaRush 课程中。是时候解决一些问题了:) Java 类设计 (SOLID) 的五个基本原则 - 2
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION