厚积薄发打卡Day113:Debug设计模式:设计原则(一)<开闭原则、依赖倒置、单一职责>

news2024/11/18 6:27:16

厚积薄发打卡Day113:Debug设计模式:设计原则(一)<开闭原则、依赖倒置、单一职责>

开闭原则

定义

一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。其优点:提高软件系统的可复用性及可维护性

用抽象构建框架,用实现扩展细节,实现 面向抽象编程。

中心思想:抽象构建框架,实现扩展细节

场景举例Coding

在线课程购买例子,课程在线购买:

在这里插入图片描述

/**
 * 课程接口
 */
public interface ICourse {
    // 课程id
    Integer getId();

    // 课程名字
    String getName();

    // 课程价格
    Double getPrice();
}
/**
 * Java课程
 */
public class JavaCourse implements ICourse {
    private Integer Id;
    private String name;
    private Double price;

    public JavaCourse(Integer id, String name, Double price) {
        Id = id;
        this.name = name;
        this.price = price;
    }

    @Override
    public Integer getId() {
        return this.Id;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public Double getPrice() {
        return this.price;
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        ICourse course = new JavaCourse(1415, "Java企业级别开发", 299d);
        System.out.printf("课程id: %d,课程名称:%s,课程价格:%f 元\n", course.getId(), course.getName(), course.getPrice());
    }
}
课程id: 1415,课程名称:Java企业级别开发,课程价格:299.000000 元

场景变更:现在遇到活动,线上课程通通八折!那应该怎么修改呢?

~~修改接口?~~那会影响全部实现类,如果有一个实现类则修改一个实现类,那如果实现类很多是否都需要一个个进行修改呢?

**软件开发设计重点:接口不应该轻易修改,稳定且可靠,作为契约。**同时,越底层的模块变化影响越大,越高层的模块变化影响则越小。

那不在影响原有代码的基础上进行扩展,最好的方法就是使用Java的多态特性:继承。

场景实现:

在这里插入图片描述

public class DiscountCourse extends JavaCourse {
    public DiscountCourse(Integer id, String name, Double price) {
        super(id, name, price);
    }

    public Double getOriginPrice() {
        return super.getPrice();
    }


    public Double getDiscountPrice() {
        // 同时这里可以对折扣价格方案进行处理
        return super.getPrice() * 0.8;
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        ICourse course = new JavaCourse(1415, "Java企业级别开发", 299d);
        System.out.printf("课程id: %d,课程名称:%s,课程价格:%f 元\n", course.getId(), course.getName(), course.getPrice());

        DiscountCourse course2 = new DiscountCourse(1415, "Java企业级别开发", 299d);
        System.out.printf("课程id: %d,课程名称:%s,课程原始价格:%f 元,618活动折扣价为:%f 元\n", course2.getId(), course2.getName(), course2.getOriginPrice(), course2.getDiscountPrice());
    }
}
课程id: 1415,课程名称:Java企业级别开发,课程价格:299.000000 元
课程id: 1415,课程名称:Java企业级别开发,课程原始价格:299.000000 元,618活动折扣价为:239.200000 元

这样通过继承的方式,不改变原有代码的基础上,实现了折扣价方案的修改实现。

依赖倒置

定义:

  • 高层模块不应该依赖低层模块,二者都应该依赖其抽象。

  • 抽象不应该依赖细节;细节应该依赖抽象

  • 核心思想:针对接口编程,面向接口编程,不要针对实现编程

优点:

  • 可以减少类间的耦合性、提高系统稳定性,提高代码可读性和可维护性,可降低修改程序所造成的风险

场景Coding:

  • 举例:面向实现类编程

    public class Wayne {
        public void studyJavaCourse(){
            System.out.println("Wayne在学习Java课程");
        }
        public void studyPythonCourse(){
            System.out.println("Wayne在学习Python课程");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // v1
            Wayne wayne = new Wayne();
            wayne.studyJavaCourse();
            wayne.studyPythonCourse();
        }
    }
    
    Wayne在学习Java课程
    Wayne在学习Python课程
    
    • 此时Test是应用层,Wayne是底层模块,如果这时候wayne要学习更多课程,则在底层模块进行修改,违背了高层模块不应该依赖低层模块,直接引用了而不是通过抽象依赖。
  • 引用抽象

    在这里插入图片描述

