设计模式六大原则:面向对象设计的核心

news2024/9/24 6:21:32

在面向对象编程(OOP)中,设计模式为编写高效、可维护且可扩展的代码提供了重要指导。而这背后的核心是设计模式中的六大原则。本文将详细介绍这些原则,并使用Java代码实例进行说明。


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

通俗解释:
每个人只做自己分内的事,不要什么都想做。一个类只负责一个功能,不要让它承担太多职责。如果一个类既管账又做市场推广,那以后改起来会很麻烦。

原则说明:
单一职责原则要求一个类应该只有一个引起它变化的原因。换句话说,类应该专注于完成一个职责。将多个职责混合在同一个类中会导致类的复杂度增加,维护起来更加困难。

Java 代码示例:

class User {
    private String name;
    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public void save() {
        System.out.println("Saving user " + name + " to the database");
    }

    public void log(String message) {
        System.out.println("Log: " + message);
    }
}

上面代码违反了单一职责原则,因为User类既负责用户信息的保存,又负责日志记录。为了遵守SRP,可以把日志功能分离到一个单独的Logger类中:

class Logger {
    public void log(String message) {
        System.out.println("Log: " + message);
    }
}

class User {
    private String name;
    private String email;
    private Logger logger;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
        this.logger = new Logger();
    }

    public void save() {
        System.out.println("Saving user " + name + " to the database");
        logger.log("User " + name + " saved successfully");
    }
}

通过分离日志和用户信息保存的职责,每个类只专注于一个职责,从而提高了代码的可维护性。


2. 开闭原则(Open-Closed Principle, OCP)

通俗解释:
新的需求来了,咱们应该在不动老代码的前提下,尽量通过“扩展”来增加新功能,而不是改动现有的东西。就像装修房子的时候,尽量别砸老墙,只在新地方加点装饰。

原则说明1:
开闭原则要求软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。也就是说,添加新功能时不应该修改已有的代码,而是通过扩展实现。

原则说明2:

开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

Java 代码示例:

假设我们有两个类来计算不同形状的面积:

class Rectangle {
    private double width, height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public double area() {
        return width * height;
    }
}

class Circle {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double area() {
        return Math.PI * radius * radius;
    }
}

现在,如果需要添加三角形面积计算,不应该修改原有代码,而是通过扩展:

class Triangle {
    private double base, height;

    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }

    public double area() {
        return 0.5 * base * height;
    }
}

通过增加新的类而不修改现有类,符合开闭原则。


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

通俗解释:
子类应该能在任何地方都替代父类,并且表现良好。就好比用不同牌子的手机充电器,你只要插上就能用,而不会影响充电效果,不会突然坏掉。

原则说明1:
里氏替换原则要求子类对象必须能够替换其父类对象,并且程序的行为不会受到影响。即子类必须保持与父类兼容的行为。

原则说明2:
里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

Java 代码示例:

假设我们有一个Bird类,以及SparrowPenguin类:

class Bird {
    public void fly() {
        System.out.println("Bird is flying");
    }
}

class Sparrow extends Bird {
    @Override
    public void fly() {
        System.out.println("Sparrow is flying");
    }
}

class Penguin extends Bird {
    @Override
    public void fly() {
        System.out.println("Penguin can't fly");
    }
}

Penguin类不能飞,但是Bird类的定义要求子类能够飞,这违反了里氏替换原则。我们可以将“飞行”的概念抽象为一种行为,避免不适用的行为继承:

abstract class Bird {
    public abstract void move();
}

class Sparrow extends Bird {
    @Override
    public void move() {
        System.out.println("Sparrow is flying");
    }
}

class Penguin extends Bird {
    @Override
    public void move() {
        System.out.println("Penguin is swimming");
    }
}

通过这样设计,每个子类可以按照自己的特性定义“移动”行为,符合LSP。


4. 依赖倒置原则(Dependency Inversion Principle, DIP)

通俗解释:
老板不要直接管理具体的员工,而是通过经理(抽象的接口)来下达指令。这样,员工换了没关系,经理还在,工作还能正常进行。我们应该依赖“接口”而不是“具体实现”。

原则说明1:
依赖倒置原则要求高层模块不应该依赖于低层模块,二者都应该依赖于抽象。这个原则的核心在于使用接口或抽象类,而不是直接依赖具体实现。

原则说明2:
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

Java 代码示例:

假设我们有一个EmailService类,它提供发送邮件的功能:

class EmailService {
    public void sendEmail(String message) {
        System.out.println("Sending email: " + message);
    }
}

class Notification {
    private EmailService emailService;

    public Notification(EmailService emailService) {
        this.emailService = emailService;
    }

    public void notify(String message) {
        emailService.sendEmail(message);
    }
}

这种设计违反了DIP,因为Notification类依赖于具体的EmailService实现。我们可以通过引入接口进行改进:

interface MessageService {
    void sendMessage(String message);
}

class EmailService implements MessageService {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending email: " + message);
    }
}

