设计模式实践—重要的原则之单一职责原则

本文探讨了单一职责原则(SRP),指出一个类或模块应专注于一个职责。通过UserInfo类的例子,解释了如何根据需求演进进行类的拆分,以保持代码的可读性和可维护性。同时,文章强调了不应过度设计,并提供了判断是否需要拆分类的实用标准。最后,通过Serializer和Deserializer的案例,说明了拆分过细可能带来的问题,强调了保持适当内聚的重要性。
摘要由CSDN通过智能技术生成

单一职责原则Single Responsibility Principle(SRP)

一个类或者模块只负责完成一个职责(或者功能)

例如,有一个实体类UserInfo,如下所示

public class UserInfo {
  private long userId;
  private String username;
  private String email;
  private String telephone;
  private long createTime;
  private long lastLoginTime;
  private String avatarUrl;
  private String provinceOfAddress; // 省
  private String cityOfAddress; // 市
  private String regionOfAddress; // 区 
  private String detailedAddress; // 详细地址
  // ...省略其他属性和方法...
}

如果在不考虑其他模块“过度”交互的情况下,以上属性信息包含在一个userInfo对象中也能够接受。但是如果后续开发又引入了其他功能,例如TransInfo电商物流信息等,用户地址相关的信息作为可复用属性,就有必要剥离出来作为另一个模块了,例如UserAddress类。

        后续如果引入了其他产品,例如点评平台、外卖平台等,则又有必要将用户信息中身份认证相关的属性,例如email、telephone等抽象出来称为例如UserAuthInfo类中。

        所以单一职责原则需要根据不同的应用场景、不同阶段的需求来划分和拆解。在真正的软件开发中,我们也没必要过于未雨绸缪,过度设计。所以,我们可以先写一个粗粒度的类,满足业务需求。随着业务的发展,如果粗粒度的类越来越庞大,代码越来越多,这个时候,我们就可以将这个粗粒度的类,拆分成几个更细粒度的类。这就是所谓的持续重构

        比起主观的去思考是否职责单一,以下是更有指导意义和可执行性的几条规则:

  • 类中的代码行数、函数或属性过多,会影响代码的可读性和可维护性,我们就需要考虑对类进行拆分;
  • 类依赖的其他类过多,或者依赖类的其他类过多,不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分;
  • 私有方法过多,我们就要考虑能否将私有方法独立到新的类中,设置为 public 方法,供更多的类使用,从而提高代码的复用性;
  • 比较难给类起一个合适名字,很难用一个业务名词概括,或者只能用一些笼统的 Manager、Context 之类的词语来命名,这就说明类的职责定义得可能不够清晰;
  • 类中大量的方法都是集中操作类中的某几个属性,比如,在 UserInfo 例子中,如果一大半的方法都是在操作 address 信息,那就可以考虑将这几个属性和对应的方法拆分出来。

当然模块的单一职责拆分并不是越细越好的。例如一个序列化工具类Serializer

/**
 * Protocol format: identifier-string;{gson string}
 * For example: UEUEUE;{"a":"A","b":"B"}
 */
public class Serialization {
  private static final String IDENTIFIER_STRING = "UEUEUE;";
  private Gson gson;
  
  public Serialization() {
    this.gson = new Gson();
  }
  
  public String serialize(Map<String, String> object) {
    StringBuilder textBuilder = new StringBuilder();
    textBuilder.append(IDENTIFIER_STRING);
    textBuilder.append(gson.toJson(object));
    return textBuilder.toString();
  }
  
  public Map<String, String> deserialize(String text) {
    if (!text.startsWith(IDENTIFIER_STRING)) {
        return Collections.emptyMap();
    }
    String gsonStr = text.substring(IDENTIFIER_STRING.length());
    return gson.fromJson(gsonStr, Map.class);
  }
}

如果将其强行拆分为,只负责序列化工作的 Serializer 类和另一个只负责反序列化工作的 Deserializer 类,例如:

public class Serializer {
  private static final String IDENTIFIER_STRING = "UEUEUE;";
  private Gson gson;
  
  public Serializer() {
    this.gson = new Gson();
  }
  
  public String serialize(Map<String, String> object) {
    StringBuilder textBuilder = new StringBuilder();
    textBuilder.append(IDENTIFIER_STRING);
    textBuilder.append(gson.toJson(object));
    return textBuilder.toString();
  }
}

public class Deserializer {
  private static final String IDENTIFIER_STRING = "UEUEUE;";
  private Gson gson;
  
  public Deserializer() {
    this.gson = new Gson();
  }
  
  public Map<String, String> deserialize(String text) {
    if (!text.startsWith(IDENTIFIER_STRING)) {
        return Collections.emptyMap();
    }
    String gsonStr = text.substring(IDENTIFIER_STRING.length());
    return gson.fromJson(gsonStr, Map.class);
  }
}

        经过拆分后功能职责的确更加单一了,但是引入了新问题:如果修改了协议(GSON改为JSON),或数据标志("UEUEUE"改为"XDXDXD”),那么Serializer 类和 Deserializer 类都需要做相应的修改,破坏了“高内聚”的原则。另外如果修改过程中Serializer和Deserializer没有同步修改,那么对象的序列化+反序列化可能报错,代码的可维护性反而变差了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值