【设计模式】软件设计原则——开闭原则里氏替换单一职责

news2025/2/4 17:58:51

开闭原则内容引出

 

开闭原则

定义:一个软件实体,类,函数,模块;对扩展开放,对修改关闭。用抽象构建框架,用实现扩展细节。可以提高软件的可复用性和可维护性。

开发新功能时,尽量不修改原有代码,尽量使用扩展来增加新功能。

实现开闭原则的核心思想是:面向抽象编程,而不是面向实现编程。

定义的对象类型是:抽象or接口;调用的是抽象类or接口中的方法。抽象是稳定的,让一个类依赖于抽象,实现对修改关闭。再通过面向对象的继承多态,实现对抽象的继承,通过重写方法or实现新的扩展方法。

开闭原则示例

/**
 * 商品接口
 */
public interface IGood {
    Integer getId();
    String getName();
    Double getPrice();
}
/**
 * 普通商品
 */
public class NormalGood implements IGood {
    private Integer id;
    private String name;
    private Double price;

    public NormalGood(Integer id, String name, Double price) {
        this.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;
    }

    @Override
    public String toString() {
        return "...普通商品信息"
    }
}
/**
添加打折功能
根据开闭原则 , 对修改关闭 , 对扩展开放;
定义子类,在继承普通商品的基础上,扩展的新功能;
不用实现接口,而是继承普通类。
站在会员卡角度如下:
card接口;普通用户类;银卡继承普通用户;金卡继承银卡
*/
public class DiscountGood extends NormalGood {

    public DiscountGood(Integer id, String name, Double price) {
        super(id, name, price);
    }

    @Override
    public Double getPrice() {
        return super.getPrice() * 0.8;//折扣位置
    }
}

里氏替换原则

子类可以扩展父类的功能,但不能改变父类原有的功能。

是开闭原则的重要方式之一,由于使用父类对象的地方都可以使用子类对象,因此在程序中尽量使用父类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。

public class FatherClass {
    public void method(HashMap map) {
        System.out.println("执行父类 void method(HashMap map) 方法");
    }
}

public class ChildClass extends FatherClass {
    /**
     * 重写
     * 重写 ( 返回值 严格 ) : 当 子类的方法 重写 / 重载 / 实现 父类的方法时
     *      方法的 后置条件 ( 返回值 ) 要 比父类更严格或相等;
     */
    @Override
    public void method(HashMap map) {
        System.out.println("执行子类重写的 void method(HashMap map) 方法");
    }

    /**
     * 重载
     * 重载 ( 输入参数 宽松 ) : 子类的方法 重载 父类的方法 时
     *      方法的前置条件 ( 输入参数 ) , 要比 父类方法的输入参数更宽松 ;
     *
     * 如果在父类中参数类型是 Map
     *      在子类中重载参数类型是 HashMap
     *      这样就会出现混乱的问题
     * 客户端调用时 , 可能不清楚情况 , 加入传入了 HashMap 参数
     *      此时就有可能出现混乱 , 无法调用到 父类/子类的 正常重写方法
     *      方法调用被重载方法拦截的情况
     *
     * 如果 重载的方法 的参数 比父类的方法参数更严格
     *      那么这就不是重载方法 , 而是重写方法
     */
    public void method(Map map) {
        System.out.println("执行子类重载的 void method(Map map) 方法");
    }

}

 


 单一职责内容引出

单一职责

定义:不要存在多余一个导致类变更的原因。

假设有一个类,负责“A”和“B”;一旦需求变更,如A功能改变,修改该类A功能时,有可能导致B功能发生故障。对于该情况,应该对于AB各自建立独立的类,保证系统的稳定性。

开发方法:一个类只负责一项职责(类、接口、方法)

优点:可读性高、提高系统可维护性、降低类的复杂度、降低需求变更导致的风险。

模块化的系统中,都适合使用单一职责。

//以下代码均违反了单一职责原则
public class Dog{
    public void mainDogs(String name) {
        if ("小狗".equals(name)) {
            System.out.println("puppy's name is "+name);
        } else {
            System.out.println("Dog's name is "+name);
        }
    }
}

//-----------------------------------------------------------------

public class Order {
    private String orderId;
    private String userId;
    private String productId;
    private double amount;
    private OrderStatus status;

    public void createOrder(String userId, String productId, double amount) {
        // 创建订单  
    }

    public void payOrder() {
        // 支付订单  
    }

    public void shipOrder() {
        // 发货 
    }

    public void completeOrder() {
        // 完成订单 
    }
}

