Java中的七种设计原则

1.开闭原则

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

下面是输入法设置皮肤的例子:

// 抽象皮肤接口
public interface Skin {
    // 显示的方法
    void display();
}
// 默认皮肤类
public class DefaultSkin implements Skin {
    @Override
    public void display() {
        System.out.println("默认皮肤");
    }
}
// 下班了皮肤
public class OffWorkSkin implements Skin{
    @Override
    public void display() {
        System.out.println("下班了皮肤");
    }
}
// 输入法类
@Data
public class Input {
    // 设置皮肤
    private Skin skin;
    public void display() {
        skin.display();
    }
}
    // 测试
    public static void main(String[] args){
        // 创建输入法对象
        Input input = new Input();
        // 创建皮肤对象
        Skin defaultSkin = new DefaultSkin();
        // 设置默认皮肤
        input.setSkin(defaultSkin);
        // 显示皮肤
        input.display(); // 默认皮肤
        // 换成下班了皮肤
        Skin offWorkSkin = new OffWorkSkin();
        input.setSkin(offWorkSkin);
        input.display();// 下班了皮肤
    }

2.单一职责原则

就一个类而言,应该仅有一个引起变化的原因。应该只有一个职责。

说白了就是,一个类或者一个方法就应该干一件事情。

下面是修改用户的例子:

public class UpdateUser {
    // 不符合单一职责原则
    public void updateUser(String type, String oldPassword, String newPassword, String oldUserName, String newUserName) {
        if ("修改密码".equals(type)) {
            System.out.println(oldPassword + "修改密码为:" + newPassword);
        } else if ("修改账号".equals(type)) {
            System.out.println(oldUserName + "修改账号" + newUserName);
        }
    }
    // 符合单一职责原则
    public void updatePassword(String oldPassword, String newPassword) {
        System.out.println(oldPassword + "修改密码为:" + newPassword);
    }
    public void updateUserName(String oldUserName, String newUserName) {
        System.out.println(oldUserName + "修改账号" + newUserName);
    }
}

可以看到上者是根据操作类型进行区分, 不同类型执行不同的逻辑,把修改账号和修改密码这两件事耦合在一起了,如果客户端在操作的时候传错了类型, 那么就会发生错误;

下者则把修改账号和修改密码逻辑分开,各自执行各自的职责,互不干扰,功能清晰明了,符合单一职责原则。

3.依赖倒置原则

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

下面是一个组装电脑的例子(简单举例电脑只保留CPU吧,嘻嘻嘻):

// Intel处理器类
public class IntelCpu {
    public void getCpu() {
        System.out.println("使用Intel处理器");
    }
}
// 电脑
@Data
public class Computer {
    private IntelCpu intelCpu; // CPU
    public void run() {
        intelCpu.getCpu();
    }
}
    // 测试
    public static void main(String[] args){
        // 创建电脑对象
        Computer computer = new Computer();
        // 设置CPU
        computer.setIntelCpu(new IntelCpu());
        computer.run(); // 使用Intel处理器
    }

可以看到上面的组装的电脑只能使用Intel的CPU,假如我想换成AMD的CPU不行。这就是违反了依赖倒置原则。修改后的代码如下:

// CPU类
public interface Cpu {
    void getCpu();
}
// Intel处理器类
public class IntelCpu implements Cpu {
    @Override
    public void getCpu() {
        System.out.println("使用Intel处理器");
    }
}
// Amd处理器类
public class AmdCpu implements Cpu {
    @Override
    public void getCpu() {
        System.out.println("使用Amd处理器");
    }
}
// 电脑
@Data
public class Computer {
    private Cpu cpu;
    public void run() {
        cpu.getCpu();
    }
}
    // 测试
    public static void main(String[] args){
        // 创建电脑对象
        Computer computer = new Computer();
        // 设置CPU为Intel处理器
        computer.setCpu(new IntelCpu());
        computer.run(); // 使用Intel处理器
        // 设置CPU为Amd处理器
        computer.setCpu(new AmdCpu());
        computer.run(); // 使用Amd处理器
    }

此时,当用户需要更换CPU时,只需要创建实现类去实现CPU接口而不需要修改原本的接口代码,这就符合了开闭原则。

4.接口隔离原则

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

下面用一个安全门的例子:

现在有A品牌的安全门具有防火防盗功能,于是把防火防盗的功能提取出来一个接口,即:

// 安全门接口
public interface Door {
    //防火
    void fireproof();
    //防水
    void waterproof();
}
// 安全门接口
public interface Door {
    //防火
    void fireproof();
    //防水
    void waterproof();
}

