您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

深入设计原则-SOLID

时间:2021-03-08 17:01:39  来源:今日头条  作者:架构即人生

介绍

SOLID是什么,它是如何帮助我们写更好的代码的?

SOLID原则由以下5个概念组成:

  1. Single Responsibility(单一职责)
  2. Open/Closed(开闭)
  3. Liskov Substitution(里氏替换)
  4. Interface Segregation(接口隔离)
  5. Dependency Inversion(依赖反转)

 

简单来说,这些原则帮助我们创造更易维护,易于理解和灵活的软件。因此,随着我们应用的扩大,我们可以降低其复杂性,并在以后为我们省去很多麻烦。

 

深入说明

单一职责原则

像我们从字面意思看到的一样,该原则指出,一个类仅应承担一种责任。此外,它应该只有一个改变的理由(不能同时有很多理由来修改它)。

那么,这个原则是怎么帮助我们写更好的软件的?让我们先看一下它的优点:

  1. 易测试 - 因为职责单一,所以拥有更少的测试用例
  2. 低耦合 - 单个类中较少的功能将具有较少的依赖关系
  3. 易于组织 - 更容易编写代码逻辑

 

下面我们以代码示例说明:

我们用Book类代表一本书,其属性包括:书名(name),作者(author)和内容(text)

/**
 *  实体:书
 */
public class Book {

    private String name;
    private String author;
    private String text;

    /**
     *  文本替换
     * @param word
     * @return
     */
    public String replaceWordInText(String word){
        return text.replaceAll(word, text);
    }

    /**
     *  是否包含指定的文本
     * @param word
     * @return
     */
    public boolean isWordInText(String word){
        return text.contains(word);
    }
}

现在我们的程序运行的很好,我们能存储任意的书的内容。但是我们无法将书的内容打印出来,无法阅读该怎么办?那么,我们就在新增一个打印内容的方法,如:

void printTextToConsole(){
    // 输出文本
}

好的,现在已经可以实现打印内容了。但是,我们这样就违背了“单一职责”(书本身不与打印有什么关系)。为了解决我们的问题,需要实现一个单独的类,该类仅与打印书籍有关:

/**
 * 书籍打印
 */
public class BookPrinter {

    /**
     *  打印到控制台
     * @param text
     */
    void printTextToConsole(String text){
        // printing the text
    }

    /**
     *  打印到其他媒介
     * @param text
     */
    void printTextToAnotherMedium(String text){
        // do something
    }
}

上面的类不仅与书籍本身解耦,而且能够实现通过各种媒介进行打印内容,本身又是一个职责单一的案例(打印)。

 

开闭原则

简单来说,一个类应对扩展是打开的,对修改是关闭的(open for extension, closed for modification)。这样一来,我们就可以避免修改现有代码,从而引入新的潜在的问题。当然有个特例,就是如果现有代码中存在已有bug,我们还是应该去解决它的。

 

比如现在有一个吉他,可以实现基本功能的弹奏:

/**
 *  吉他
 */
public class Guitar {
    private String make;
    private String model;
    private int volume;
}

但是用了一段时间后,觉得有点无聊想增加一些音节,让它用起来更加的摇滚。

我们如果直接在原有的Guitar类上修改,可能会把原有的功能破坏掉从而引入新的问题,所以根据“开闭原则”,我们应该在原有基础上进行扩展而不是修改:

/**
 *  更酷炫的音节
 */
public class SuperCoolGuitarWithFlames extends Guitar {
    private String flameColor;
}

通过扩展实现,我们可以保证现有的功能不会受到破坏。

里氏替换原则

这个原则字面意思较难理解,简单来说就是,如果一个类A是类B的子类,那么我们在不中断程序行为的情况下可以把B替换成A,而不影响程序原有的功能。

我们用代码示例说明:

/**
 *  车
 */
public interface Car {
    /**
     *  打开引擎
     */
    void turnOnEngine();

    /**
     *  加速
     */
    void accelerate();
}

我们定义了一个接口,里面有两个方法,可以实现引擎打开和车加速功能。

下面来看下具体的实现类:

/**
 *  摩托车
 */
public class MotorCar {
    private Engine engine;
    
    public void turnOnEngine() {
        //turn on the engine!
        engine.on();
    }
    public void accelerate() {
        //move forward!
        engine.powerOn(1000);
    }
}

可以看出摩托车属于车的一种,实现了打开引擎和加速能力,我们继续看其他实现:

/**
 *  电车
 */