类的单一

/**
 *本类的职责单一,只负责puppy
 */
public class Puppy{
    public void IsPuppy(String name) {
        System.out.println("Puppy's name is "+name);
    }
}
public class Dog{
    public void IsDog(String name) {
        System.out.println("Dogs's name is "+name);
    }
}

//--------------------------------------------------
// 订单接口
public interface Order {
    void create();

    void pay();

    void ship();

    void complete();
}

// 创建类
public class OrderCreator implements Order {
    @Override
    public void create() {
    // 创建订单的业务逻辑
    }
}

...其他业务类


// 订单完成类
public class OrderCompleter implements Order {
    @Override
    public void complete() {
// 完成订单的逻辑
    }
}

方法单一

如果方法中存在大片的if-else说明是不完善的代码,而我们在开发中其实应该避免如is-else或者switch等条件语句。

//违反单一原则的代码
//如果只针对价格or名字or描述进行修改,会连带修改另外两个,存在一定风险
public class Good {
    public void updateGoodInfo(String name, double price,String description) {
            //更新商品信息逻辑
    }
}


//修改后:
public class Good{
    public void updateGoodName(String Name){
            //修改名字
    }

    public void updateGoodPrice(String Name){
            //修改价格
    }
    public void updateGoodDescription(String Name){
            //修改商品描述
    }

}

接口单一

public interface IGood {
    //获取名称
    String getName();

    //获取价格
    double getPrice();

    //获取商品描述
    String getDescription();

    //购买
    void buyGood();
}

//-----------------------拆分------------------------
public interface IGoodInfo{//接口1,商品信息接口
    String getName();
    String getDescription();
    double getPrice();
}

public interface IGoodManage{
    void buyGood();//接口2,商品操作接口
    //退换等其他逻辑
}

//商品实现
publci class IGoodImpl implements IGoodInfo,IGoodManage{
    @Override
    //.....
}

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

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

相关文章

Redis实现每日签到(大数据量)

PHP语言使用Redis NoSQL服务器二进制数据类型实现大数据情况下签到功能 目录 问题 解决方式 封装签到类 功能调用 总结 问题 实现用户每日签到功能不难,但随着用户量上升之后,不论是存储还是判断对数据量来说都很麻烦;假如每天有100万用…

pytorch 与 pytorch lightning, pytorch geometric 各个版本之间的关系

主要参考 官方的给出的意见; 1. pytorch 与 pytorch lightning 各个版本之间的关系 lightning 主要可以 适配多个版本的 torch; https://lightning.ai/docs/pytorch/latest/versioning.html#compatibility-matrix; 2. pytorch 与 pytorch geometric 各…

深度学习-----------------------------束搜索

目录 贪心搜索(一般来说计算最快的)穷举搜索(一般来说是最好的,但可能算不动)束搜索(Beam Search) 总结 贪心搜索(一般来说计算最快的) 在seq2seq中使用贪心搜索来预测序…

软考数据库部分 ---- (概念数据库模型,三级模式,两级映像,事物管理)

文章目录 一、概念数据库模型二、结构数据库模型三、三级模式四、两级映像五、关系模式基本术语六、关系模式七、关系的数学定义八、数据定义语言九、SQL访问控制十、视图十一、索引十二、关系模式十三、范式十四、数据库设计十五、事物管理(ACID)十六、…

基于SPI协议的Flash扇区擦除实验

当一块Flash芯片中的不同的扇区烧录了不同的程序,而我们只想擦除某个扇区的程序保留其他程序时,Flash的全擦除是不能满足要求的,这时候就需要扇区擦除来实现这一功能。扇区擦除可以对Flash芯片中的某一扇区进行擦除而不改变其他扇区中的存储数…

2.3MyBatis——插件机制

2.3MyBatis——插件机制 1.基本用法2.原理探究2.1加载过程2.2执行过程2.2.1 插件的执行点2.2.2 SQL执行的几个阶段2.2.3 如何梳理出执行流程 插件机制是一款优秀框架不可或缺的组成部分,比如spring、dubbo,还有我们要聊的Mybatis等等。所谓插件&#xff…

【AI大模型】使用Embedding API

一、使用OpenAI API 目前GPT embedding mode有三种,性能如下所示: 模型每美元页数MTEB得分MIRACL得分text-embedding-3-large9,61554.964.6text-embedding-3-small62,50062.344.0text-embedding-ada-00212,50061.031.4 MTEB得分为embedding model分类…

Linux驱动开发(速记版)--printctl子系统

