08 SOLID原则之:单一职责

SOLID 原则,实际上是由 5个设计原则组成的,它们分别是:单一职责原则、开闭原则、里式替换原则、接口隔离原则和依赖反转原则, 为了方便记忆是那个五个设计原则:依次取五个设计原则的首字母 S、O、L、I、D 这 5 个英文字母命名。

单一职责原则的英文是 Single Responsibility Principle,缩写为 SRP。这个原则的英文描述是这样的:A class or module should have a single reponsibility。如果我们把它翻译成中文,那就是:一个类或者模块只负责完成一个职责(或者功能)。单一职责原则的核心就是解耦和增强内聚性

怎么判断一个类是否满足单一原则是比较好判断的,但是在设计以及编程的时候,大脑里怎么样区分以及保证编程满足单一原则呢?

为什么一直在强调的单一职责,而实际代码中却屡屡违反呢?

如何判断类的职责是否足够单一?

在设计中很难明确区分是否满足单一,也要结合实际的情况来考虑。

js 复制代码
    private String username;
    private String password;
    private String pubsubTopic;
    private int pubsubPort;
    private Boolean isPc;
    private PubsubClient pubsubClient;
    private TelemetryTool telemetryTool;

分析上面的这个例子,这个例子里包含账号密码,包含常用的打点参数,

  1. 如果整个项目对于安全层级要求较高,要存取在安全秘钥或者秘钥服务器中,那么usernamepassword,就需要自己封装一个类,并且有有网络请求以及加解密和校验逻辑在里面。

  2. 假如说目前对于账号密码的要求较低,并且用户名和密码也不会对业务有很大的影响,单纯是数据查看,并且数据几乎不会变动,那么用户名和密码完全可以存在于Pubsub初始化处理类。

从刚刚这个例子,不同的应用场景、不同阶段的需求背景下,对同一个类的职责是否单一的判定,结论是不一样的。在当下的需求背景下,一个类的设计可能已经满足单一职责原则了,但如果在未来的某个需求背景下,可能就不满足了,需要继续拆分成粒度更细的类。

根据《代码整洁之道》和网上的例子,下面的情况就需要对类进行重构和拆分了:

  • 当类中的代码行数很多时,鼠标滚轮需要滚半天,影响代码的可读性和可维护性,就需要考虑拆分了。
  • 类依赖的其他类过多,或者依赖类的其他类过多,不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分;
  • 当给类命名时,发现他既有这个功能,也有那个功能,那么就需要考虑下是不是职责不够清晰,没有遵循单一职责。

编程过程这能够怎么注意实现呢?

其实在我们调用很多模块的方法和监听对象时,想一想,很熟悉的就是继承某个接口,重写某个方法,来单独实现我们需要的功能,我想这就是单一职责的具体体现吧。

1. 方法

js 复制代码
public interface AccountOperate {
    void updateUserName(UserInfo userInfo);

    void updateUserPassword(UserInfo userInfo);
}
js 复制代码
public class AccountOperateImpl implements AccountOperate {
    @Override
    public void updateUserName(UserInfo userInfo) {
        // 修改用户名逻辑
    }

    @Override
    public void updateUserPassword(UserInfo userInfo) {
        // 修改密码逻辑
    }
}

2. 接口

想起来前段时间刚刚加入的业务,有2个弹窗,一个是业务开启的弹窗,另一个是同一隐私的弹窗,两个弹窗前面都是实现的同一个接口

js 复制代码
/** * 弹窗接口 */ 
public interface Dialog {
    // 开启业务
    void agree(); 
    
    // 不同意
    void disAgree(); 
}


//实现-首页
public class ShouPage implements Dialog{

    @Override 
    public void agree() { 
         // 开启业务
    }

    @Override 
    public void disAgree() { 
         // 不同意
    }
}

//实现-设置页面
public class SettingPage implements Dialog{

    @Override 
    public void agree() { 
         // 开启业务
    }

    @Override 
    public void disAgree() { 
         // 不同意
    }
}

