设计模式相关内容介绍—软件设计原则(六个)

news2024/12/27 15:54:40

        在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程员要尽量根据6条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。

目录

1.开闭原则

2.里氏代替原则

 3.依赖倒转原则

4.接口隔离原则

5.迪米特法则

​​​​​​​6.合成复用原则


1.开闭原则

对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。

想要达到这样的效果,我们需要使用接口和抽象类

因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。

下面以 搜狗输入法 的皮肤为例介绍开闭原则的应用。
[例] 搜狗输入法 的皮肤设计

分析:搜狗输入法 的皮肤是输入法背景图片、窗口颜色和声音等元素的组合。用户可以根据自己的喜爱更换自己的输入法的皮肤,也可以从网上下载新的皮肤。这些皮肤有共同的特点,可以为其定义一个抽象类(abstractSkin),而每个具体的皮肤(DefaultSpecificskin和emaspecificskin)是其子类用户窗体可以根据需要选择或者增加新的主题,而不需要修改原代码,所以它是满足开闭原则的

package design_patterns;

public abstract class AbstractSkin {
    public abstract void display();
}
package design_patterns;

public class DefaultSkin extends AbstractSkin{
    @Override
    public void display() {
        System.out.println("默认皮肤");
    }
}
package design_patterns;

public class HeimaSkin extends AbstractSkin{
    @Override
    public void display() {
        System.out.println("黑马皮肤");
    }
}
package design_patterns;

public class SougouInput {
    private AbstractSkin skin;

    public void setSkin(AbstractSkin skin) {
        this.skin = skin;
    }
    public void display(){
        skin.display();
    }
}
package design_patterns;

public class Demo {
    public static void main(String[] args) {
        //1.创建搜狗输入法对象
        SougouInput input=new SougouInput();
        //2.创建皮肤对象
        DefaultSkin skin=new DefaultSkin();
        //3.将皮肤设置到输入法中
        input.setSkin(skin);
        //4.显示皮肤
        input.display();
    }
}

2.里氏代替原则

任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。

下面看一个里氏替换原则中经典的一个例子

【例】正方形不是长方形。

在数学领域里,正方形毫无疑问是长方形,它是一个长宽相等的长方形。所以,我们开发的一个与几何图形相关的软件系统,就可以顺理成章的让正方形继承自长方形。

 

public class Rectangle {
    private double length;
    private double width;

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }
}
public class Square extends Rectangle {
    
    public void setWidth(double width) {
        super.setLength(width);
        super.setWidth(width);
    }

    public void setLength(double length) {
        super.setLength(length);
        super.setWidth(length);
    }
}

类RectangleDemo是我们的软件系统中的一个组件,它有一个resize方法依赖基类Rectangle,resize方法是RectandleDemo类中的一个方法,用来实现宽度逐渐增长的效果。

public class RectangleDemo {
    
    public static void resize(Rectangle rectangle) {
        while (rectangle.getWidth() <= rectangle.getLength()) {
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }

    //打印长方形的长和宽
    public static void printLengthAndWidth(Rectangle rectangle) {
        System.out.println(rectangle.getLength());
        System.out.println(rectangle.getWidth());
    }

    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.setLength(20);
        rectangle.setWidth(10);
        resize(rectangle);
        printLengthAndWidth(rectangle);

        System.out.println("============");

        Rectangle rectangle1 = new Square();
        rectangle1.setLength(10);
        resize(rectangle1);
        printLengthAndWidth(rectangle1);
    }
}

运行一下这段代码就会发现,假如我们把一个普通长方形作为参数传入resize方法,就会看到长方形宽度逐渐增长的效果,当宽度大于长度,代码就会停止,这种行为的结果符合我们的预期;假如我们再把一个正方形作为参数传入resize方法后,就会看到正方形的宽度和长度都在不断增长,代码会一直运行下去,直至系统产生溢出错误。所以,普通的长方形是适合这段代码的,正方形不适合。 我们得出结论:在resize方法中,Rectangle类型的参数是不能被Square类型的参数所代替,如果进行了替换就得不到预期结果。因此,Square类和Rectangle类之间的继承关系违反了里氏代换原则,它们之间的继承关系不成立,正方形不是长方形。

如何改进呢?此时我们需要重新设计他们之间的关系。抽象出来一个四边形接口(Quadrilateral),让Rectangle类和Square类实现Quadrilateral接口

 经过修改后我们发现正方形Square和长方形Rectangle同时实现抽象类Quadrilateral,属于同级关系而非继承关系,测试类中的resize()函数的参数为长方形Rectangle,所以此时resize()函数不能够使用正方形Square作为参数,这样就很好的解决了上面违背里氏代替原则的问题

 3.依赖倒转原则

