solid原则应用实例_设计模式solid原则

solid原则应用实例_设计模式solid原则在面向对象编程中,SOLID是5个重要的设计原则的缩写。首先是由著名的软件大师RobertC.Martin(Bob大叔)在DesignPrinciplesandDesignPatterns中提出,后来MichaelFeathers用SOLID来概括这五大原则。SOLID原则使得软件设计更加容易理解、灵活和可维护。作为一名软件工程师,这5个原则我们必须知道。本文,我将涵盖这些原则,并举例说明怎样是违背了原则,以及如何进行纠正来符合SOLID原则。S—Singlerespons

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

在面向对象编程中,SOLID是5个重要的设计原则的缩写。首先是由著名的软件大师Robert C.Martin (Bob 大叔)在Design Principles and Design Patterns 中提出, 后来Michael Feathers 用SOLID来概括这五大原则。

SOLID原则使得软件设计更加容易理解、灵活和可维护。作为一名软件工程师,这5个原则我们必须知道。

本文,我将涵盖这些原则,并举例说明怎样是违背了原则,以及如何进行纠正来符合SOLID原则。

S — Single responsibility principle

在程序设计中,单一责任原则指的是每个模块或者类应该只有一个职责。

你可能听过这样一句谚语“做一件事并把它做好”,这指的就是单一责任原则。

在这里插入图片描述

在文章《Principles of Object Oriented Design》中,Bob 大叔定义责任为“改变的原因”。并认为有一个且仅有一个原因使得类或模块发生改变。

class User
{
    void CreatePost(Database db, string postMessage)
    {
        try
        {
            db.Add(postMessage);
        }
        catch (Exception ex)
        {
            db.LogError("An error occured: ", ex.ToString());
            File.WriteAllText("\LocalErrors.txt", ex.ToString());
        }
    }
}

Jetbrains全家桶1年46,售后保障稳定

在上述代码示例中,我们注意到*CreatePost()*方法有多个功能,创建新的邮件,在数据库中记录错误日志以及在本地文件记录错误日志。

这违背了单一责任原则。我们尝试修改如下:

class Post
{
    private ErrorLogger errorLogger = new ErrorLogger();

    void CreatePost(Database db, string postMessage)
    {
        try
        {
            db.Add(postMessage);
        }
        catch (Exception ex)
        {
            errorLogger.log(ex.ToString())
        }
    }
}

class ErrorLogger
{
    void log(string error)
    {
      db.LogError("An error occured: ", error);
      File.WriteAllText("\LocalErrors.txt", error);
    }
}

通过把错误日志功能抽象出来,我们不再违背单一责任原则。

现在有2个类,每个类都有一个责任;创建邮件和记录一个错误日志。

O — Open/closed principle

在程序设计中,开闭原则指的是软件对象(类,模块,函数等等)应该对扩展开放,对修改关闭。

在这里插入图片描述

如果你熟悉OOP,那么对于多态应该不陌生。通过继承或接口实现,使得一个抽象类具有多个子类,就可以确保代码是符合开闭原则的。

这听起来有点困惑,所以接下来举个例子,你就会非常清楚我在说什么。

class Post
{
    void CreatePost(Database db, string postMessage)
    {
        if (postMessage.StartsWith("#"))
        {
            db.AddAsTag(postMessage);
        }
        else
        {
            db.Add(postMessage);
        }
    }
}

在这个代码段中,每当邮件是用字符“#“开头,我们都需要做一些指定。然而,当有不同的字符开头,代码会有不同的行为,这违背了开闭原则。

比如,如果我们以后想用“@”开头,我们必须在CreatePost()方法中增加一个‘else if’,这修改了类。

这里简单使用了继承来使代码符合开闭原则。

class Post
{
    void CreatePost(Database db, string postMessage)
    {
        db.Add(postMessage);
    }
}

class TagPost : Post
{
    override void CreatePost(Database db, string postMessage)
    {
        db.AddAsTag(postMessage);
    }
}

通过使用继承,重写*CreatePost()*方法来创建邮件的扩展行为变得更加简单。

现在,判断第一个字符“#”可以在软件其它地方处理。更酷的事情是,如果我们想改变postMessage的判断方式,可以不影响基类的行为。

L — Liskov substitution principle

这个原则可能是第一次介绍时最难理解的一个。

在这里插入图片描述

在程序设计中,里氏替换原则指的是如果 ST 的子类,那么T 的实例可以用 S 的实例取代。

更一般的表述是,在不改变程序正确性的前提下,派生类对象可以在程序中代替其基类对象。