    // 抽象依赖
    public interface IStudy {
        void studyCourse();
    }
    

    依赖实现:

    public class GoStudy implements IStudy {
        @Override
        public void studyCourse() {
            System.out.println("Wayne在学习Go语言");
        }
    }
    
    public class JavaStudy implements IStudy{
        @Override
        public void studyCourse() {
            System.out.println("Wayne在学习Java");
        }
    }
    
    public class PythonStudy implements IStudy {
        @Override
        public void studyCourse() {
            System.out.println("Wayne在学习Python");
        }
    }
    

    底层:

    public class Wayne {
        public void studyCourse(IStudy study) {
            study.studyCourse();
        }
    }
    

    应用层:

    public class Test {
        public static void main(String[] args) {
            //v2
            Wayne wayne = new Wayne();
            wayne.studyCourse(new PythonStudy());
            wayne.studyCourse(new JavaStudy());
            wayne.studyCourse(new GoStudy());
        }
    }
    
    Wayne在学习Python
    Wayne在学习Java
    Wayne在学习Go语言
    
  • 使用构造器注入(单例模式):有点Spring的味道了

public class Wayne {
    // ---单例:
    private IStudy iStudy;

    public Wayne(IStudy iStudy) {
        this.iStudy = iStudy;
    }

    public void studyCourse() {
        iStudy.studyCourse();
    }
}
//v3:单例模式:
new Wayne(new JavaStudy()).studyCourse();
new Wayne(new PythonStudy()).studyCourse();
  • set方法注入:注入方式无需修改底层模块,即后续想要学习什么课程直接扩展IStudy即可。

    public class Wayne {
        // set注入
        private IStudy iStudy;
    
        public void setiStudy(IStudy iStudy) {
            this.iStudy = iStudy;
        }
        public void studyCourse() {
            iStudy.studyCourse();
        }
    }
    
        //v4:set注入
        Wayne wayne = new Wayne();
        wayne.setiStudy(new JavaStudy());
        wayne.studyCourse();
    
        wayne.setiStudy(new GoStudy());
        wayne.studyCourse();
    

    综上总结:面向接口编程

单一职责

定义:

不要存在多于一个导致类变更的原因,一个类/接口/方法只负责一项职责

优点:降低类的复杂度、提高类的可读性,提高系统的可维护性、降低变更引起的风险

场景Coding

  • 场景耦合

    public class Bird {
        public void mainMoveMode(String birdName) {
            //使用边界判断
            if ("鸵鸟".equals(birdName)) {
                System.out.println(birdName + "用脚走");
            } else {
                System.out.println(birdName + "用翅膀飞");
            }
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            Bird bird = new Bird();
            bird.mainMoveMode("鸵鸟");
            bird.mainMoveMode("大雁");
            bird.mainMoveMode("鸽子🕊");
        }
    }
    
    鸵鸟用脚走
    大雁用翅膀飞
    鸽子🕊用翅膀飞
    

    如果后面增加飞行动物,则需要不断修改Bird里面的代码,同时功能并不单一,违背单一职责原则。

  • 类扩展,实现单一原则

在这里插入图片描述

public class FlyBird {
    public void mainMoveMode(String birdName){
        System.out.println(birdName+"用翅膀飞");
    }
}
public class WalkBird {
    public void mainMoveMode(String birdName){
        System.out.println(birdName+"用脚走");
    }
}
public class Test {
    public static void main(String[] args) {
        WalkBird walkBird = new WalkBird();
        walkBird.mainMoveMode("鸵鸟");

        FlyBird flyBird = new FlyBird();
        flyBird.mainMoveMode("大雁");
    }
}
鸵鸟用脚走
大雁用翅膀飞
  • 接口扩展,以课程举例

    public interface ICourse {
        // 课程内容管理
        String getCourseName();
        byte[] getCourseVideo();
    
        // 课程中心管理
        void studyCourse();
        void refundCourse();
    }
    

    接口在实现类中进行细分:

在这里插入图片描述

public interface ICourseContent {
    String getCourseName();
    byte[] getCourseVideo();
}
public interface ICourseManager {
    void studyCourse();
    void refundCourse();
}
public class CourseImpl implements ICourseManager, ICourseContent {
    @Override
    public void studyCourse() {
    }

    @Override
    public void refundCourse() {
    }

