开发原则二:单一职责SRP

单一职责原则(SRP)要求一个类或方法应有且仅有一个引起变化的原因。遵循此原则能提高代码的内聚性、可复用性和可测试性,降低耦合性,增强可维护性。通过接口、方法和类的重构示例,展示了如何实现单一职责,以创建更清晰、更健壮的软件设计。
摘要由CSDN通过智能技术生成

前言

单一职责原则(Single Responsibility Principle,SRP)又称单一功能原则,由罗伯特·C.马丁(Robert C. Martin)于《敏捷软件开发:原则、模式和实践》一书中提出的。这里的职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分(There should never be more than one reason for a class to change)
在这里插入图片描述

单一职责原则是 OOP 的一个基本原则,它指出一个类应该只有一个职责。这有助于保持代码可维护、灵活且易于理解。通过应用 SRP,可以创建集中且简洁的类,这使它们更易于理解和重用。

单一职责原则的重要性

遵守单一职责原则是软件设计和开发中的重要原则之一。以下是一些理由解释为什么要遵守单一职责原则:
高内聚性:单一职责原则能够确保类或模块只关注一个特定的功能或任务,从而提高代码的内聚性。类的职责越单一,代码的逻辑结构越清晰、简洁,使得代码容易理解和维护。
可复用性:遵守单一职责原则可以使独立的功能块更容易被复用,在其他系统中使用。由于每个类或模块只负责一个特定职责,因此可以更方便地将其提取出来,与其他部分进行组合使用。
可测试性:当一个类或模块只有一个职责时,测试该职责变得更加简单明确。这样可以更容易编写单元测试,验证类或模块是否正确地执行其职责。
降低耦合性:单一职责原则有助于减少类或模块之间的耦合。当每个类都只负责一个职责时,修改其中一个类不会影响到其他类的功能,从而降低了系统的耦合度,使得代码更加灵活和可扩展。
提高可维护性:遵守单一职责原则使得代码的变动范围更加可控。当需要修改或扩展某个功能时,只需要关注与该功能相关的类,而不会对其他部分产生影响。这简化了维护工作,减少了引入错误和破坏其他功能的风险。

单一职责的应用范围

单一职责原则适用的范围有接口、方法、类。按大家的说法,接口和方法必须保证单一职责,类就不必保证,只要符合业务就行。

【方法层面】

不遵循单一职责的方法

/**
 * 操作的类型
 */
public enum OperateEnum {
    UPDATE_USERNAME,
    UPDATE_PASSWORD;
}
public interface UserOperate {
    void updateUserInfo(OperateEnum type, UserInfo userInfo);
}
public class UserOperateImpl implements UserOperate{
    @Override
    public void updateUserInfo(OperateEnum type, UserInfo userInfo) {
        if (type == OperateEnum.UPDATE_PASSWORD) {
            // 修改密码
        } else if(type == OperateEnum.UPDATE_USERNAME) {
            // 修改用户名
        }
    }
}
遵循单一职责的方法
public interface UserOperate {
    void updateUserName(UserInfo userInfo);
    void updateUserPassword(UserInfo userInfo);
}
public class UserOperateImpl implements UserOperate {
    @Override
    public void updateUserName(UserInfo userInfo) {
        // 修改用户名逻辑
    }
    @Override
    public void updateUserPassword(UserInfo userInfo) {
        // 修改密码逻辑
    }
}

第一种实现是根据操作类型进行区分, 不同类型执行不同的逻辑. 把修改用户名和修改密码这两件事耦合在一起了. 如果客户端在操作的时候传错了类型, 那么就会发生错误.
第二种实现是我们推荐的实现方式. 修改用户名和修改密码逻辑分开. 各自执行各自的职责, 互不干扰. 功能清晰明了.
由此可见, 第二种设计是符合单一职责原则的. 这是在方法层面实现单一职责原则.

【接口层面】

假设一个场景, 大家一起做家务, 张三扫地, 李四买菜. 李四买完菜回来还得做饭. 这个逻辑怎么实现呢?

/**
 * 做家务
 */