class Post
{
    void CreatePost(Database db, string postMessage)
    {
        db.Add(postMessage);
    }
}

class TagPost : Post
{
    override void CreatePost(Database db, string postMessage)
    {
        db.AddAsTag(postMessage);
    }
}

class MentionPost : Post
{
    void CreateMentionPost(Database db, string postMessage)
    {
        string user = postMessage.parseUser();

        db.NotifyUser(user);
        db.OverrideExistingMention(user, postMessage);
        base.CreatePost(db, postMessage);
    }
}

class PostHandler
{
    private database = new Database();

    void HandleNewPosts() {
        List<string> newPosts = database.getUnhandledPostsMessages();

        foreach (string postMessage in newPosts)
        {
            Post post;

            if (postMessage.StartsWith("#"))
            {
                post = new TagPost();
            }
            else if (postMessage.StartsWith("@"))
            {
                post = new MentionPost();
            }
            else {
                post = new Post();
            }

            post.CreatePost(database, postMessage);
        }
    }
}

由于没有覆写,CreatePost()方法在子类MentionPost中不会起到应有的作用。

修改后如下:

...

class MentionPost : Post
{
    override void CreatePost(Database db, string postMessage)
    {
        string user = postMessage.parseUser();

        NotifyUser(user);
        OverrideExistingMention(user, postMessage)
        base.CreatePost(db, postMessage);
    }

    private void NotifyUser(string user)
    {
        db.NotifyUser(user);
    }

    private void OverrideExistingMention(string user, string postMessage)
    {
        db.OverrideExistingMention(user, postMessage);
    }
}

...

通过重构MentionPost类,就能满足可替换性。

这只是一个不违背里氏替换原则的简单例子。然而,在实际使用过程中,这种情形可以用多种方式实现并且不易识别出来。

I — Interface segregation principle

这个原则理解起来很简单,实际上,如果你习惯于使用接口,很大概率上会用到这个原则。

在这里插入图片描述

在程序设计中,接口隔离原则指的是客户不应被迫使用对其而言无用的方法或功能。

简单来讲,不要在已有接口上增加新的方法来实现新的功能。相反的,可以创建新的接口,如果有必要,可以让你的类实现多个接口。

interface IPost
{
    void CreatePost();
}

interface IPostNew
{
    void CreatePost();
    void ReadPost();
}

在上述代码示例中,假设我已经有了一个IPost 接口,包含CreatePost()方法;后来,我增加了一个新方法 ReadPost(),修改了这个接口,变成IPostNew 接口,这违背了接口隔离原则。修改如下:

interface IPostCreate
{
    void CreatePost();
}

interface IPostRead
{
    void ReadPost();
}

一旦任何类需要实现这2个方法,就将同时实现这2个接口。

D – Dependency inversion principle

最后,我们来看一下D,最后一个设计原则。

在这里插入图片描述

在程序设计中,依赖倒置原则用于解耦软件中的模块。这个原则表述如下:

  • 高层的模块不应该依赖底层的模块,而应该依赖抽象;
  • 抽象不应该依赖实现细节,实现细节应该依赖抽象。

为了遵循这一原则,我们需要使用一种设计模式称为依赖注入,典型的,依赖注入通过类的构造函数作为输入参数。

class Post
{
    private ErrorLogger errorLogger = new ErrorLogger();

    void CreatePost(Database db, string postMessage)
    {
        try
        {
            db.Add(postMessage);
        }
        catch (Exception ex)
        {
            errorLogger.log(ex.ToString())
        }
    }
}

观察到我们在Post 类中创建了ErrorLogger 实例,如果我们想使用不同的日志,我们需要修改Post类,这违背了依赖倒置原则。修改如下:

class Post
{
    private Logger _logger;

    public Post(Logger injectedLogger)
    {
        _logger = injectedLogger;
    }

    void CreatePost(Database db, string postMessage)
    {
        try
        {
            db.Add(postMessage);
        }
        catch (Exception ex)
        {
            _logger.log(ex.ToString());
        }
    }
}

通过使用依赖注入,我们不再依赖Post类来定义指定类型的日志。

OK,介绍完这么多,也大致理解了这几个原则。这些原则有区别,同时彼此间也有着联系。

