史无前例的设计模式-单一职责原则

一、概念

单一职责:Single Responsibility Principle,简称SRP。字面义很简单,但是做起来有的时候真的蛮难的。就是小到一个接口只干一件事,大到一个类、一个模块只负责完成一个功能。
比如:UserService里面既包含用户账号密码登录注册等操作又包含用户积分等操作。这种情况就很明显的违背了单一职责原则,需要将其拆分为UserService和ScoreService。

二、如何确定是否单一职责

1、描述

上面那么明显的demo就不多说了,但是一般都会出现摸棱两可的情况,比如下面的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.java就足够了,如果你不光有用户系统,还有积分兑换系统,兑换的实物需要发到你的省市区详细地址,这种情况我认为UserInfo.java结合UserAddress.java两个类比较合适。因为地址信息不光显示用,物流发货也需要用。
所以我觉得,这就是取决于产品业务的,当然特别明显的违背了单一职责原则的代码一眼就看得出,只有这种摸棱两可的代码需要取决于业务。但我觉得没必要过度设计,如果你系统有这个需求或者近期可能会出现这个需求,那就拆分。如果近期不会这么搞,我觉得先弄一个类,到时候重构就完了,不要过度设计。

2、总结

总结评判是否符合单一职责原则的几个技巧

  • 类的代码行数、函数、属性过多的时候。多少算多?个人建议类行数不过300行、函数不过50行、函数和属性的个人不多于10个。
  • 类依赖的其他类过多,不符合高内聚低耦合的思想,需要拆分。比如一个service层又依赖了其他七八个service层才能完成某个业务。
  • 私有方法过多,可以考虑放到其他类里弄成public公用。提高复用性。
  • 根据业务场景来适当拆分,这句话好像是废话…这里想说的是那个UserInfo和UserAddress的取舍。

三、切勿过度设计

千万不要为了单一而过度单一。比如Serialization实现了序列化和反序列化的功能。

public class Serialization {
  private static final String IDENTIFIER_STRING = "CTW";
  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);
  }
}

上面的设计很合理,也很完美,完全符合上面【如何确定是否单一职责】的描述。但是如下代码就过度设计了,他将序列化和反序列化拆分成两个Java类。

/**
 * 序列化
 */
public class Serializer {
  private static final String IDENTIFIER_STRING = "CTW";
  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 = "CTW";
  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);
  }
}

乍一看好像是更加符合单一职责了,其实是典型的过度设计。首先完全没必要,照这个思路的话,那所有的类里面都只会包含一个方法了,其次他会引入新的问题,若我们修改了协议,数据标识从CTW变成了HELLO,或者序列化的方式从JSON换成了XML等,那Serializer和Deserializer两个类都需要做相应的修改。显然没有第一种方式的内聚性高,主要是我们如果只修改了Serializer的CTW->HELLO,忘记了反序列化的类的修改,这时候会导致序列化、反序列化不一致,报错。(我知道你能将CTW放到公用常量类里去解决,我只想突出问题,说明这是过度设计)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章