SOLID原则之Java实战(2)

简介: 这也有点类似于单一责任原则。正确的应用程序设计和正确的抽象是接口隔离原则背后的关键。让我们举个例子。我们有一个支付接口来代表所有类型的支付。 BankPayment 和 LoanPayment 是 Payment 中实现类。

接口隔离原则(ISP)

该原则指出较大的接口分为较小的接口。因为实现类只使用需要的方法,我们不应该强迫客户使用他们不想使用的方法

这也有点类似于单一责任原则。正确的应用程序设计和正确的抽象是接口隔离原则背后的关键。

让我们举个例子。

我们有一个支付接口来代表所有类型的支付。 BankPayment 和 LoanPayment 是 Payment 中实现类。

java

复制代码

public interface Payment {
    void init();
    Object status();
    List<Object> getPayments();
}
public class LoanPayment implements Payment {
    @Override
    public void init() {
        System.out.println("Initiate LoanPayment...");
    }
    @Override
    public Object status() {
        return "LoanPayment Status";
    }
    @Override
    public List<Object> getPayments() {
        return Arrays.asList("LoanPayment1", "LoanPayment2");
    }
}
public class BankPayment implements Payment {
    @Override
    public void init() {
        System.out.println("Initiate BankPayment...");
    }
    @Override
    public Object status() {
        return "BankPayment Status";
    }
    @Override
    public List<Object> getPayments() {
        return Arrays.asList("BankPayment1", "BankPayment2");
    }
}

想象一下,我们收到一个新要求,需要向 Payment 接口添加一个方法。但是实际上这个方法只有 LoanPayment 类需要持有。那么我们就得修改 Payment 接口了。除了修改 LoanPayment 我们还得修改 BankPayment 类,因为 BankPayment 也实现了 Payment 接口。

新方法将返回付款期限:贷款 => 10 年

java

复制代码

public interface Payment {
    void init();
    Object status();
    List<Object> getPayments();
    int getTimePeriod();
}
public class LoanPayment implements Payment {
    @Override
    public void init() {
        System.out.println("Initiate LoanPayment...");
    }
    @Override
    public Object status() {
        return "LoanPayment Status";
    }
    @Override
    public List<Object> getPayments() {
        return Arrays.asList("LoanPayment1", "LoanPayment2");
    }
    public int getTimePeriod() {
        return 10;
    }
}
public class BankPayment implements Payment {
    @Override
    public void init() {
        System.out.println("Initiate BankPayment...");
    }
    @Override
    public Object status() {
        return "BankPayment Status";
    }
    @Override
    public List<Object> getPayments() {
        return Arrays.asList("BankPayment1", "BankPayment2");
    }
    // not needed for BankPayment but we have to override
    public int getTimePeriod() {
        retun 0;
    }
}

这样做不好。在这种情况下,一开始的代码设计就很重要了!让我们尝试在这里应用接口隔离原则(ISP)。

java

复制代码

public interface Payment {
    void init();
    Object status();
    List<Object> getPayments();
}
public interface Loan extends Payment {
    int getTimePeriod();
}
public interface Bank extends Payment {
    int getOutstandingBalance();
}
public class LoanPayment implements Loan {
    @Override
    public int getTimePeriod() {
        return 10;
    }
    @Override
    public void init() {
        System.out.println("Initiate LoanPayment...");
    }
    @Override
    public Object status() {
        return "LoanPayment Status";
    }
    @Override
    public List<Object> getPayments() {
        return Arrays.asList("LoanPayment1", "LoanPayment2");
    }
}
public class BankPayment implements Bank {
    @Override
    public int getOutstandingBalance() {
        return 1000;
    }
    @Override
    public void init() {
        System.out.println("Initiate BankPayment...");
    }
    @Override
    public Object status() {
        return "BankPayment Status";
    }
    @Override
    public List<Object> getPayments() {
        return Arrays.asList("BankPayment1", "BankPayment2");
    }
}

这就是设计如何让我们的代码变得更好!我们已经彻底摆脱了 ISP 的困扰。我们使用了更多的接口,并全面地划分了职责。

如果出现与银行支付相关的新要求会怎样?

我们只需要修改 Bank 接口并在 BankPayment 类中重写它,不会影响其他类!

依赖倒置原则(DIP)

该原则指出我们必须使用抽象(抽象类和接口)而不是具体实现。高层模块不应该依赖于低层模块,两者都应该依赖于抽象

让我在这里用另一个例子来解释。

假设我们要创建一个购物场景,我们需要信用卡或借记卡来购买物品,让我们创建它们并进行购买!

java

复制代码

public class CreditCard {
    public void doTransaction(double amount) {
        System.out.println("Transaction with CreditCard: " + amount);
    }
}
public class DebitCard {
    public void doTransaction(double amount) {
        System.out.println("Transaction with DebitCard: " + amount);
    }
}

商场场景

java

复制代码

public class ShoppingMall {
    private DebitCard debitCard;
    public ShoppingMall(DebitCard debitCard) {
        this.debitCard = debitCard;
    }
    public void purchase(double amount) {
        this.debitCard.doTransaction(amount);
    }
    public static void main(String[] args) {
        DebitCard debitCard = new DebitCard();
        ShoppingMall shoppingMall = new ShoppingMall(debitCard);
        shoppingMall.purchase(10000);
    }
}

