如何理解Java设计模式中的单一职责原则?

我们在实际的项目过程中,如何理解Java设计模式的单一职责原则(SRP)?

我们今天要学习的是 SOLID 原则中的第一个原则:单一职责原则。

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

注意,这个原则描述的对象包含两个,一个是类(class),一个是模块(module)。关于这两个概念,在专栏中,有两种理解方式。一种理解是:把模块看作比类更加抽象的概念,类也可以看作模块。另一种理解是:把模块看作比类更加粗粒度的代码块,模块中包含多个类,多个类组成一个模块。

不管哪种理解方式,单一职责原则在应用到这两个描述对象的时候,道理都是相通的。为了方便你理解,接下来我只从 “类” 设计的角度,来讲解如何应用这个设计原则。对于 “模块” 来说,你可以自行引申。

单一职责原则的定义描述非常简单,也不难理解。一个类只负责完成一个职责或者功能。也就是说,不要设计大而全的类,要设计粒度小、功能单一的类。换个角度来讲就是,一个类包含了两个或者两个以上业务不相干的功能,那我们就说它职责不够单一,应该将它拆分成多个功能更加单一、粒度更细的类。

我举一个例子来解释一下。比如,一个类里既包含订单的一些操作,又包含用户的一些操作。而订单和用户是两个独立的业务领域模型,我们将两个不相干的功能放到同一个类中,那就违反了单一职责原则。为了满足单一职责原则,我们需要将这个类拆分成两个粒度更细、功能更加单一的两个类:订单类和用户类。

类的职责是否设计得越单一越好?
为了满足单一职责原则,是不是把类拆得越细就越好呢?答案是否定的。我们还是通过一个例子来解释一下。Serialization 类实现了一个简单协议的序列化和反序列功能,具体代码如下:


/**

 * 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);

  }

}
复制代码

如果我们想让类的职责更加单一,我们对 Serialization 类进一步拆分,拆分成一个只负责序列化工作的 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);

  }

}
复制代码

虽然经过拆分之后,Serializer 类和 Deserializer 类的职责更加单一了,但也随之带来了新的问题。如果我们修改了协议的格式,数据标识从 “UEUEUE” 改为 “DFDFDF”,或者序列化方式从 JSON 改为了 XML,那 Serializer 类和 Deserializer 类都需要做相应的修改,代码的内聚性显然没有原来 Serialization 高了。而且,如果我们仅仅对 Serializer 类做了协议修改,而忘记了修改 Deserializer 类的代码,那就会导致序列化、反序列化不匹配,程序运行出错,也就是说,拆分之后,代码的可维护性变差了。

实际上,不管是应用设计原则还是设计模式,最终的目的还是提高代码的可读性、可扩展性、复用性、可维护性等。我们在考虑应用某一个设计原则是否合理的时候,也可以以此作为最终的考量标准。

重点回顾
如何理解单一职责原则(SRP)?

一个类只负责完成一个职责或者功能。不要设计大而全的类,要设计粒度小、功能单一的类。单一职责原则是为了实现代码高内聚、低耦合,提高代码的复用性、可读性、可维护性。

更多原创阅读:javawu.com

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享