    @Override
    public String getCourseName() {
        return null;
    }

    @Override
    public byte[] getCourseVideo() {
        return new byte[0];
    }
}
  • 方法级别,单一职责,方法一般通过对象实现,酌情参考。

    public class Method {
        private void updateUserInfo(String userName, String address) {
            userName = "wayne";
            address = "beijing";
        }
    
        private void updateUserInfo(String userName, String... properties) {
            userName = "wayne";
    //        address = "beijing";
        }
    
        private void updateUsername(String userName) {
            userName = "wayne";
        }
    
        private void updateUserAddress(String address) {
            address = "beijing";
        }
    
        private void updateUserInfo(String userName, String address, boolean bool) {
            if (bool) {
                //todo something1
            } else {
                //todo something2
            }
            userName = "wayne";
            address = "beijing";
        }
    }
    

总结:单一职责会新增很多类,若盲目使用则会造成类爆炸要根据实际情况,尽量做到接口和方法的单一职责

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

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

相关文章

JavaEE多线程-创建线程(Thread)

目录一、线程(Thread)1.1 Thread类中的构造方法1.2 启用线程的方法二、创建第一个多线程三、多线程并发执行简单演示四、多线程并发执行的优势五、Thread的常见构造方法和属性5.1 属性5.2 方法六、中断线程七、线程等待一、线程(Thread) 线程是操作系统中的概念. 操作系统内核…

LeetCode二叉树经典题目(六):特殊位置构造二叉树

目录 21. LeetCode404. 左叶子之和 22.LeetCode513. 找树左下角的值 23. LeetCode112. 路径总和 24. LeetCode113. 路径总和 II 25. LeetCode106. 从中序与后序遍历序列构造二叉树 26. LeetCode105. 从前序与中序遍历序列构造二叉树​编辑 27. LeetCode654. 最大二叉树 …

LED、Mini LED、Micro LED、LCD、OLED技术

1、传统led、miniled、microled的异同 2、OLED OLED&#xff08;Organic Light-Emitting Diode&#xff09;&#xff0c;又称为有机电激光显示、有机发光半导体&#xff08;Organic Electroluminescence Display&#xff0c;OLED&#xff09;。OLED属于一种电流型的有机发光器…

S32K144-hello_word点灯

官方提供了很多的参考例程&#xff0c;每个历程分别配置了不同的外设&#xff0c;这里挨个尝试解读一下。 示例效果 RGB红灯绿灯交替闪烁。 导入示例 示例文件所在目录&#xff1a; 该示例使用PCC和PORT模块交替切换两个LED。 硬件连接 配置引脚功能 生成代码 S32DS自带引…

C#上位机基础学习_登录窗体的创建方法和步骤

C#上位机基础学习_登录窗体的创建方法和步骤 本次和大家分享如何制作一个简单的登录窗体。具体的方法和步骤可以参考以下内容: 如下图所示,打开Visual Studio 2019,新建一个Windows 窗体应用(.NET Framework), 如下图所示,在窗体中添加Label标签、Text文本框、Button按…

Java中常用API总结(5)—— Object类中的深克隆和浅克隆

对象克隆一、前言二、浅克隆1.概述2.实例1️⃣思路2️⃣继承cloneable接口底层原理3️⃣重写clone方法底层原理3.代码实现三、深克隆1.概述2.实例3.代码实现四、结语一、前言 本文将详细讲述Object类中的对象克隆方法&#xff0c;其中包含深克隆和浅克隆&#xff0c;两者有一定…

算法竞赛100天第2天——STL IN C++(算法竞赛必备知识总结汇总)

本文已收录于专栏 &#x1f332;《百日算法竞赛》&#x1f332; 目录 前言&#xff1a; 序列容器 序列的要求&#xff1a; 1.vector vector常用方法 vector遍历 2、deque 头文件 方法 3、list 头文件 方法 4、queue 方法&#xff1a; 关联容器 1、map 2、se…

SWPUCTF 2022新生赛部分wp

&#x1f60b;大家好&#xff0c;我是YAy_17&#xff0c;是一枚爱好网安的小白。 本人水平有限&#xff0c;欢迎各位大佬指点&#xff0c;一起学习&#x1f497;&#xff0c;一起进步⭐️。⭐️此后如竟没有炬火&#xff0c;我便是唯一的光。⭐️[SWPUCTF 2022 新生赛]ez_ez_ph…

【算法】链表

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录链表数组转链表链表转数组往链表的前面增加一个节点获得指定位置的值在链表的末尾增加一个节点在…

我最近发现的一些问题。

‍‍ 大家好&#xff0c;我是小z&#xff0c;也可以叫我阿粥~这段时间看了不少分析报告&#xff0c;启发颇多。也发现有一些普遍存在且很容易误导分析师的问题。在聊问题之前&#xff0c;先给大家分享一个&#xff08;我刚写的&#xff09;小故事&#xff1a;有一个学生…

Pytorch 多项式拟合

目录 1、训练误差和泛化误差 2、独立同分布假设 3、欠拟合和过拟合 4、多项式回归 1、训练误差和泛化误差 训练误差&#xff08;training error&#xff09;是指&#xff0c; 模型在训练数据集上计算得到的误差。 泛化误差&#xff08;generalization error&#xff09;是指…

【OpenCV 例程 300篇】255.OpenCV 实现图像拼接

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程 300篇】255.OpenCV 实现图像拼接 6.2 OpenCV 实现图像拼接 OpenCV中图像的数据结构是Numpy数组&#xff0c;使用切片方法可以实现图像的裁剪&#xff0c;使用数组堆叠方法可以实现图像的拼接。 Numpy 函数…

K8S Replication Controller 示例

K8S Replication Controller Replication Controller可确保在任何时间运行指定数量的pod副本。换句话说&#xff0c;ReplicationController确保一个pod或一组同类pod始终处于可用状态。 可以理解为Replication Controller &#xff08;RC&#xff09;是基于 K8S Pod对象之上的…

PR采购申请启用灵活工作流配置

目录 1. 检查系统基础工作流配置 2. 激活标准场景模版 WS02000458 3. 维护收件箱操作按钮文本 4. 维护任务代理及激活事件 5. 配置Inbox审批页面 6. PR灵活工作流启动配置 7. 为流程设置代理人 8. 配置工作流场景 9. 可扩展部分 官方Help文档路径&#xff1a; SAP He…

算法_杨氏矩阵_杨氏矩阵算法_剑指offer

目录 一、问题描述 二、问题分析 三、算法设计 ​四、代码实现 一、问题描述 有一个数字矩阵&#xff0c;矩阵的每行从左到右是递增的&#xff0c;矩阵从上到下是递增的&#xff0c;请编写程序在这样的矩阵中查找某个数字是否存在。 要求&#xff1a;时间复杂度小于O(N);…

【目标检测】Cascade RCNN中的两个常见问题

一、关于mismatch问题 在training阶段和inference阶段使用不同的阈值很容易导致mismatch&#xff0c;什么意思呢&#xff1f; 在training阶段&#xff0c;由于给定了GT&#xff0c;所以可以把与GT的IoU大于阈值的proposals作为正样本&#xff0c;这些正样本参与之后的bbox回归…

【C语言】深入浅出讲解函数栈帧(超详解 | 建议收藏)

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;凡人修C传 &#x1f4ac;总结&#xff1a;希望你看完之后&…

基于机器学习与协同过滤的图书管理推荐系统

基于机器学习与协同过滤的图书推荐系统 一、系统结构图 二、Demo示例 完整源码可联系博主微信【1257309054】 点我跳转 三、K-means聚类机器学习推荐算法 1、原理 从数据库中 1、首先获取书籍类别 2、获取用户注册时勾选喜欢的类别&#xff0c;勾选的为1&#xff0c;否则…

让 Java Agent 在 Dragonwell 上更好用

本文是《容器中的Java》系列文章之 3/n&#xff0c;欢迎关注后续连载 &#x1f603; 。1 背景 随着越来越多的云原生微服务应用的大规模部署&#xff0c;大家对微服务治理的能力需求越来越强。 Java Agent技术能够让业务专注于业务逻辑&#xff0c;与此同时&#xff0c;中间…

kali渗透测试到底该如何学?

1、渗透测试是什么&#xff1f; 渗透测试&#xff0c;是为了证明网络防御按照预期计划正常运行而提供的一种机制。渗透测试是通过各种手段对目标进行一次渗透&#xff08;攻击&#xff09;&#xff0c;通过渗透来测试目标的安全防护能力和安全防护意识。打个比方&#xff1a;比…