刚刚开始业务所有开启入口都实现这个接口,来对用户同意开启业务和拒绝业务,开启隐私和拒绝隐私做处理。后来产品对业务做了调整,但是呢,只对用户开启页面做了修改,隐私页面没有。特别是需要调用接口传参数,连不需要修改的隐私页面也被动做了修改。当时对单一职责不太了解。那现在学习了,应该怎么改呢?

js 复制代码
/** * 弹窗接口 */ 
public interface ProjectDialog {
    // 开启业务
    void agree(int num); 
    
    // 不同意
    void disAgree(); 
}

public interface CookieDialog {
    // 开启业务
    void agree(); 
    
    // 不同意
    void disAgree(); 
}


//实现-首页
public class ProjectPage implements Dialog{

    @Override 
    public void agree(int num) { 
         // 开启业务
    }

    @Override 
    public void disAgree() { 
         // 不同意
    }
}

//实现-设置页面
public class SettingPage implements CookieDialog{

    @Override 
    public void agree() { 
         // 开启业务
    }

    @Override 
    public void disAgree() { 
         // 不同意
    }
}

应该这样设计,不应该贪图省事就混为一谈,导致后面的人修改的时候没有办法进行修改,虽然代码review中,说这一点小修改也没有什么,但是现在想想,确实挺低级的,对自己的应该有一定的要求。

3. 类

因为类是起初实现代码的地方,不能单独为了满足单一职责原则,是不是把类拆得越细就越好呢?答案是否定的。

比如说上面的例子,同意开启隐私和不同意开启隐私,在类中也需要执行单一原则的话,需要拆分成2个类。但是也没有其他的逻辑来实现,那这样显然是多余的。

js 复制代码
/** * 弹窗接口 */ 
public interface ProjectDialog {
    // 开启业务
    void agree(int num); 
    
    // 不同意
    void disAgree(); 
}


//实现-首页
public class ProjectPage implements Dialog{

    @Override 
    public void agree(int num) { 
         // 开启业务
         //写入缓存,记录开关状态
          new ProjectInitManger().initProject()
    }

    @Override 
    public void disAgree() { 
         // 不同意
         //清除缓存,记录开关状态
         new ProjectInitManger().resetProject(f)
    }
}
js 复制代码
public class ProjectInitManger{

    //写入缓存,记录开关状态
    public void initProject()
    
    //清除缓存,记录开关状态
    public void initProject()

}

上面的例子就是对开启应用时状态的记录,没有其他额外的功能,如果强制遵循单一职责的话,那就需要再拆分为2个类,并没有实际的意义。

单一职责原则的优点

  • 类的复杂性降低: 一个类实现什么职责都有明确定义, 复杂性自然就降低
  • 可读性提高: 复杂性降低,可读性自然就提高
  • 可维护性提高: 可读性提高,代码就更容易维护

实际中为什么还会违反单一原则?

一个是真的是对项目不了解,二是对于单一职责不清晰,三是人的惰性,共勉。

相关推荐
忒可君18 分钟前
C# winform 报错:类型“System.Int32”的对象无法转换为类型“System.Int16”。
java·开发语言
斌斌_____33 分钟前
Spring Boot 配置文件的加载顺序
java·spring boot·后端
路在脚下@42 分钟前
Spring如何处理循环依赖
java·后端·spring
一个不秃头的 程序员1 小时前
代码加入SFTP JAVA ---(小白篇3)
java·python·github
丁总学Java1 小时前
--spring.profiles.active=prod
java·spring
上等猿1 小时前
集合stream
java
java1234_小锋1 小时前
MyBatis如何处理延迟加载?
java·开发语言
菠萝咕噜肉i1 小时前
MyBatis是什么?为什么有全自动ORM框架还是MyBatis比较受欢迎?
java·mybatis·框架·半自动
林的快手2 小时前
209.长度最小的子数组
java·数据结构·数据库·python·算法·leetcode
zh路西法2 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式