那么假如我们现在又有一个B品牌的安全门,只有防水功能的呢?那么显然是不能直接实现安全门的接口的,那么应该如何改进呢?改进如下:

// 防火功能
public interface FireProof {
    void fireProof();
}
// 防火功能
public interface FireProof {
    void fireProof();
}
// A类门,具有防火防水功能
public class ADoor implements WaterProof, FireProof {
    @Override
    public void fireProof() {
        System.out.println("A品牌安全门防火功能");
    }
    @Override
    public void waterProof() {
        System.out.println("A品牌安全门防水功能");
    }
}// B类安全门
public class BDoor implements WaterProof {
    @Override
    public void waterProof() {
        System.out.println("B品牌安全门防水功能");
    }
}

这样改进以后可以看到,当不同的品牌的安全门具有不同的功能的时,有什么功能就实现什么功能的接口,假如以后还有一个C品牌类的安全门具有防火防水防盗的功能,那么可以加一个防盗的接口,然后C类门去实现防火防水防盗的接口即可。这就是接口隔离原则。

5.迪米特法则

迪米特法则又叫最少知识原则。如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。

下面是一个明星,粉丝,经纪公司的例子:

// 明星类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Star {
    private String name;
}
// 粉丝类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Fans {
    private String name;
}
// 经纪公司类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Company {
    private String name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Agent {
    private Star star;
    private Fans fans;
    private Company company;
    //和粉丝见面的方法
    public void meeting(){
        System.out.println(star.getName() + "和粉丝"+fans.getName() + "见面");
    }
    //和媒体公司洽谈的方法
    public void business(){
        System.out.println(star.getName() + "和" + company.getName() + "洽谈");
    }
}
    // 测试
    public static void main(String[] args) {
        //创建明星对象
        Star star = new Star("严晓波");
        //创建粉丝对象
        Fans fans = new Fans("彭晓锋");
        //创建公司对象
        Company company = new Company("杨永信电疗娱乐有限公司");
        //创建经纪人对象
        Agent agent = new Agent(star, fans, company);
        agent.meeting();// 严晓波和粉丝彭晓锋见面
        agent.business();// 严晓波和杨永信电疗娱乐有限公司洽谈
    }

这里明星的日常事务有经纪人负责处理,比如和粉丝见面,和媒体公司洽谈,这里明星的朋友是经纪人,而和粉丝和公司是陌生人,所以适合使用迪米特法则。本例子可知迪米特法则主要是为了降低明星与粉丝和公司之间的耦合度。

6.里氏替换原则

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

下面正方形不是长方形的例子:

// 长方形
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Rectangle {
    private Double length;
    private Double width;
}
// 正方形类(错误继承长方形)
public class Square extends Rectangle {
    @Override
    public void setLength(Double length) {
        super.setLength(length);
        super.setWidth(length);
    }
    public void setWidth(Double width) {
        super.setLength(width);
        super.setWidth(width);
    }
}