第102章 pinctrl 子系统的引入 Linux中的 pinctrl子系统是管理和配置GPIO引脚的框架,提供标准化方法以适应不同硬件。 它遵循 Linux内核设备模型,分为设备、驱动等部分。 本章节从设备和驱动角度介绍 pinctrl子系统。 102.1 pinctrl 设备树 在设备树中&a…

旅游心动盲盒:开启个性化旅行新体验

嘿,宝子们!在如今这个数字化时代呀,文心智能体可是给咱们的生活带来了超多便利和创新呢。今天呀,我来给大家介绍一款超棒的智能体——旅游心动盲盒,它肯定能给你的旅行带来全新的惊喜和超个性化的体验哟。 一、项目背…

基于H3C环境的实验——OSPF

目录 实验设备和环境 实验设备 实验环境 实验记录 1、单区域 OSPF基本配置 步骤1:搭建实验环境并完成基本配置 步骤2:检查网络连通性和路由器路由表。 步骤3:配置OSPF 步骤4:检查路由器OSPF邻居状态及路由表 实验设备和环境 实验设备 三台路由器、两台PC、电源线、两…

GO网络编程(四):海量用户通信系统2:登录功能核心【重难点】

目录 一、C/S详细通信流程图二、消息类型定义与json标签1. 消息类型定义2. JSON标签3.结构体示例及其 JSON 表示:4.完整代码与使用说明 三、客户端发送消息1. 连接到服务器2. 准备发送消息3. 创建 LoginMes 并序列化4. 将序列化后的数据嵌入消息结构5. 序列化整个 M…

java 数据存储方式

1. 变量存储 这是最基本的数据存储方式,通过声明变量来存储数据。变量可以是基本数据类型(如int、float、char等),也可以是引用数据类型(如对象、数组等)。变量存储的数据通常存储在内存中,随着…

有状态(Session) VS 无状态(Token)

目录 概念 JWT Token在项目中使用 概念 有状态和无状态服务是两种不同的服务架构,两者的不同之处在于对于服务状态的处理。 1、有状态服务 是指程序在执行过程中生成的中间数据,服务器端一般都要保存请求的相关信息,每个请求可以默认地使…

Hugging Face 任意大模型仓库劫持 - 无声的破坏

摘要 在这篇博客中,我们展示了攻击者如何攻击Hugging Face的Safetensors转换空间及其相关的服务机器人。这些服务是一个在Hugging Face上广受欢迎的服务,专门用于将不安全的机器学习模型转换为更安全的版本。我们随后展示了如何通过Hugging Face自身的服…

C0016.Clion中qDebug()打印输出中文时,都是问号??????的解决办法

问题描述 在clion中使用qDebug打印输出中文内容时,都是?????如下图: 注意:修改该文件的编码格式就行,该文件名为apr.cpp; 解决办法

矩阵求解复数(aniwoth求解串扰)

所以这种求解串扰的格式是因为,有串扰的共轭项在方程组中 复数共轭项的作用,但是这是二次方程,

vue2集成vuex实现网站统一数据管理

文章目录 前言安装配置过程1、安装vuex依赖2、在src目录下创建store文件夹,创建模块site.jsgetters.jsindex.js 3、在man.js中添加vuex vuex实战:存储与获取网站基础数据何时去存储数据?(路由前置获取数据)如何取数据&…

windows中下载、安装、配置JDK/JDK环境配置/Java配置环境变量/Linux中安装配置JDK环境

JDK下载(官网)、安装、配置(包括系统、idea、eclipse)一篇就够了 1、问题概述? Java开发者必须掌握的JDK下载、安装、配置过程。 包括在Eclipse及IDEA中的配置使用 2、下载JDK 【注册Oracle官网账号】 下载的前天是注册orcle官网账号,作为开发者,这个必须有,随时关注…

RTX4060安装nvidia显卡驱动

文章目录 nvidia drivers下载删除原有nvidia驱动安装nvidia驱动如果报错Unable to find the kernel source tree for the currently runningbuilding kernel modules解决方法 报错成功安装!!! nvidia drivers下载 https://www.nvidia.cn/geforce/drivers/#:~:textNVIDIA%20GeF…

ESP01 AT指令学习

一 、AT指令 测试指令:ATCWMODE? 参数及取值范围 cwmode(1-3) 查询指令: ATCWMODE? 当前cwmode的取值 3 设置指令: ATCWMODE3 设置当前的cwmode为 3 1、station 模式 连接到其他wifi 2、softA…