设计模式的七大原则

news2024/9/16 23:22:34

1.单一职责原则

单一职责原则(Single responsibility principle),即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1、A2。所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。

单一职责原则注意事项和细节

  1. 降低类的复杂度,一个类只负责一项职责。
  2. 提高类的可读性,可维护性
  3. 降低变更引起的风险
  4. 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则

2.接口隔离原则

​接口隔离原则(Interface Segregation Principle, ISP)是面向对象设计的五大原则之一,由Robert C. Martin在《Agile Software Development, Principles, Patterns, and Practices》一书中提出。这一原则强调了在设计软件时,应当尽量保持接口的小而专一,以避免客户端依赖它不需要的方法。

具体来说,接口隔离原则提倡以下几点:

  1. 模块化设计:每个接口应该专注于提供一组相关功能,而不是试图满足所有客户端的需要。这样可以确保接口的高内聚性,每个接口都只负责一个功能模块。

  2. 客户端定制接口:为不同的客户端或用途定义不同的接口,而不是强迫它们去依赖并实现那些它们实际上并不使用的接口方法。这样可以减少不必要的耦合,提高系统的灵活性和可维护性。

  3. 避免胖接口:胖接口是指包含了大量方法的接口,这往往意味着使用该接口的类需要实现很多它实际上并不关心的方法。ISP建议将这样的大接口拆分为更小、更具体的接口,使得每个类仅需实现它真正需要的方法。

遵循接口隔离原则的好处包括:

  • 提高代码的可读性和可维护性。
  • 减少代码间的耦合度,易于修改和扩展。
  • 提升系统的灵活性,便于单元测试。
  • 促进设计的清晰和职责的单一。

下面我将以Java语言为例,简单展示如何应用接口隔离原则。

假设我们有一个系统需要处理不同类型的动物,一开始可能会设计一个包含多种行为的胖接口

// 胖接口示例
interface Animal {
    void eat();
    void fly();
    void swim();
}

然后,有一个Duck类实现了这个接口:

class Duck implements Animal {
    @Override
    public void eat() {
        System.out.println("Duck is eating.");
    }

    @Override
    public void fly() {
        System.out.println("Duck is flying.");
    }

    @Override
    public void swim() {
        System.out.println("Duck is swimming.");
    }
}

但问题在于,并非所有动物都能飞和游泳,比如鸟可能不能游泳,鱼不能飞。这导致了不必要的方法实现,如对于一个只能游泳的鱼来说,实现fly()方法是没有意义的。

按照接口隔离原则改进,我们可以将接口拆分为更具体的几个:

interface Eater {
    void eat();
}

interface Flyer {
    void fly();
}

interface Swimmer {
    void swim();
}

现在,每个类只实现它需要的接口: 

class Duck implements Eater, Flyer, Swimmer {
    // 实现相应的方法...
}

class Fish implements Eater, Swimmer {
    // 实现相应的方法,无需实现fly()
}

这样,Fish类就不需要实现fly()方法,因为它不符合鱼的行为特征,这便是接口隔离原则的应用实例,它让我们的设计更加合理和灵活。 

3.依赖倒转原则

面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本。

面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。

 4.里氏替换原则

在软件中,子类型(子类)必须能够替换其基类型(父类),并且在软件中使用基类型的地方,都能够使用子类型而不引发任何错误或导致程序的行为异常。这意味着子类应当能够保持父类的行为约定,不会破坏或违背父类的预期用途。

具体来说,里氏替换原则强调以下几个关键点:

  1. 行为一致性:子类应当能够替换父类,且不会影响到程序的正确性。即,使用父类的地方可以用子类替代,而不会引起任何错误或意外行为。

  2. 合约不变:子类应当遵守父类定义的接口契约(即方法的前置条件和后置条件不能比父类更严格)。例如,如果父类的一个方法承诺在某种输入下一定会返回一个正数,那么子类的相同方法也必须满足这一承诺。

  3. 增加功能但不改变原有行为:子类可以添加新的方法或属性,但不得去除或修改父类已有的方法(除非是通过重写使行为更为一般化,但仍需满足上述的合约不变原则)。

遵循里氏替换原则的好处包括:

  • 增强代码的稳定性:确保现有代码在新子类加入时仍能正常工作。
  • 提高代码的可预测性:开发者可以依赖基类的行为,而不用担心子类的具体实现细节。
  • 促进良好的设计:鼓励设计者在设计类的继承结构时更加谨慎,考虑接口和实现的一致性。