高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。

下面看一个例子来理解依赖倒转原则

【例】组装电脑

现要组装一台电脑,需要配件CPU,硬盘,内存条。只有这些配置都有了,计算机才能正常的运行。选择cpu有很多选择,如Intel,AMD等,硬盘可以选择希捷,西数等,内存条可以选择金士顿,海盗船等。

 

public class XiJieHardDisk implements HardDisk {

    public void save(String data) {
        System.out.println("使用希捷硬盘存储数据" + data);
    }

    public String get() {
        System.out.println("使用希捷希捷硬盘取数据");
        return "数据";
    }
}
public class IntelCpu implements Cpu {

    public void run() {
        System.out.println("使用Intel处理器");
    }
}
public class KingstonMemory implements Memory {

    public void save() {
        System.out.println("使用金士顿作为内存条");
    }
}
public class Computer {

    private XiJieHardDisk hardDisk;
    private IntelCpu cpu;
    private KingstonMemory memory;

    public IntelCpu getCpu() {
        return cpu;
    }

    public void setCpu(IntelCpu cpu) {
        this.cpu = cpu;
    }

    public KingstonMemory getMemory() {
        return memory;
    }

    public void setMemory(KingstonMemory memory) {
        this.memory = memory;
    }

    public XiJieHardDisk getHardDisk() {
        return hardDisk;
    }

    public void setHardDisk(XiJieHardDisk hardDisk) {
        this.hardDisk = hardDisk;
    }

    public void run() {
        System.out.println("计算机工作");
        cpu.run();
        memory.save();
        String data = hardDisk.get();
        System.out.println("从硬盘中获取的数据为:" + data);
    }
}
public class TestComputer {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.setHardDisk(new XiJieHardDisk());
        computer.setCpu(new IntelCpu());
        computer.setMemory(new KingstonMemory());

        computer.run();
    }
}

可以看到已经组装了一台电脑,但是似乎组装的电脑的cpu只能是Intel的,内存条只能是金士顿的,硬盘只能是希捷的,这对用户肯定是不友好的,用户有了机箱肯定是想按照自己的喜好,选择自己喜欢的配件。

根据依赖倒转原则进行改进:

代码我们只需要修改Computer类,让Computer类依赖抽象(各个配件的接口),而不是依赖于各个组件具体的实现类。

 对每个组件进行向上抽取,让Computer类依赖抽取出的抽象(各个配件的接口),这样就解决了上述问题。

4.接口隔离原则

客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上。

下面看一个例子来理解接口隔离原则

【例】安全门案例

我们需要创建一个黑马品牌的安全门,该安全门具有防火、防水、防盗的功能。可以将防火,防水,防盗功能提取成一个接口,形成一套规范。

 它存在的问题,黑马品牌的安全门具有防盗,防水,防火的功能。现在如果我们还需要再创建一个传智品牌的安全门,而该安全门只具有防盗、防水功能呢?很显然如果实现SafetyDoor接口就违背了接口隔离原则,那么我们如何进行修改呢?

 

5.迪米特法则

迪米特法则又叫最少知识原则。

只和你的直接朋友交谈,不跟“陌生人”说话(只和你的直接朋友交谈,不要和陌生人说话)。

其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

迪米特法则中的"朋友"是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。

下面看一个例子来理解迪米特法则

【例】明星与经纪人的关系实例

明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如和粉丝的见面会,和媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则。

 

public class Star {
    private String name;

    public Star(String name) {
        this.name=name;
    }

    public String getName() {
        return name;
    }
}
public class Fans {
    private String name;

    public Fans(String name) {
        this.name=name;
    }

    public String getName() {
        return name;
    }
}
public class Company {
    private String name;

    public Company(String name) {
        this.name=name;
    }

    public String getName() {
        return name;
    }
}
public class Agent {
    private Star star;
    private Fans fans;
    private Company company;

    public void setStar(Star star) {
        this.star = star;
    }

    public void setFans(Fans fans) {
        this.fans = fans;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

    public void meeting() {
        System.out.println(fans.getName() + "与明星" + star.getName() + "见面了。");
    }

    public void business() {
        System.out.println(company.getName() + "与明星" + star.getName() + "洽淡业务。");
    }
}

​​​​​​​6.合成复用原则

合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

通常类的复用分为继承复用和合成复用两种。

继承复用虽然有简单和易实现的优点,但它也存在以下缺点:

