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版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
2月前
|
安全 Java 开发者
告别NullPointerException:Java Optional实战指南
告别NullPointerException:Java Optional实战指南
275 119
|
3月前
|
人工智能 Java API
Java AI智能体实战:使用LangChain4j构建能使用工具的AI助手
随着AI技术的发展,AI智能体(Agent)能够通过使用工具来执行复杂任务,从而大幅扩展其能力边界。本文介绍如何在Java中使用LangChain4j框架构建一个能够使用外部工具的AI智能体。我们将通过一个具体示例——一个能获取天气信息和执行数学计算的AI助手,详细讲解如何定义工具、创建智能体并处理执行流程。本文包含完整的代码示例和架构说明,帮助Java开发者快速上手AI智能体的开发。
1177 8
|
3月前
|
人工智能 Java API
Java与大模型集成实战:构建智能Java应用的新范式
随着大型语言模型(LLM)的API化,将其强大的自然语言处理能力集成到现有Java应用中已成为提升应用智能水平的关键路径。本文旨在为Java开发者提供一份实用的集成指南。我们将深入探讨如何使用Spring Boot 3框架,通过HTTP客户端与OpenAI GPT(或兼容API)进行高效、安全的交互。内容涵盖项目依赖配置、异步非阻塞的API调用、请求与响应的结构化处理、异常管理以及一些面向生产环境的最佳实践,并附带完整的代码示例,助您快速将AI能力融入Java生态。
569 12
|
3月前
|
Java 开发者
Java并发编程:CountDownLatch实战解析
Java并发编程:CountDownLatch实战解析
464 100
|
3月前
|
存储 前端开发 Java
【JAVA】Java 项目实战之 Java Web 在线商城项目开发实战指南
本文介绍基于Java Web的在线商城技术方案与实现,涵盖三层架构设计、MySQL数据库建模及核心功能开发。通过Spring MVC + MyBatis + Thymeleaf实现商品展示、购物车等模块,提供完整代码示例,助力掌握Java Web项目实战技能。(238字)
406 0
|
4月前
|
数据采集 JSON Java
Java爬虫获取1688店铺所有商品接口数据实战指南
本文介绍如何使用Java爬虫技术高效获取1688店铺商品信息,涵盖环境搭建、API调用、签名生成及数据抓取全流程,并附完整代码示例,助力市场分析与选品决策。
|
4月前
|
算法 Java 开发者
Java流程控制:条件与循环结构实战
本文深入讲解编程中的流程控制结构,涵盖条件语句(if-else、switch)、循环结构(for、while、do-while)及循环控制关键字(break、continue)的使用技巧与实战案例,帮助开发者写出更清晰、高效的代码。
|
4月前
|
Java API Maven
2025 Java 零基础到实战最新技术实操全攻略与学习指南
本教程涵盖Java从零基础到实战的全流程,基于2025年最新技术栈,包括JDK 21、IntelliJ IDEA 2025.1、Spring Boot 3.x、Maven 4及Docker容器化部署,帮助开发者快速掌握现代Java开发技能。
893 1
|
4月前
|
Java 关系型数据库 数据库
Java 项目实战教程从基础到进阶实战案例分析详解
本文介绍了多个Java项目实战案例,涵盖企业级管理系统、电商平台、在线书店及新手小项目,结合Spring Boot、Spring Cloud、MyBatis等主流技术,通过实际应用场景帮助开发者掌握Java项目开发的核心技能,适合从基础到进阶的学习与实践。
568 3