违反里氏替换原则可能导致的问题:

  • 子类的行为与父类不一致,可能在运行时引发错误。
  • 系统变得更加脆弱,难以维护和扩展,因为子类的修改可能会影响到依赖父类的其他部分。

基类 Shape.java

public abstract class Shape {
    public abstract double area();
}

子类 Rectangle.java

public class Rectangle extends Shape {
    private double width;
    private double height;

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

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

使用这些类的 Main.java

public class Main {
   public static void main(String[] args) {
       Shape shape = new Rectangle(10, 5); // 这里展示了里氏替换原则的应用
       System.out.println("Area: " + shape.area()); // 输出矩形的面积
   }
}

在这个Java示例中,Rectangle类继承了抽象类Shape并实现了area()方法。在Main类中,我们创建了一个Rectangle对象,并将其向上转型为Shape类型的引用。这证明了子类对象可以替换父类对象,同时程序逻辑保持正确,体现了里氏替换原则的精神。通过将Rectangle实例赋给Shape类型的变量,我们展示了子类对父类的透明替换能力,而无需修改使用Shape的现有代码。
父类可以不是抽象类吗?

父类不一定是抽象类。里氏替换原则并没有规定父类必须是抽象类。它可以是一个具体的类,只要子类遵循其规则即可。实际上,里氏替换原则适用于所有类型的继承关系,不论是抽象类还是具体类作为基类。

这里有一个非抽象父类的例子:

父类 Vehicle.java

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

子类 Car.java

public class Car extends Vehicle {
    @Override
    public void start() {
        System.out.println("Car started with ignition.");
    }
}

在这个例子中,Vehicle是一个具体类,它有一个start()方法。Car类继承了Vehicle类并重写了start()方法,提供了更具体的实现。只要Car类中的start()方法不违反Vehicle类中该方法的预期行为(比如,没有改变车辆启动的基本含义或引入不可接受的行为变更,即车开起来细化一下,可以是汽车,公交车,卡车开起来,但都比车这个范围更小更细化),这就遵循了里氏替换原则。即使Vehicle类不是抽象的,我们依然可以将Car实例安全地用在任何期待Vehicle类型的地方。

5.开闭原则 

开闭原则(Open Closed Principle)一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。也就是当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。用抽象构建框架,用实现扩展细节。

开始 Display直接修改成BarChart

  • Display:用于显示各种图表。
  • BarChart:一种特定类型的图表,表示条形图。

重构此设计以符合开闭原则。开闭原则要求软件实体(模块、类或方法)应该对扩展开放,对修改关闭。这意味着我们应该能够轻松添加新功能(如新的图表类型)而不必更改现有的代码。

重构后的类结构图:

  • Display:负责显示图表。
  • AbstractChart:一个新的抽象类,定义了显示图表的方法。
  • BarChart:继承自AbstractChart,表示条形图。
  • LineChart:同样继承自AbstractChart,表示折线图。
// 抽象类,定义了显示图表的方法
abstract class AbstractChart {
    abstract void display();
}

// 条形图类
class BarChart extends AbstractChart {
    @Override
    void display() {
        System.out.println("Displaying bar chart");
    }
}

// 折线图类
class LineChart extends AbstractChart {
    @Override
    void display() {
        System.out.println("Displaying line chart");
    }
}

// 显示器类,负责显示图表
class Display {
    void display(AbstractChart chart) {
        chart.display();
    }

现在,如果需要添加新的图表类型(例如饼状图),只需创建一个新的类(如PieChart),让它继承自AbstractChart,并实现display()方法。然后,在Display类中,您不需要更改任何现有代码就可以处理新的图表类型。这就是开闭原则的一个简单应用。

6.迪米特法则

迪米特法则,也被称为最少知识原则(Least Knowledge Principle, LKP),是一种面向对象设计中的指导思想,它的核心理念非常直白:“不要和陌生人说话”。这句话听起来像是一句社交建议,但在编程世界里,它帮助我们构建低耦合、高内聚的软件系统。

想象一下,在一个公司里,员工们专注于自己的工作,他们通常只会直接和紧密相关的同事沟通,比如他们的直接上司、团队成员或是直接对接的其他部门同事。他们不会也不应该去干涉或者直接联系公司里每一个不认识的人。这样做的好处是,每个人的工作职责清晰,减少了不必要的干扰,提高了工作效率。

在编程中,迪米特法则应用这个思路,要求一个对象应该尽量减少与其他对象的直接交互,特别是避免和那些它不直接依赖的对象交流。具体来说,一个对象应该只和它的直接朋友通信:

  • 直接的朋友包括:对象本身、对象的成员变量(属性)、传递给对象的方法参数,以及由该方法创建或返回的对象。

遵循迪米特法则,我们可以这样做:

  1. 限制访问范围:尽量使用private或protected修饰符限制类成员的访问权限,只暴露必要的接口给外部。
  2. 减少依赖:通过接口而非具体类编程,减少类之间的直接耦合。
  3. 使用中介者:当需要跨多个层级访问数据时,可以引入一个中介对象来协调,避免高层级的对象直接操作低层级的对象。

7.合成复用原则

合成复用原则(Composite Reuse Principle)就是是尽量使用合成/聚合的方式,而不是使用继承。合成复用原则则更像是使用标准化的建筑模块来组装房屋。每个模块,比如卧室模块、厨房模块、浴室模块,都是独立设计和制造的。当你需要建造一座新房子时,不是从零开始或者修改旧设计,而是选择需要的模块进行组合。这样,即使未来想升级卧室设计或者替换浴室设施,只需替换相应的模块即可,不影响整体房屋结构的稳定性和其他部分的正常使用。

放到编程领域,这意味着我们应当尽量使用对象组合(即将对象作为其他对象的属性或通过集合管理)来实现新功能,而不是通过继承来扩展类。这样做能够使得系统更加灵活,易于维护和扩展,因为修改或添加功能时,不必触及或改动现有的类结构,只需调整或增加新的组件(对象)即可,保证了各个部分的独立性和复用性。就像是高效又灵活的建筑工地,随时可以根据需求调整设计方案,而不会造成大规模的返工。

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

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

相关文章

2-32 基于matlab的最小二乘估计递推算法

基于matlab的最小二乘估计递推算法,生成M序列,对参数估计值进行辨识,输出估计误差结果。程序已调通,可直接运行。 2-32 最小二乘估计递推算法 参数估计 - 小红书 (xiaohongshu.com)

C# Winform 系统方案目录的管理开发

在做一个中等复杂程度项目时,我们通常有系统全局配置,还要有对应的方案目录的管理和更新。 比如我们有如下需求:开发一个方案管理,可以新建、打开和保存方案,同时还需要保存方案中的各种文件。我设计的采用目录管理和…

计算器原生js

目录 1.HTML 2.CSS 2.JS 4.资源 5.运行截图 6.下载连接 7.注意事项 1.HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-s…

IDEA 中的调试方式(以 java 为例)

文章目录 IDEA 中的调试方式(以 java 为例)1. 基本介绍2. 断点调试的快捷键2.1 设置断点并启动调试2.3 快捷键 IDEA 中的调试方式(以 java 为例) 在开发中查找错误的时候&#xff0c;我们可以用断点调试&#xff0c;一步一步的看源码执行的过程&#xff0c;从而发现错误所在。 …

WEB前端01-HTML5基础(01)

一.WEB相关概念 软件架构 C/S: Client/Server &#xff08;客户端/服务器端&#xff09;&#xff1a;在用户本地有一个客户端程序&#xff0c;在远程有一个服务器端程序 优点&#xff1a;用户体验好 缺点&#xff1a;开发、安装&#xff0c;部署&#xff0c;维护麻烦 B/S: Br…

【银河麒麟服务器操作系统】系统夯死分析及处理建议

了解银河麒麟操作系统更多全新产品&#xff0c;请点击访问麒麟软件产品专区&#xff1a;https://product.kylinos.cn 服务器环境以及配置 【机型】物理机 处理器&#xff1a; Intel 内存&#xff1a; 512G 整机类型/架构&#xff1a; X86_64 【内核版本】 4.19.90-25…

IDEA的JAVA版本没有8怎么办

问题&#xff1a; 很多小伙伴会出现如下的情况&#xff0c;java的版本很高&#xff0c;没有8 解决 更换IDEA内置的Server URL的镜像地址 就是这个 把其中的地址换成 https://start.aliyun.com/ https://start.aliyun.com/ 我们可以看到JAVA 8就出现了

Mysql的语句执行很慢,如何分析排查?

1、检查服务器性能是否存在瓶颈 如果系统资源使用率比较高&#xff0c;比如CPU,硬盘&#xff0c;那访问肯定会慢&#xff0c;如果你发现是Mysl占比比较高&#xff0c;说明Mysql的读写频率高&#xff0c;如果本身网站访问量不大&#xff0c;说明你的sql参数&#xff0c;sql语句查…

气膜建筑的消防应急门:安全与保障—轻空间

气膜建筑&#xff0c;作为一种现代化的建筑形式&#xff0c;以其独特的结构和多功能用途受到广泛欢迎。然而&#xff0c;消防安全作为任何建筑的核心问题&#xff0c;尤其受到关注。为了确保在紧急情况下的安全疏散&#xff0c;气膜建筑在设计和建设过程中&#xff0c;特别重视…

网络安全高级工具软件100套

1、 Nessus&#xff1a;最好的UNIX漏洞扫描工具 Nessus 是最好的免费网络漏洞扫描器&#xff0c;它可以运行于几乎所有的UNIX平台之上。它不止永久升级&#xff0c;还免费提供多达11000种插件&#xff08;但需要注册并接受EULA-acceptance–终端用户授权协议&#xff09;。 它…

LabVIEW阀门运动PCT测试

开发了一套基于LabVIEW的阀门运动PCT&#xff08;Pressure-Composition-Temperature&#xff09;测试方法。该系统通过控制阀门运动&#xff0c;实现对氢气吸附和解吸过程的精确测量和控制。所用硬件包括NI cDAQ-9174数据采集模块、Omega PX309压力传感器、SMC ITV2030电动调节…

Intel 和 ARM 对ROP/COP/JOP的缓解措施

文章目录 前言一、ROP1.1 Intel1.2 ARM 二、COP/JOP2.1 Intel2.2 ARM 前言 前向转移(forward)&#xff1a;将控制权定向到程序中一个新位置的转移方式, 就叫做前向转移, 比如jmp和call指令。这里我们主要保护的间接跳转&#xff0c;间接跳转是运行时才知道函数地址&#xff0c…

虚幻引擎ue5如何调节物体锚点

当发现锚点不在物体上时&#xff0c;如何调节瞄点在物体上。 步骤1&#xff1a;按住鼠标中键拖动锚点&#xff0c;在透视图中多次调节锚点位置。 步骤2:在物体上点击鼠标右键点击-》锚定--》“设置为枢轴偏移”即可。

百日筑基第十九天-一头扎进消息队列2

百日筑基第十九天-一头扎进消息队列2 消息队列的通讯协议 目前业界的通信协议可以分为公有协议和私有协议两种。公有协议指公开的受到认可的具有规 范的协议&#xff0c;比如 JMS、HTTP、STOMP 等。私有协议是指根据自身的功能和需求设计的协 议&#xff0c;一般不具备通用性&…

AI网络爬虫023:用deepseek批量提取天工AI的智能体数据

文章目录 一、介绍二、输入内容三、输出内容一、介绍 天工AI的智能体首页: F12查看真实网址和响应数据: 翻页规律: https://work.tiangong.cn/agents_api/square/sq_list_by_category?category_id=7&offset=0 https://work.tiangong.cn/agents_api/square/sq_list_b…

MUR2060CTR-ASEMI无人机专用MUR2060CTR

编辑&#xff1a;ll MUR2060CTR-ASEMI无人机专用MUR2060CTR 型号&#xff1a;MUR2060CTR 品牌&#xff1a;ASEMI 封装&#xff1a;TO-220 批号&#xff1a;最新 最大平均正向电流&#xff08;IF&#xff09;&#xff1a;20A 最大循环峰值反向电压&#xff08;VRRM&#…

基于R语言的水文、水环境模型优化技术及快速率定方法与多模型案例

在水利、环境、生态、机械以及航天等领域中&#xff0c;数学模型已经成为一种常用的技术手段。同时&#xff0c;为了提高模型的性能&#xff0c;减小模型误用带来的风险&#xff1b;模型的优化技术也被广泛用于模型的使用过程。模型参数的快速优化技术不但涉及到优化本身而且涉…

Python 的 metaclass

文章目录 先说结论1. metaclass 的作用2. 主要的执行过程 1. metaclass.__new__2. metaclass.__call__关于 metaclass.__init__ 3. metaclass.__prepare__4. 自动创建 __slots__ 属性4.1 metaclass 的接口类4.2 metaclass conflict 5. Class metaprogramming 先说结论 1. meta…

【linux】服务器安装及卸载pycharm社区版教程

【linux】服务器安装及卸载pycharm社区版教程 【创作不易&#xff0c;求点赞关注收藏】 文章目录 【linux】服务器安装及卸载pycharm社区版教程1、到官网下载安装包2、通过终端wget下载安装包3、解压4、安装5、设置环境变量6、运行pycharm7、删除pycharm安装包、卸载pycharm …

Arcgis横向图例设置

想把这个图例改成横向的 点击图例的属性&#xff0c;找到样式