  1. 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为"白箱"复用。
  2. 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
  3. 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点:

  1. 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为"黑箱"复用。
  2. 对象间的耦合度低。可以在类的成员位置声明抽象。
  3. 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

【例】汽车分类管理程序

汽车按"动力源"划分可分为汽油汽车、电动汽车等;按"颜色"划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。

 从上面类图我们可以看到使用继承复用产生了很多子类,如果现在又有新的动力源或者新的颜色的话,就需要再定义新的类。我们试着将继承复用改为聚合复用看一下。

 ​​​​​​​

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

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

相关文章

dvwa中的文件包含攻击

环境&#xff1a;dvwa: 192.168.11.135 dvwa版本&#xff1a; Version 1.9 (Release date: 2015-09-19)kail机器&#xff1a;192.168.11.156一、什么是文件包含漏洞?为简化代码&#xff0c;会把重复的code内容单独写到一个页面文件&#xff0c;然后再需要调用重复内容的页面中…

C语言:初识C语言

目录前言1. 什么是c语言呢2. 第一个c语言程序2. 数据类型3. 变量和常量3.1 变量3.1.1 变量的定义3.1.2 变量的分类3.1.3 变量的使用3.1.4 变量的作用域和生命周期3.2 常量4. 字符串、转义字符、注释4.1 字符串4.2 转义字符4.3 注释5. 选择语句6. 循环语句7. 函数8. 数组9. 操作…

学习笔记——keep-alive缓存组件,再次返回组件data数据重置

前言&#xff1a;使用keep-alive缓存组件&#xff0c;当再次返回该组件后&#xff0c;希望其组件中的数据或状态&#xff0c;保持上次离开该组件时的情况。 一、当前组件树 希望缓存HomeMain组件的状态。 二、错误处理 我在HomeMain的祖先组件HomeLayout中&#xff0c;写了如下…

sqlplus 连接数据库

终端直连 Oracle 数据库 ORA-12162 错误 出于各种网络原因&#xff0c;无法直连数据库&#xff0c;但又必须查询数据库数据 我们只能选择直连数据库的服务器 然后通过 sqlplus 连接 Oracle 从配置文件里获取这样一段信息 urljdbc:oracle:thin:192.168.1.3:1521:testdb use…

【SpringCloud08】SpringCloud Consul服务注册与发现

1.Consul简介 1.1是什么 官网 Consul 是一套开源的分布式服务发现和配置管理系统&#xff0c;由 HashiCorp 公司用Go 语言开发 提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用&#xff0c;也可以一起使用以构建全方位…

基于MPLS-V**多分部互访的ensp企业网络规划与设计_ensp综合实验

作者&#xff1a;BSXY_19计科_陈永跃BSXY_信息学院注&#xff1a;未经允许禁止转发任何内容基于MPLS-V**多分部互访的ensp企业网络规划与设计_ensp综合实验前言及技术/资源下载说明&#xff08; **未经允许禁止转发任何内容** &#xff09;插曲&#xff1a;基于eNSP中大型校园/…

卡特加特数字中控主机,数字家庭控制中心!没它智能家居就是智障!

数字中控主机是数字家庭的核心&#xff0c;承担着“协调各方、总揽全局”的作用&#xff0c;是打造未来数字家庭空间必不可少的设备。区别于传统家居智能&#xff0c;它真正意义上告别了过去以设备为中心的架构&#xff0c;而是以人的个性化需求为中心&#xff0c;以数据作为资…

2020统考真题-距离最小三元组

2020年统考真题 定义三元组$ (a,b,c)$ &#xff08; a,b,c 均为正数&#xff09;的距离 D∣a−b∣∣b−c∣∣c−a∣D|a−b||b−c||c−a|D∣a−b∣∣b−c∣∣c−a∣ 。给定 3个非空整数集合 S1 、 S2 和 S3 &#xff0c;按升序分别存储在 3 个数组中。请设计一个尽可能高效的算…

蓝队攻击的四个阶段(三)

目录 一&#xff0c; 专业技能储备 1.工具开发技能 2.漏洞挖掘技能 3.代码调试技能 4.侦破拓展技能 二&#xff0c;目标网情搜集 1 何为网情搜集 2. 网情搜集的主要工作 三&#xff0c; 网情搜集的途径 1.专业网站 2.专业开发资源网站 3.目标官网 一&#xff0c; 专…

算法训练营 day17 二叉树 平衡二叉树 二叉树的所以路径 左叶子之和

算法训练营 day17 二叉树 平衡二叉树 二叉树的所以路径 左叶子之和 平衡二叉树 110. 平衡二叉树 - 力扣&#xff08;LeetCode&#xff09; 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a; 一个二叉树每…

osg fbo(五) 通过shader计算纹理坐标并采样

在前边几节&#xff0c;纹理坐标数组是在生成geometry前指定的。 这里在shader里计算&#xff0c; 一&#xff0c;注释掉geometry中的纹理坐标 //osg::ref_ptr<osg::Vec2Array> texCoord new osg::Vec2Array; //texCoord->push_back(osg::Vec2(0.0, 0.0)); //texCoo…

1.13UART串口实验

UART总线&#xff1a; 异步全双工串行总线。用于芯片与外设之间的通信 UART通信协议&#xff1a; 空闲状态处于高电平 起始位&#xff1a;串口通信的起始标志 数据位&#xff1a;传输数据时先从低位开始传输&#xff0c;再传输高位 奇偶校验位&#xff1a; 奇校验&#x…

MacOS 系统中如何使用EF Core进行数据迁移?

原有 笔者最近在使用MacOS系统做asp.net core mvc项目开发。可是一直习惯了使用宇宙最强大的vs2019工具。突然换到苹果系统就感觉什么都是问题了。现在我将遇到该问题的解决方案做下记录&#xff0c;方便自己也是方便别的同学快速的解决问题。 发现有坑 其实最开始我也是翻阅…

RocketMQ 存储优化技术 解析——图解、源码级解析

&#x1f34a; Java学习&#xff1a;Java从入门到精通总结 &#x1f34a; 深入浅出RocketMQ设计思想&#xff1a;深入浅出RocketMQ设计思想 &#x1f34a; 绝对不一样的职场干货&#xff1a;大厂最佳实践经验指南 &#x1f4c6; 最近更新&#xff1a;2023年1月13日 &#x…

Django 后端没有接收到前端anxios的 post 内容

前端使用 vue 无论怎样 post 后端都说没有接收到值&#xff0c;&#xff08;后端接口正确&#xff09; 寻找原因&#xff1a; 1、前端查看自己的请求类型 Content-Type:application/json 我们的请求是这样的&#xff1a; axios({method:post,url:/video/upload,data:{"…

RedHat6配置本地yum源(最新超详细过程)

一、环境准备 挂载iso的镜像文件在CD/DVD驱动器上&#xff0c;需要确保&#xff0c;已连接已打开&#xff0c;且CD/DVD上的介质符合当前操作系统的版本。 挂载本地光盘到系统 在“编辑设置”——>“硬件”——>“CD/DVD驱动器”里指定操作系统的ISO镜像文件 光驱挂载…

web性能测试:Lighthouse测试实践

一工具简介 Lighthouse是Google开源的一个自动化工具&#xff0c;它可以搜集多个Web网页性能指标&#xff0c;分析Web应用的性能并生成报告&#xff0c;为开发人员进行性能优化提供了参考方向。1工作原理•Driver&#xff08;驱动&#xff09;—— 通过 Chrome Debugging Proto…

力扣sql基础篇(六)

力扣sql基础篇(六) 1 学生参加各科测试的次数 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 a 输入示例 b 输出示例 1.2 示例sql语句 # 无论考没考试都要该科目这栏且无连接字段,就可以考虑笛卡尔积了 SELECT s.student_id,s.student_name,s.subject_name,IFNULL…

【软件STM32cubeIDE下STM32F4xx使用DMA+定时器推PWM+灯带WS2812-进阶-综合汇总(讲解移植相关)】

2TOC &#xff08;1&#xff09;前言 做灯带ws2812其实有一段时间了&#xff0c;中间遇到很多问题&#xff0c;从开始的学习&#xff0c;到后来慢慢熟悉&#xff0c;再到后来尝试点很多灯带&#xff0c;做过非常多的实验了&#xff0c;自己新建工程&#xff0c;几乎尝试过很多…

【Git】GitHub 操作

6、GitHub 操作 GitHub 网址&#xff1a;https://github.com/ Ps:全球最大同性交友网站&#xff0c;技术宅男的天堂&#xff0c;新世界的大门&#xff0c;你还在等什么? 账号姓名验证邮箱atguiguyueyue岳不群atguiguyueyuealiyun.comatguigulinghuchong令狐冲atguigulinghu…