SOLID原则之Java实战(1)

简介: NotificationService 应该是一个接口。然后我将在另外 3 个服务中实现该接口,分别为 MobileNotificationService、EmailNotificationService 和 WhatsAppNotificationService。

本文翻译自国外论坛 medium,原文地址:salithachathuranga94.medium.com/solid-princ…

本文将带领大家学习在日常编程中如何使用 SOLID 原则。

image.png

如果你是一名优秀的编程人员,那么我要讨论的内容应该是一个众所周知的话题!废话不多说,让我们进入主题。

SOLID 原则由 Robert C. Martin 提出。它们是创建更易于维护、更易于理解和更灵活的软件代码的设计原则。这些经验法则帮助我们以更少的复杂性来编写我们的项目代码。SOLID 原则五个单词含义如下:

  • 单一职责原则 (SRP) [ S ]
  • 开闭原理 (OCP) [ O ]
  • 里氏替换原理 (LSP) [ L ]
  • 接口隔离原则 (ISP) [ I ]
  • 依赖倒置原则 (DIP) [ D

现在我们用实际例子来一一列举。

单一职责原则(SRP)

该原则规定每个 Java 类必须执行单一功能。在这里单一功能意味着:类必须执行只属于该类的操作。

假设我们有一个名为 BankService 的类。在应用单一职责原则(SRP)之前会是这样的。存款、取款、发送通知、打印存折等所有操作均由 BankService 完成。这样 BankService 类就具有多个彼此不相关的职责。

java

复制代码

public class BankService {
    public void withdraw(double amount) {
        System.out.println("Withdraw money : " + amount);
    }
    public void deposit(double amount) {
        System.out.println("Deposit money : " + amount);
    }
    public String getLoanInfo(String loanType) {
        if (loanType.equals("professional")) {
            return "Professional Loan";
        } else if (loanType.equals("home")) {
            return "Home Loan";
        } else {
            return "Personal Loan";
        }
    }
    public void printPassbook() {
        System.out.println("Printing Book Details...");
    }
    public void sendOTP(String medium) {
        if (medium.equals("mobile")) {
            System.out.println("Sending OTP to mobile");
        } else if (medium.equals("email")) {
            System.out.println("Sending OTP to email");
        } else {
            System.out.println("Not a valid medium");
        }
    }
}

让我们将单一职责原则(SRP)应用于 BankService。我们可以将职责划分为多组服务。

java

复制代码

// BankService
public class BankService {
    public void withdraw(double amount) {
        System.out.println("Withdraw money : " + amount);
    }
    public void deposit(double amount) {
        System.out.println("Deposit money : " + amount);
    }
}
// LoanService
public class LoanService {
    public String getLoanInfo(String loanType) {
        if (loanType.equals("professional")) {
            return "Professional Loan";
        } else if (loanType.equals("home")) {
            return "Home Loan";
        } else {
            return "Personal Loan";
        }
    }
}
// PrinterService
public class PrinterService {
    public void printPassbook() {
        System.out.println("Printing Book Details...");
    }
}
// NotificationService
public class NotificationService {
    public void sendOTP(String medium) {
        if (medium.equals("mobile")) {
            System.out.println("Sending OTP to mobile");
        } else if (medium.equals("email")) {
            System.out.println("Sending OTP to email");
        } else {
            System.out.println("Not a valid medium");
        }
    }
}

现在你可以看到每个类都在执行自己的操作。这样代码看起来更加清晰易懂。这就是单一职责原则(SRP)的意义!

开闭原则(OCP)

该原则规定,当有新需求到来,模块应该对扩展开放,对修改关闭。这样我们应该就能够在现有代码的基础上添加扩展,而不改变原来的基本实现,这使得我们更容易扩展逻辑。

假设我们有一个名为 NotificationService 的服务可以向各种媒介发送通知。我们有两种发送 OTP 通知的方法。他们是手机和电子邮件。如果出现通过 WhatsApp 发送 OTP 通知的新要求,会发生什么情况。想象一下,我们应该做什么?我们只能修改原来 NotificationService 的代码!这就违反了开闭原则(OCP)!

让我们将开闭原则(OCP)应用到这个场景中。我将重新实现这个 NotificationService 类,准确来说 NotificationService 应该是一个接口。然后我将在另外 3 个服务中实现该接口,分别为 MobileNotificationService、EmailNotificationService 和 WhatsAppNotificationService。

java

复制代码

public interface NotificationService {
    void sendOTP(String medium);
    void sendTransactionHistory(String medium);
}
public class MobileNotificationService implements NotificationService {
    @Override
    public void sendOTP(String medium) {
        System.out.println("Sending OTP Number Message to: " + medium);
    }
    @Override
    public void sendTransactionHistory(String medium) {
        System.out.println("Sending Transactions Message to: " + medium);
    }
}
public class EmailNotificationService implements NotificationService {
    @Override
    public void sendOTP(String medium) {
        System.out.println("Sending OTP Number Email to: " + medium);
    }
    @Override
    public void sendTransactionHistory(String medium) {
        System.out.println("Sending Transactions Email to: " + medium);
    }
}
public class WhatsAppNotificationService implements NotificationService {
    @Override
    public void sendOTP(String medium) {
        System.out.println("Sending OTP Number to: " + medium);
    }
    @Override
    public void sendTransactionHistory(String medium) {
        System.out.println("Sending Transactions Details to: " + medium);
    }
}

如果我想发送所有 3 种类型的通知,我可以这样做。

image.png

image.png

如果又有另一种类型的新媒介需要发送那会发生什么?我们只需要创建另一个服务,从 NotificationService 实现它并完成与新媒介相关的逻辑就行!

这就是所有代码!我们已经成功应用开闭原则(OCP)。

里氏替换原则(LSP)

据说这是大多数开发人员最难理解的原则。这是由芭芭拉·利斯科夫(Barbara Liskov)介绍的。它适用于继承,子类必须完全可替换其父类

如果类 A 是类 B 的子类型,那么我们应该能够在不中断程序行为的情况下用 A 替换 B。

让我们通过一个例子来理解这一点,但是我要提醒你,这个原则会让文章变得很长。🤔

假设我们要管理多种类型的社交媒体平台。它们是 Facebook、Instagram 和 WhatsApp。

image.png

因此,SocialMedia 类有 3 个方法,分别称为 chat()、publish() 和 groupCall()。为了展现里氏替换原则(LSP)的作用,我将直接用 Facebook、Instagram、WhatsApp 3 个类来实现这一点。

java

复制代码

public abstract class SocialMedia {
    abstract void chat(String user);
    abstract void publish(Object post);
    abstract void groupCall(String... users);
}
public class Facebook extends SocialMedia {
    @Override
    void chat(String user) {
    }
    @Override
    void publish(Object post) {
    }
    @Override
    void groupCall(String... users) {
    }
}
public class Instagram extends SocialMedia {
    @Override
    void chat(String user) {
    }
    @Override
    void publish(Object post) {
    }
    @Override
    void groupCall(String... users) {
    }
}
public class WhatsApp extends SocialMedia {
    @Override
    void chat(String user) {
    }
    @Override
    void publish(Object post) {
    }
    @Override
    void groupCall(String... users) {
    }
}

SocialMedia 是一个抽象类,所有其他平台都是它的子类。预期的逻辑现已实现。那么这里出了什么问题呢?

你是否注意到,这样做的话,所有平台都必须实现这三个方法,即使该平台不支持此方法!让我解释一下

Facebook:支持所有方法 chat()、publish() 和 groupCall()。

WhatsApp:不支持发布帖子 publish()。

Instagram:不支持群组通话 groupCall()。

所以在这种情况下,Instagram 或 WhatsApp 不能完全替代 SocialMedia 类!

现在让我们在这里应用里氏替换原则(LSP)。

java

复制代码

public interface SocialMedia {
     void chat(String user);
}
public interface PostManager {
    void publish(Object post);
}
public interface VideoCallManager {
    void groupCall(String... users);
}
public class Facebook implements SocialMedia, PostManager {
    @Override
    public void publish(Object post) {
        System.out.println("Publishing a post on Facebook: " + post);
    }
    @Override
    public void chat(String user) {
        System.out.println("Chatting on Facebook with: " + user);
    }
}
public class WhatsApp implements SocialMedia, VideoCallManager {
    @Override
    public void chat(String user) {
        System.out.println("Chatting on WhatsApp with: " + user);
    }
    @Override
    public void groupCall(String... users) {
        System.out.println("Taking a Group Call on WhatsApp with: " + Arrays.toString(users));
    }
}

你现在可以看到 SocialMedia 是一个具有单一职责的接口,包含 chat() 方法。因此我们有单独的界面来管理视频通话和发布帖子。这样所有子类:Facebook 和 WhatsApp 都会执行它们只能执行的操作!

  • WhatsApp 是 SocialMedia 和 VideoCallManager 的子类
  • Facebook 是 SocialMedia 和 PostManager 的子类

让我们检查一下定义:

如果 WhatsApp 类是 SocialMedia 类的子类,那么我们应该能够在不中断程序行为的情况下用 WhatsApp 替换 SocialMedia。

image.png

image.png

这就是里氏替换原则(LSP)的全部内容!!!讲解完了!

目录
相关文章
|
2月前
|
存储 Java 开发者
Java Map实战:用HashMap和TreeMap轻松解决复杂数据结构问题!
【10月更文挑战第17天】本文深入探讨了Java中HashMap和TreeMap两种Map类型的特性和应用场景。HashMap基于哈希表实现,支持高效的数据操作且允许键值为null;TreeMap基于红黑树实现,支持自然排序或自定义排序,确保元素有序。文章通过具体示例展示了两者的实战应用,帮助开发者根据实际需求选择合适的数据结构,提高开发效率。
74 2
|
3天前
|
Java
Java基础却常被忽略:全面讲解this的实战技巧!
本次分享来自于一道Java基础的面试试题,对this的各种妙用进行了深度讲解,并分析了一些关于this的常见面试陷阱,主要包括以下几方面内容: 1.什么是this 2.this的场景化使用案例 3.关于this的误区 4.总结与练习
|
20天前
|
Java 程序员
Java基础却常被忽略:全面讲解this的实战技巧!
小米,29岁程序员,分享Java中`this`关键字的用法。`this`代表当前对象引用,用于区分成员变量与局部变量、构造方法间调用、支持链式调用及作为参数传递。文章还探讨了`this`在静态方法和匿名内部类中的使用误区,并提供了练习题。
21 1
|
1月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
52 6
|
1月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
2月前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
2月前
|
开发框架 Java 程序员
揭开Java反射的神秘面纱:从原理到实战应用!
本文介绍了Java反射的基本概念、原理及应用场景。反射允许程序在运行时动态获取类的信息并操作其属性和方法,广泛应用于开发框架、动态代理和自定义注解等领域。通过反射,可以实现更灵活的代码设计,但也需注意其性能开销。
55 1
|
2月前
|
缓存 Java 数据库
JAVA分布式CAP原则
JAVA分布式CAP原则
69 0
|
2月前
|
缓存 前端开发 Java
Java中的RESTful API原则
总结而言,遵循RESTful原则不仅能够提升API的互操作性,还便于维护和扩展,是构建现代Web服务的重要实践。通过精心设计的URI、利用HTTP协议特性以及采用成熟框架如Spring Boot,Java开发者能够高效地创建出既强大又易于使用的RESTful API。
58 0
|
3月前
|
Java 数据中心 微服务
Java高级知识:线程池隔离与信号量隔离的实战应用
在Java并发编程中,线程池隔离与信号量隔离是两种常用的资源隔离技术,它们在提高系统稳定性、防止系统过载方面发挥着重要作用。
73 0