public interface HouseWork {
    // 扫地
    void sweepFloor();
    // 购物
    void shopping();
}
public class Zhangsan implements HouseWork{
    @Override
    public void sweepFloor() {
        // 扫地
    }
    @Override
    public void shopping() {
    }
}
public class Lisi implements HouseWork{
    @Override
    public void sweepFloor() {
    }
    @Override
    public void shopping() {
        // 购物
    }
}

首先定义了一个做家务的接口, 定义两个方法扫地和买菜. 张三扫地, 就实现扫地接口. 李四买菜, 就实现买菜接口. 然后李四买完菜回来还要做饭, 于是就要在接口类中增加一个方法cooking. 张三和李四都重写这个方法, 但只有李四有具体实现.
这样设计本身就是不合理的.
首先: 张三只扫地, 但是他需要重写买菜方法, 李四不需要扫地, 但是李四也要重写扫地方法.
第二: 这也不符合开闭原则. 增加一种类型做饭, 要修改3个类. 这样当逻辑很复杂的时候, 很容易引起意外错误.
上面这种设计不符合单一职责原则, 修改一个地方, 影响了其他不需要修改的地方.

/**
 * 做家务
 */
public interface Hoursework {
}
public interface Shopping extends Hoursework{
    // 购物
    void shopping();
}
public interface SweepFloor extends Hoursework{
    // 扫地
    void sweepFlooring();
}
public class Zhangsan implements SweepFloor{
    @Override
    public void sweepFlooring() {
        // 张三扫地
    }
}
public class Lisi implements Shopping{
    @Override
    public void shopping() {
        // 李四购物
    }
}

上面做家务不是定义成一个接口, 而是将扫地和做家务分开了. 张三扫地, 那么张三就实现扫地的接口. 李四购物, 李四就实现购物的接口. 后面李四要增加一个功能做饭. 那么就新增一个做饭接口, 这次只需要李四实现做饭接口就可以了.

public interface Cooking extends Hoursework{ 
    void cooking();
}
public class Lisi implements Shopping, Cooking{
    @Override
    public void shopping() {
        // 李四购物
    }
    @Override
    public void cooking() {
        // 李四做饭
    }
}

我们看到张三没有实现多余的接口, 李四也没有. 而且当新增功能的时候, 只影响了李四, 并没有影响张三.
这就是符合单一职责原则. 一个类只做一件事. 并且他的修改不会带来其他的变化.

【类层面】

从类的层面来讲, 没有办法完全按照单一职责原来来拆分. 换种说法, 类的职责可大可小, 不想接口那样可以很明确的按照单一职责原则拆分. 只要符合逻辑有道理即可.

public interface UserOperate {
    void login(UserInfo userInfo);
    void register(UserInfo userInfo);
    void logout(UserInfo userInfo);
}
public class UserOperateImpl implements UserOperate{
    @Override
    public void login(UserInfo userInfo) {
        // 用户登录
    }
    @Override
    public void register(UserInfo userInfo) {
        // 用户注册
    }
    @Override
    public void logout(UserInfo userInfo) {
        // 用户登出
    }
}
如果按照单一职责原则拆分, 也可以拆分为下面的形式
public interface Register {
    void register();
}
public interface Login {
    void login();
}
public interface Logout {
    void logout();
}
public class RegisterImpl implements Register{
    @Override
    public void register() {
    }
}
public class LoginImpl implements Login{
    @Override
    public void login() {
        // 用户登录
    }
}
public class LogoutImpl implements Logout{
    @Override
    public void logout() {
    }
}

像上面这样写可不可以呢? 其实也可以, 就是类很多. 如果登录、注册、注销操作代码很多, 那么可以这么写.
总结来说,遵守单一职责原则有助于提高代码质量、可读性、可复用性和可维护性。它是良好软件设计的基础之一,能够帮助开发者构建高效、健壮且易于维护的软件系统。

参考材料:https://developer.aliyun.com/article/921552?spm=a2c6h.24874632.expert-profile.74.1ac62afeKHP9JB

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值