在这里插入图片描述

  • 单一职责原则是 SOLID 所有原则的基础和解决问题的思路。
  • 开闭原则是直接保障代码质量的原则,用来解决设计的脆弱性、僵化、难以阅读、难以复用等问题,应用开闭原则的关键在于如何“抽象”。
  • 里氏替换原则通过确保子类和父类是 “is a” 的关系,来帮助实现开闭原则。该原则的使用中,引申出面向对象角度的 “is a” 是关于行为的,以及模型的正确性不是内在的,而是由它的客户程序来体现。
  • 接口隔离原则提供了一种方案,在不违反里氏替换原则的情况下,如何实现开闭原则。同时接口隔离的思想本身也体现了单一职责原则。
  • 依赖倒置原则是过程式设计与面向对象设计的分水岭,通过适当的抽象,让高层模块和底层模块同样地可复用和可测试。同时它也被用来指导接口隔离原则。

参考

【1】What Is Design Pattern?

【2】Single Responsibility Principle in C++

【3】单一功能原则

【4】Open Closed Principle in C++

【5】开闭原则

【6】Liskov’s Substitution Principle in C++

【7】里氏替换原则

【8】Interface Segregation Principle in C++

【9】接口隔离原则

【10】Dependency Inversion Principle in C++

【11】依赖倒置原则


That’s it!If you have any questions or feedback, please feel free to comment below.

-EOF-

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/215915.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 启动马达接线实物图_软启动器怎么接线?一张电路图一张实物图供大家参考

    启动马达接线实物图_软启动器怎么接线?一张电路图一张实物图供大家参考朋友们大家好,我是大俵哥,今天我们来聊一下软启动。很多大型动力设备在启动的时候,启动电流都是比较大的,对整个电网有冲击性,所以不能直接启动,具体原因有以下两点。一,有的电机启动电流为额定电流的4--7倍,直接启动会影响同一电网内的其他用电设备。二,直接启动产生较高的峰值转矩,不仅对驱动电机有冲击性,而且易损坏机械装置。软启动相比星三角降压启动、自耦变压器启动等效果要好一些,启动更加平稳,保护也更加…

    2022年6月6日
    290
  • linuxchmod用法_chmod 指定用户

    linuxchmod用法_chmod 指定用户chmod—-改变一个或多个文件的存取模式(mode)chmod[options]modefiles只能文件属主或特权用户才能使用该功能来改变文件存取模式。mode可以是数字形式或以whoopcodepermission形式表示。who是可选的,默认是a(所有用户)。只能选择一个opcode(操作码)。可指定多个mode,以逗号分开。

    2022年10月20日
    0
  • Java Map集合的详解「建议收藏」

    Java Map集合的详解「建议收藏」一,Map先说一下Map吧~如果程序中存储了几百万个学生,而且经常需要使用学号来搜索某个学生,那么这个需求有效的数据结构就是Map。Map是一种依照键(key)存储元素的容器,键(key)很像下标,在List中下标是整数。在Map中键(key)可以使任意类型的对象。Map中不能有重复的键(Key),每个键(key)都有一个对应的值(value)。一个键(key)和它对应的值构成ma

    2022年5月30日
    32
  • IntelliJ IDEA创建maven web项目(IDEA新手适用)

    IntelliJ IDEA创建maven web项目(IDEA新手适用)PS:从eclipse刚转到IDEA,对于这个陌生的工具我表示无言,但听说很好用,也就试试,结果我几乎花了一晚上的时间才搭起来mavenweb项目,觉得在此给各位一个搭建mavenweb项目的教程,指出我踩过的各种坑!步骤一:首先先创建一个project,在这里就是创建一个maven的工作空间步骤二:按照下面的步骤操作就可以了,最后next首先,选择左边的maven然后在右…

    2022年6月26日
    53
  • windowspythonpygame安装_pygame安装(windows pycharm)[通俗易懂]

    windowspythonpygame安装_pygame安装(windows pycharm)[通俗易懂]一、安装流程1.查看python版本2.检查是否安装pip3.下载pygame安装文件选择合适的版本(我选择的是python3.7windows64bit):4.使用pip安装pygame将下载好的whl文件移动到python安装目录的\Lib\site-packages下(我的路径是D:\ProgramFiles\Python\Python37\Lib\site-packages),然后…

    2022年5月23日
    46
  • Maskrcnn中resnet50改为resnet34「建议收藏」

    Maskrcnn中resnet50改为resnet34「建议收藏」因需要训练的数据集并不复杂,resnet50的结构有点冗余,于是就把maskrcnn的backbone从resnet50改为resnet34结构。找到model文件,将resnet50部分代码做一定的修改,就可以得到resnet34的相关代码下面是相关代码:##con_block修改为conv_block0并添加到model文件中defconv_block0(input_tensor…

    2022年10月6日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号