你可以在这里看到,DebitCard 作为 ShoppingMall 类的依赖项紧密耦合。我们需要它来执行购买。那么如果有人没有借记卡会怎样?但他/她有信用卡!现在我们再次必须更改 ShoppingMall 类并绑定信用卡而不是借记卡。(代码设计很重要!)

让我们将依赖倒置原则(DIP)应用到购物中心 ShoppingMall。

java

复制代码

public interface BankCard {
    void doTransaction(double amount);
}
public class DebitCard implements BankCard {
    @Override
    public void doTransaction(double amount) {
        System.out.println("Transaction with DebitCard: " + amount);
    }
}
public class CreditCard implements BankCard {
    @Override
    public void doTransaction(double amount) {
        System.out.println("Transaction with CreditCard: " + amount);
    }
}

我们引入了一个接口作为两张卡的父级,一个 BankCard 接口。我们的新购物中心现在将能够使用任何银行卡,而不是与特定的卡类型紧密结合!请参阅下面的客户端代码。

java

复制代码

public class ShoppingMall {
    private BankCard bankCard;
    public ShoppingMall(BankCard bankCard) {
        this.bankCard = bankCard;
    }
    public void purchase(double amount) {
        this.bankCard.doTransaction(amount);
    }
    public static void main(String[] args) {
        BankCard bankCard1 = new DebitCard();
        BankCard bankCard2 = new CreditCard();
        ShoppingMall shoppingMall = new ShoppingMall(bankCard1);
        // ShoppingMall shoppingMall = new ShoppingMall(bankCard2);
        shoppingMall.purchase(10000);
    }
}

ShoppingMall 现在接受任何卡作为其依赖项。就这样我们实现了最后一个依赖倒置原则(DIP)。

总结

我希望本文我举的例子能够被大家理解,因为我尝试用熟悉的场景来讲解它们。当我第一次读到 SOLID 原则时,它对我来说也像希腊语(很难理解)。但后来我渐渐地理解了他们,我把我理解这些概念的方式也写在这里,所以这篇文章的内容非常丰富。最后,感谢大家阅读。

目录
相关文章
|
5天前
|
存储 SQL 安全
Java 安全性编程:基本概念与实战指南
【4月更文挑战第27天】在当今的软件开发领域,安全性编程是一个至关重要的方面。Java,作为广泛使用的编程语言之一,提供了多种机制来保护应用免受常见的安全威胁。本博客将探讨 Java 安全性编程的基本概念,并通过实际示例来展示如何实现这些安全措施。
12 3
|
2天前
|
设计模式 算法 安全
Java多线程编程实战:从入门到精通
【4月更文挑战第30天】本文介绍了Java多线程编程的基础,包括线程概念、创建线程(继承`Thread`或实现`Runnable`)、线程生命周期。还讨论了线程同步与锁(同步代码块、`ReentrantLock`)、线程间通信(等待/通知、并发集合)以及实战技巧,如使用线程池、线程安全设计模式和避免死锁。性能优化方面,建议减少锁粒度和使用非阻塞算法。理解这些概念和技术对于编写高效、可靠的多线程程序至关重要。
|
3天前
|
XML Java 测试技术
Java异常处理神器:Guava Throwables类概念与实战
【4月更文挑战第29天】在Java开发中,异常处理是保证程序稳定性和可靠性的关键。Google的Guava库提供了一个强大的工具类Throwables,用于简化和增强异常处理。本篇博客将探讨Throwables类的核心功能及其在实战中的应用。
11 2
|
3天前
|
安全 Java 测试技术
利用Java反射机制提高Spring Boot的代码质量:概念与实战
【4月更文挑战第29天】Java反射机制提供了一种强大的方法来在运行时检查或修改类和对象的行为。在Spring Boot应用中,合理利用反射可以提高代码的灵活性和可维护性。本篇博客将探讨Java反射的核心概念,并展示如何通过反射提高Spring Boot项目的代码质量。
16 0
|
3天前
|
存储 Java 大数据
JAVA:编程的艺术与实战解析
JAVA:编程的艺术与实战解析
13 2
|
5天前
|
监控 Java API
Java 模块化设计:概念与实战应用
【4月更文挑战第27天】模块化设计是现代软件开发的关键,它帮助开发者构建可管理、可维护的大型系统。Java 平台的模块化支持始于 Java 9,引入了一种全新的模块系统。
13 3
|
5天前
|
设计模式 算法 Java
Java 设计模式:探索策略模式的概念和实战应用
【4月更文挑战第27天】策略模式是一种行为设计模式,它允许在运行时选择算法的行为。在 Java 中,策略模式通过定义一系列的算法,并将每一个算法封装起来,并使它们可以互换,这样算法的变化不会影响到使用算法的客户。
11 1
|
5天前
|
Java
Java 事件驱动编程:概念、优势与实战示例
【4月更文挑战第27天】事件驱动编程是一种编程范式,其中程序的执行流程由外部事件的发生而触发或驱动。
12 0
|
5天前
|
Java 程序员
Java 异步编程:概念、优势与实战示例
【4月更文挑战第27天】在现代软件开发中,异步编程是一种重要的编程范式,特别适用于处理长时间运行的任务,如网络通信、文件操作等。
12 0
|
5天前
|
Java 数据处理 API
Java 函数式编程:概念、优势与实战示例
【4月更文挑战第27天】函数式编程(Functional Programming,简称 FP)是一种编程范式,它将计算视为数学函数的求值并避免使用程序状态以及可变数据。
10 1