class SMSService implements MessageService {
    @Override
    public void sendMessage(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

class Notification {
    private MessageService messageService;

    public Notification(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notify(String message) {
        messageService.sendMessage(message);
    }
}

通过依赖接口而不是具体类,我们可以轻松切换不同的消息服务(如SMS或Email),符合依赖倒置原则。


5. 接口隔离原则(Interface Segregation Principle, ISP)

通俗解释:
不要让一个人负责太多不相关的事情,接口也是一样,不要把一大堆不相关的方法放在一个接口里。每个人负责自己该做的事情就好,比如,一个人既要修车又要做饭,显然不合理。

原则说明1:
接口隔离原则要求客户端不应该依赖它不需要的接口。即接口应该尽可能小,确保客户端只依赖于它实际需要的方法。

原则说明2:
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

Java 代码示例:

假设我们有一个Worker接口,它既包含工作职责,也包含吃饭的职责:

interface Worker {
    void work();
    void eat();
}

如果有些工人不需要eat方法,这个设计就违反了接口隔离原则。我们可以将接口分离:

interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

这样,只有需要吃饭功能的类才会实现Eatable接口,符合ISP原则。


6. 迪米特法则(Law of Demeter, LoD)

通俗解释:
少打听别人的隐私,保持一定的“距离感”。一个类不需要知道其他类的太多细节,只需要知道自己该怎么用其他类公开的功能就好了,避免太过“亲密”导致耦合度太高。

原则说明1:
迪米特法则要求一个对象应尽量少了解其他对象的细节。也就是说,一个类应该尽量减少与其他类的直接交互,只与必要的对象交互。

原则说明2:
也叫最少知道原则:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。

Java 代码示例:

class Engine {
    public void start() {
        System.out.println("Engine started");
    }
}

class Car {
    private Engine engine;

    public Car() {
        engine = new Engine();
    }

    public void start() {
        engine.start();
    }
}

在这个例子中,Car类只与Engine类交互,并且只使用Engine提供的公开方法,而不涉及其内部实现细节,符合迪米特法则。


结论

设计模式的六大原则是面向对象设计的基石。通过遵循这些原则,开发者可以编写出更易维护、扩展性更强的代码。理解和应用这些原则对于提高代码质量和降低系统复杂性至关重要。

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

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

相关文章

2003-2022年各省区域创新能力评价相关指标数据(报告年份2003-2022年)

2003-2022年各省区域创新能力相关指标数据(报告年份2003-2022年) 1、来源:2003-2022年中国区城创新能力评价报告 2、指标:综合值、知识创造综合指标、研究开发投人综合指标、专利综合指标、科研论文综合指标、知识获取综合指标、…

个人导航网站介绍和部署

前言: 大家好,我是神的孩子都在歌唱,这是我csdn的博客 , 这是我做的一个神唱导航网站项目,这是一个练习项目,所以还存在很多问题,目的是方便收集和查阅日常浏览的网站,代码完全开源github&#…

LeetCode 面试经典150题 201.数字范围按位与

题目&#xff1a;给你两个整数 left 和 right &#xff0c;表示区间 [left, right] &#xff0c;返回此区间内所有数字 按位与 的结果&#xff08;包含 left 、right 端点&#xff09;。 提示&#xff1a;0 < left < right < 2^31 - 1 思路&#xff1a; 位与的特性…

leetcode91. 解码方法,动态规划

leetcode91. 解码方法 一条包含字母 A-Z 的消息通过以下映射进行了 编码 &#xff1a; “1” -> ‘A’ “2” -> ‘B’ … “25” -> ‘Y’ “26” -> ‘Z’ 然而&#xff0c;在 解码 已编码的消息时&#xff0c;你意识到有许多不同的方式来解码&#xff0c;…

【漏洞复现】HIKVISION 视频编码设备接入网关 showFile.php 任意文件下载漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

PHP智慧教育新篇章优校管理系统小程序源码

智慧教育新篇章 —— 优校管理系统 &#x1f680;【开篇启航&#xff1a;智慧教育的浪潮已至】 在这个日新月异的时代&#xff0c;教育也在悄然发生着变革。随着科技的飞速发展&#xff0c;智慧教育已成为教育领域的新风尚。而“优校管理系统”&#xff0c;正是这股浪潮中的佼…

Keil5 操作

目录 1.Debug&#xff08;软件模拟调试&#xff1a;&#xff09;&#xff1a; 2.代码提示设置&#xff1a; 3.添加. c与.h文件&#xff1a; 常用技巧 安装下载推荐&#xff1a;正点原子 1.Debug&#xff08;软件模拟调试&#xff1a;&#xff09;&#xff1a; 文章讲解 …

【例题】证明极限

已知&#xff1a; ∀ ε > 0 , ∃ n > N , ∣ a n − A ∣ < ε \forall \varepsilon >0, \exist n>N,|a_n-A|<\varepsilon ∀ε>0,∃n>N,∣an​−A∣<ε 目标&#xff1a; ∀ ε > 0 , ∃ n > N 1 , ∣ a 1 . . . a n n − A ∣ < ε \…

pytorch学习笔记二:用pytorch神经网络模型做气温预测、分类任务构建和分类网络构建、卷积神经网络原理介绍

文章目录 一、搭建pytorch神经网络进行气温预测1&#xff09;基础搭建2&#xff09;实际操作标识特征和标签3&#xff09;构建成标准化的预处理数据&#xff08;做标准化收敛速度更快&#xff09; 二、按照建模顺序构建完成网络架构1&#xff09;np.array格式的标签(y)和特征(x…

从入门到精通:计算机视觉学习路线与实战项目推荐

全面解析计算机视觉的学习路径&#xff0c;深入探讨关键技术与实战项目&#xff0c;助您快速掌握核心技能 引言 随着人工智能的飞速发展&#xff0c;计算机视觉已成为AI领域中最具潜力和应用价值的分支之一。从自动驾驶到医疗影像分析&#xff0c;计算机视觉技术正在改变我们的…

9.23-部署项目

部署项目 一、先部署mariadb [rootk8s-master ~]# mkdir aaa [rootk8s-master ~]# cd aaa/ [rootk8s-master aaa]# # 先部署mariadb [rootk8s-master aaa]# # configmap [rootk8s-master aaa]# vim mariadb-configmap.yaml apiVersion: v1 kind: ConfigMap metadata:name: ma…

【通俗易懂介绍OAuth2.0协议以及4种授权模式】

文章目录 一.OAuth2.0协议介绍二.设计来源于生活三.关于令牌与密码的区别四.应用场景五.接下来分别简单介绍下四种授权模式吧1.客户端模式1.1 介绍1.2 适用场景1.3 时序图 2.密码模式2.1 介绍2.2 适用场景2.3时序图 3.授权码模式3.1 介绍3.2 适用场景3.3 时序图 4.简化模式4.1 …

【LIO-SAM】LIO-SAM论文翻译(2020年)

【LIO】LIO-SAM论文翻译&#xff08;2020年&#xff09; 1&#xff0e;Abstract&#xff12;&#xff0e;INTRODUCTION&#xff14;&#xff0e;通过平滑和映射实现激光雷达惯性里程计A. 系统概述B. IMU Preintegration Factor&#xff08;推导过程参阅&#xff09;C. Lidar Od…

对onlyoffice进行定制化开发

基于onlyoffice8.0源码&#xff0c;进行二次开发&#xff0c;可实现包括但不限于以下的功能 1、内容控件的插入 2、内容空间的批量替换 3、插入文本 4、插入图片 5、添加&#xff0c;去除水印 6、修改同时在线人数限制 7、内容域的删除 8、页面UI的定制化 9、新增插件开发 10、…

Pytest-如何将allure报告发布至公司内网

原理简介 使用Python启动HTTP服务器&#xff0c;指定一个端口号port&#xff0c;内网用户可以使用ipport访问报告。 本文章继续进阶&#xff0c;简单使用nginx进行一个代理&#xff0c;使用域名可以直接访问报告。 前情概述 Pytest-allure如何在测试完成后自动生成完整报告&am…

Ansible流程控制-条件语句_循环语句

文章目录 Ansible流程控制条件语句且、或、非、是模糊条件when指令的详细使用方法 循环语句如何使用使用item变量结合with_items或loop指令item变量有固定子元素&#xff1f; 实例-服务器安装基础环境优化需求部分实现换指定新仓库安装基础软件包 Ansible流程控制 一、 1. 条件…

opencv4.5.5 GPU版本编译

一、安装环境 1、opencv4.5.5 下载地址&#xff1a;https://github.com/opencv/opencv/archive/refs/tags/4.5.5.ziphttps://gitee.com/mirrors/opencv/tree/4.5.0 2、opencv-contrib4.5.5 下载地址&#xff1a;https://github.com/opencv/opencv_contrib/archive/refs/tags/4…

塑料瓶回收流水线分拣系统源码分享

塑料瓶回收流水线分拣检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comp…

介绍GPT-o1:一系列解决困难问题( science, coding, and math )的推理模型

openai o1介绍 一、官方技术报告要点剖析实验1 benchmark分析实验2:和phd比赛技术细节&#xff1a;Chain of Thought的使用人类偏好评估Human preference evaluationsatety技术细节&#xff1a;隐藏思维链为监控模型提供了机会:)openai的几点conclusion 二、官方介绍剖析 Intro…

【C++】8.类和对象(6)

文章目录 5. 内部类6. 匿名对象7. 对象拷贝时的编译器优化 5. 内部类 如果一个类定义在另一个类的内部&#xff0c;这个内部类就叫做内部类。内部类是一个独立的类&#xff0c;跟定义在全局相比&#xff0c;他只是受外部类类域限制和访问限定符限制&#xff0c;所以外部类定义的…