// 测试类
public class Test {
    public static void main(String[] args){
        // 创建长方形对象
        Rectangle rectangle = new Rectangle(15.0, 10.0);
        // 扩宽
        resize(rectangle);
        // 15.0
        // 16.0
        print(rectangle);
        System.out.println("====================");
        Square square = new Square();
        square.setLength(10.0);
        // 扩宽
        resize(square);
        // 死循环 直到oom
        print(square);
    }
    // 扩宽修正方法
    public static void resize(Rectangle rectangle) {
        while (rectangle.getWidth() <= rectangle.getLength()) {
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }
    // 打印长宽
    public static void print(Rectangle rectangle) {
        System.out.println(rectangle.getLength());
        System.out.println(rectangle.getWidth());
    }
}

可以从运行上面的代码看到,当我们调用resize()方法时候,普通长方形可以正常运行;但是当调用resize()方法的对象是正方形时,会死循环,这是因为正方形的长宽相等,永远都不会满足扩宽的条件。所以可以得出结论:在resize()方法中,长方形的参数是不能被正方形的参数所替代的,如果进行了替换就得不到预期的效果,所以Square和Rectangle类之间的继承关系违反了里氏代换原则,它们之间的继承关系不成立。修改后的代码如下:

// 四边形接口,长方形和正方形同属于四边形
public interface Quadrilateral {
    Double getLength(); // 获取长
    Double getWidth(); // 获取宽
}
// 长方形
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Rectangle implements Quadrilateral {
    private Double length;
    private Double width;
}
@Data
public class Square implements Quadrilateral {
    private Double side;
    @Override
    public Double getLength() {
        return side;
    }
    @Override
    public Double getWidth() {
        return side;
    }
}
// 测试类
public class Test {
    public static void main(String[] args){
        // 创建长方形对象
        Rectangle rectangle = new Rectangle(15.0, 10.0);
        // 扩宽
        resize(rectangle);
        // 15.0
        // 16.0
        print(rectangle);
    }
    // 扩宽修正方法
    public static void resize(Rectangle rectangle) {
        while (rectangle.getWidth() <= rectangle.getLength()) {
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }
    // 打印长宽
    public static void print(Rectangle rectangle) {
        System.out.println(rectangle.getLength());
        System.out.println(rectangle.getWidth());
    }
}

这样Square类的对象就不能使用resize()方法。

7.合成复用原则

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

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

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

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

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

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

下面是汽车种类的例子:

继承复用
继承复用

合成复用

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

制造业为什么要数字化?有何意义?

制造业为什么要数字化&#xff1f;有何意义&#xff1f; 党的二十大报告指出&#xff0c;要“坚持把发展经济的着力点放在实体经济上&#xff0c;推进新型工业化”“促进数字经济和实体经济深度融合”。 新一代信息技术催生第四次工业革命&#xff0c;互联网、大数据、人工智能…

【五一创作】自动驾驶技术未来大有可为

本文概要 自动驾驶技术是当今汽车行业的发展热点之一&#xff0c;但其也存在着许多争议。大家也可以从以下几个维度谈谈你对这项技术的看法。 &#x1f31f;&#x1f31f;&#x1f31f;个人简介&#x1f31f;&#x1f31f;&#x1f31f; ☀️大家好&#xff01;我是新人小白博…

带头双向循环链表(增、删 、查、改)基本操作详细介绍 必看!!!

文章目录 链表介绍链表初始化链表打印查找元素增加节点头插尾插在指定位置插入 删除节点头删尾删删除指定位置节点 链表判空获取链表中元素的个数链表销毁 链表介绍 前面说到&#xff0c;链表的结构一共有八种&#xff1a;带头单向循环链表、带头单向非循环链表、带头双向循环…

《编码——隐匿在计算机软硬件背后的语言》精炼——第17章(自动操作)

夫道成于学而藏于书&#xff0c;学进于振而废于穷。 文章目录 完善加法器加入代码的加法器扩大加数范围自由调用地址的加法器合并代码RAM和数据RAMJump指令硬件实现条件Jump指令零转移的硬件实现条件Jump指令的例子 总结 完善加法器 我们在第14章介绍了一个可以进行连加的加法…

ChatGPT都有些什么好玩的玩法?

ChatGPT是一个智能聊天机器人&#xff0c;可以进行多种有趣的玩法&#xff0c;以下是其中一些&#xff1a; 1. 问答游戏&#xff1a;ChatGPT可以回答各种问题&#xff0c;你可以和它玩问答游戏&#xff0c;看看谁更聪明。 2. 聊天互动&#xff1a;ChatGPT可以进行自然语言聊天…

自学黑客【网络安全】,一般人我劝你还是算了吧

一、自学网络安全学习的误区和陷阱 1.不要试图先成为一名程序员&#xff08;以编程为基础的学习&#xff09;再开始学习 我一直强调不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;而且实际向安全过渡后可用到的关键…

分区计量管理项目应用

为充分发挥分区计量管理项目在漏损控制的效用&#xff0c;应构建科学完备的应用体系&#xff0c;如下图 分区计量应用体系 1. 基于水量平衡分析的漏损现状评估方法 分区计量管理项目通过监控分析DMA 分区内流量、压力、水质、大用户用水等情况&#xff0c;结合营业抄收系统的营…

win10中rclone挂载minio的多实例安装方式

1.下载rclone安装包&#xff0c;复制多个.exe并重命名 2.1添加rclone1server.xml <service><id>rclone1</id><name>rclone1</name><description>rclone1service</description><executable>rclone</executable><argum…

LeetCode_二叉树_简单_112.路径总和

目录 1.题目2.思路3.代码实现&#xff08;Java&#xff09; 1.题目 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum。判断该树中是否存在 根节点到叶子节点的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum。如果存在&#xff0c;返回 true&#…

【学习笔记】低速数字输入电路

1、方案设计&#xff1a;单通道、单向、反相器 该电路采用单通道&#xff0c;单向光耦&#xff0c;只支持漏型输入&#xff0c;电路的输入端压差满足24V DC10%(21.6V DC-26.4V DC)&#xff0c;输出端电压在0~3.3V范围摆动。 1.1关键技术规格 1.2具体原理图 1.3电路原理详解 …

Java版本电子招标采购系统源码:营造全面规范安全的电子招投标环境,促进招投标市场健康可持续发展

营造全面规范安全的电子招投标环境&#xff0c;促进招投标市场健康可持续发展 传统采购模式面临的挑战 一、立项管理 1、招标立项申请 功能点&#xff1a;招标类项目立项申请入口&#xff0c;用户可以保存为草稿&#xff0c;提交。 2、非招标立项申请 功能点&#xff1a;非招标…

如何利用生产管理系统提高粉末治金工业的生产调度能力

在粉末冶金工业中&#xff0c;生产管理系统的应用已经成为了一个必不可少的部分。生产管理系统可以帮助企业实现自动化、信息化、智能化的生产&#xff0c;提高生产效率、降低生产成本、提高产品质量。生产管理系统可以对生产流程进行全面的监控和管理&#xff0c;从而实现生产…

Android还要继续学习吗?高薪高级开发领先位置占据一席之地

Android开发还有必要学习吗 &#xff1f; 我们来看Android从业大佬的回答&#xff1b;从回答中可以读取出一些信息&#xff0c;Android市场仍有岗位需求&#xff0c;只不过减少许多初级Android开发岗位。对于中高端市场还是面临着缺少人才&#xff1b;因为初级开发人员多啊&am…

Adobe Photoshop 2022版 功能介绍及使用技巧

目录 版本介绍&#xff1a; 使用技巧&#xff1a; 截图展示&#xff1a; 分享 版本介绍&#xff1a; Adobe Photoshop 2022是Adobe公司的一款专业的图像处理软件&#xff0c;它提供了强大的图像处理功能&#xff0c;从色彩调整&#xff0c;图层处理到高级合成等功能。新版…

AP3266 DC-DC大功率同步降压恒流芯片 过EMC三级 摩托电动汽车灯IC

1&#xff0c;产品描述 AP3266 是高效率、外围简单、内置功率管的同步降压恒流芯片&#xff0c;适用于4-40V输入的降压LED恒流驱动芯片。输出功率可达 40W&#xff0c;电流3.6A。AP3266 可通过调节 OVP 端口的分压电阻&#xff0c;设定输出空载电压 保护&#xff0c;避免高压 空…

Jmeter和Postman那个工具更适合做接口测试?

软件测试行业做功能测试和接口测试的人相对比较多。在测试工作中&#xff0c;有高手&#xff0c;自然也会有小白&#xff0c;但有一点我们无法否认&#xff0c;就是每一个高手都是从小白开始的&#xff0c;所以今天我们就来谈谈一大部分人在做的接口测试&#xff0c;小白变高手…

基于梯度提升决策树的组合特征方法,《百面机器学习》学习笔记

《百面机器学习》学习笔记&#xff1a;基于梯度提升决策树的组合特征方法 基于梯度提升决策树的组合特征方法梯度提升决策树这里举一个例子来说明梯度提升决策树的思想方法为了更好地说明如何使用梯度提升决策树来实现对特征的组合&#xff0c;再举一个例子假设对于某种类型的输…

Nuxt学习笔记

创建项目 npx create-nuxt-app projectName SSR 渲染流程 客户端发送 URL 请求到服务端&#xff0c;服务端读取对应的 URL 的模板信息&#xff0c;在服务端做出 HTML 和数据的渲染&#xff0c;渲染完成之后返回整个 HTML 结构给客户端。所以用户在浏览首屏的时候速度会比较快…

物联网| 定时器计数器开发之中断方法|定时器中断处理函数|完整测试代码|物联网之蓝牙4.0 BLE基础-学习笔记(6)

文章目录 11 定时器计数器开发之中断方法定时器中断处理函数:完整测试代码&#xff1a; 11 定时器计数器开发之中断方法 LED控制电路同前节&#xff1a; CC2530的T3定时器(8位&#xff09;需要了解T3GJL,T3CCTLO,T3CCO,T3CCTL1,T3CC寄存器。如下表所示&#xff1a; 按照表格…

像素画板-第14届蓝桥杯省赛Scratch初级组真题第4题

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第133讲。 像素画板&#xff0c;本题是2023年5月7日举行的第14届蓝桥杯省赛Scratch图形化编程初级组真题第4题&#xf…