public class ElectricCar {
    public void turnOnEngine() {
        throw new AssertionError("I don't have an engine!");
    }
    public void accelerate() {
        //this acceleration is crazy!
    }
}

可以看出,上面的电车虽然实现了Car,但是它没有引擎,所以不具有打开引擎的功能,那么这个就改变了程序的行为,违背了我们说的“里氏替换原则”。

接口隔离原则

简单来说,就是将大的接口切分为更小的接口,这样我们就可以确保实现类只需要关心它们感兴趣的方法。

举个例子,假如我们在动物园工作,具体是熊的“看护人”,那么可以这样定义:

public interface BearKeeper {
    /**
     *  给熊洗澡
     */
    void washTheBear();

    /**
     *  给熊喂食
     */
    void feedTheBear();

    /**
     *  抚摸熊
     */
    void petTheBear();
}

我们可以开心的喂养熊,但是抚摸熊可能有危险,但是我们的接口定义的相当大,我们别无选择。那么,现在我们将接口拆分一下:

public interface BearCleaner {
    void washTheBear();
}
public interface BearFeeder {
    void feedTheBear();
}
public interface BearPetter {
    void petTheBear();
}

通过拆分,我们就可以自由实现我们感兴趣的方法,

public class BearCarer implements BearCleaner, BearFeeder {
    public void washTheBear() {
        
    }
    public void feedTheBear() {
     
    }
}

我们可以将抚摸熊的“福利”给那么胆大或疯狂的人:

public class CrazyPerson implements BearPetter {
    public void petTheBear() {
        //Good luck with that!
    }
}

依赖反转

这个原则主要是为了解耦。代替低级模块依赖高级模块,二者都应该依赖抽象。

举个例子,假如我们现有一台windows98电脑:

public class Windows98machine {}

但是没有显示器和键盘对我们来说有什么用呢?于是我们给这台电脑新增显示器和键盘:

public class Windows98Machine {
    private final StandardKeyboard keyboard;
    private final Monitor monitor;
    public Windows98Machine() {
        monitor = new Monitor();
        keyboard = new StandardKeyboard();
    }
}

上面代码运行的很好,我们的电脑已经实现了显示器和键盘,那么问题解决了吗?没有,我们又把这三个类紧密结合在一起了。这不仅使Windows98Machine难以测试,而且还失去了将StandardKeyboard替换为其他类的能力。

让我们把Keyboard抽象出来:

public interface Keyboard { }
public class Windows98Machine{
    private final Keyboard keyboard;
    private final Monitor monitor;
    public Windows98Machine(Keyboard keyboard, Monitor monitor) {
        this.keyboard = keyboard;
        this.monitor = monitor;
    }
}
public class StandardKeyboard implements Keyboard { }

现在Windows98Machine就与StandardKeyboard解耦了,通过依赖反转(依赖抽象而不是具体类)完成了解耦。

 

总结

在本文中我们深入研究了面向对象的SOLID原则,并通过代码示例说明其原理和实现。深入设计原则-SOLID



Tags:SOLID   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
介绍SOLID是什么,它是如何帮助我们写更好的代码的?SOLID原则由以下5个概念组成: Single Responsibility(单一职责) Open/Closed(开闭) Liskov Substitution(里氏替换) Interface Segr...【详细内容】
2021-03-08  Tags: SOLID  点击:(223)  评论:(0)  加入收藏
微服务架构作者:DevOps亮哥来自:DevOps探路者一、关键点:对于面向对象的设计,我们遵循SOLID原则。对于微服务设计,我们建议开发人员遵循IDEALS原则:接口分离(Interface segregation...【详细内容】
2020-09-11  Tags: SOLID  点击:(99)  评论:(0)  加入收藏
S.O.L.I.D原则的重要性> S.O.L.I.D principles 介绍作为开发人员,我们一直在处理遗留代码库。 大多数传统代码库具有紧密耦合的类,冗余代码和较少的测试范围。 快速浏览代码时...【详细内容】
2020-08-02  Tags: SOLID  点击:(95)  评论:(0)  加入收藏
▌简易百科推荐
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(1)  评论:(0)  加入收藏
程序是如何被执行的  程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(9)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(19)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(23)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(24)  评论:(0)  加入收藏
一个项目的大部分API,测试用例在参数和参数值等信息会有很多相似的地方。我们可以复制API,复制用例来快速生成,然后做细微调整既可以满足我们的测试需求1.复制API:在菜单发布单...【详细内容】
2021-12-14  AutoMeter    Tags:AutoMeter   点击:(20)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条