关于android:单一职责原则详解

48次阅读

共计 5927 个字符,预计需要花费 15 分钟才能阅读完成。

目录介绍

  • 00. 问题思考剖析
  • 01. 前沿根底介绍
  • 02. 如何了解繁多指摘
  • 03. 如何判断是否繁多
  • 04. 繁多判断准则
  • 05. 繁多就更好么
  • 06. 总结回顾一下

00. 问题思考剖析

  • 01. 如何了解类的繁多指摘,繁多指摘中这个繁多是如何评判的?
  • 02. 懂了,然而会用么,或者理论开发中有哪些使用,是否举例说明繁多职责劣势?
  • 03. 繁多指摘是否设计越繁多,越好呢?说出你的原因和论证的思路想法?
  • 04. 繁多职责准则,除了利用到类的设计上,还能延长到哪些其余设计方面吗?

01. 前沿根底介绍

  • 学习一些经典的设计准则,其中包含,SOLID、KISS、YAGNI、DRY、LOD 等。这些设计准则,从字面上了解,都不难。你一看就感觉懂了,一看就感觉把握了,但真的用到我的项目中的时候,你会发现,“看懂”和“会用”是两回事,而“用好”更是难上加难。从工作经验来看,很多共事因为对这些准则了解得不够透彻,导致在应用的时候过于教条主义,拿准则当真谛,生吞活剥,事与愿违。

02. 如何了解繁多指摘

  • 繁多职责准则的英文是 Single Responsibility Principle,缩写为 SRP。这个准则的英文形容是这样的:A class or module should have a single reponsibility。如果咱们把它翻译成中文,那就是: 一个类或者模块只负责实现一个职责(或者性能)
  • 留神,这个准则形容的对象蕴含两个,一个是类(class),一个是模块(module)。对于这两个概念,在专栏中,有两种了解形式。一种了解是:把模块看作比类更加形象的概念,类也能够看作模块。另一种了解是:把模块看作比类更加粗粒度的代码块,模块中蕴含多个类,多个类组成一个模块。
  • 不论哪种了解形式,繁多职责准则在利用到这两个形容对象的时候,情理都是相通的。为了不便你了解,接下来我只从“类”设计的角度,来解说如何利用这个设计准则。对于“模块”来说,你能够自行引申。
  • 繁多职责准则的定义形容非常简单,也不难理解。 一个类只负责实现一个职责或者性能。也就是说,不要设计大而全的类,要设计粒度小、性能繁多的类。换个角度来讲就是,一个类蕴含了两个或者两个以上业务不相干的性能,那咱们就说它职责不够繁多,应该将它拆分成多个性能更加繁多、粒度更细的类
  • 举一个例子来解释一下。比方,一个类里既蕴含订单的一些操作,又蕴含用户的一些操作。而订单和用户是两个独立的业务畛域模型,咱们将两个不相干的性能放到同一个类中,那就违反了繁多职责准则。为了满足繁多职责准则,咱们须要将这个类拆分成两个粒度更细、性能更加繁多的两个类:订单类和用户类。
  • 举一个例子来解释一下。比方,一个类里既蕴含订单的一些操作,又蕴含用户的一些操作。而订单和用户是两个独立的业务畛域模型,咱们将两个不相干的性能放到同一个类中,那就违反了繁多职责准则。为了满足繁多职责准则,咱们须要将这个类拆分成两个粒度更细、性能更加繁多的两个类:订单类和用户类。

03. 如何判断是否繁多

  • 从刚刚这个例子来看,繁多职责准则看似不难利用。那是因为我举的这个例子比拟极其,一眼就能看出订单和用户毫不相干。但大部分状况下,类里的办法是归为同一类性能,还是归为不相干的两类性能,并不是那么容易断定的。在实在的软件开发中,对于一个类是否职责繁多的断定,是很难拿捏的。我举一个更加贴近理论的例子来给你解释一下。
  • 在一个社交产品中,咱们用上面的 UserInfo 类来记录用户的信息。你感觉,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 类蕴含的都是跟用户相干的信息,所有的属性和办法都隶属于用户这样一个业务模型,满足繁多职责准则;
    • 另一种观点是,地址信息在 UserInfo 类中,所占的比重比拟高,能够持续拆分成独立的 UserAddress 类,UserInfo 只保留除 Address 之外的其余信息,拆分之后的两个类的职责更加繁多。
  • 哪种观点更对呢?实际上,要从中做出抉择,咱们不能脱离具体的利用场景。

    • 如果在这个社交产品中,用户的地址信息跟其余信息一样,只是单纯地用来展现,那 UserInfo 当初的设计就是正当的。
    • 如果这个社交产品倒退得比拟好,之后又在产品中增加了电商的模块,用户的地址信息还会用在电商物流中,那咱们最好将地址信息从 UserInfo 中拆分进去,独立成用户物流信息(或者叫地址信息、收货信息等)。
  • 咱们再进一步延长一下。如果做这个社交产品的公司倒退得越来越好,公司外部又开发出了跟多其余产品(能够了解为其余 App)。公司心愿反对对立账号零碎,也就是用户一个账号能够在公司外部的所有产品中登录。这个时候,咱们就须要持续对 UserInfo 进行拆分,将跟身份认证相干的信息(比方,email、telephone 等)抽取成独立的类。
  • 从刚刚这个例子,咱们能够总结出,不同的利用场景、不同阶段的需要背景下,对同一个类的职责是否繁多的断定,可能都是不一样的。在某种利用场景或者当下的需要背景下,一个类的设计可能曾经满足繁多职责准则了,但如果换个利用场景或着在将来的某个需要背景下,可能就不满足了,须要持续拆分成粒度更细的类。
  • 除此之外,从不同的业务层面去对待同一个类的设计,对类是否职责繁多,也会有不同的意识。比方,例子中的 UserInfo 类。如果咱们从“用户”这个业务层面来看,UserInfo 蕴含的信息都属于用户,满足职责繁多准则。如果咱们从更加细分的“用户展现信息”“地址信息”“登录认证信息”等等这些更细粒度的业务层面来看,那 UserInfo 就应该持续拆分。
  • 综上所述,评估一个类的职责是否足够繁多,咱们并没有一个十分明确的、能够量化的规范,能够说,这是件十分主观、仁者见仁智者见智的事件。实际上,在真正的软件开发中,咱们也没必要过于防患未然,适度设计。所以,咱们能够先写一个粗粒度的类,满足业务需要。随着业务的倒退,如果粗粒度的类越来越宏大,代码越来越多,这个时候,咱们就能够将这个粗粒度的类,拆分成几个更细粒度的类。这就是所谓的继续重构(前面的章节中咱们会讲到)。

04. 繁多判断准则

  • 听到这里,你可能会说,这个准则如此含糊不清、不置可否,到底该如何拿捏才好啊?我这里还有一些小技巧,可能很好地帮你,从侧面上断定一个类的职责是否够繁多。而且,我集体感觉,上面这几条判断准则,比起很主观地去思考类是否职责繁多,要更有指导意义、更具备可执行性:

    • 类中的代码行数、函数或属性过多,会影响代码的可读性和可维护性,咱们就须要思考对类进行拆分;
    • 类依赖的其余类过多,或者依赖类的其余类过多,不合乎高内聚、低耦合的设计思维,咱们就须要思考对类进行拆分;
    • 公有办法过多,咱们就要思考是否将公有办法独立到新的类中,设置为 public 办法,供更多的类应用,从而进步代码的复用性;
    • 比拟难给类起一个适合名字,很难用一个业务名词概括,或者只能用一些抽象的 Manager、Context 之类的词语来命名,这就阐明类的职责定义得可能不够清晰;
    • 类中大量的办法都是集中操作类中的某几个属性,比方,在 UserInfo 例子中,如果一半的办法都是在操作 address 信息,那就能够思考将这几个属性和对应的办法拆分进去。
  • 不过,你可能还会有这样的疑难:在下面的断定准则中,我提到类中的代码行数、函数或者属性过多,就有可能不满足繁多职责准则。那多少行代码才算是行数过多呢?多少个函数、属性才称得上过多呢?

    • 比拟高级的工程师常常会问这类问题。实际上,这个问题并不好定量地答复,就像你问大厨“放盐少许”中的“少许”是多少,大厨也很难通知你一个特地具体的量值。
  • 实际上,从另一个角度来看,当一个类的代码,读起来让你头大了,实现某个性能时不晓得该用哪个函数了,想用哪个函数翻半天都找不到了,只用到一个小性能要引入整个类(类中蕴含很多无关此性能实现的函数)的时候,这就阐明类的行数、函数、属性过多了。实际上,等你做多我的项目了,代码写多了,在开发中缓缓“品味”,天然就晓得什么是“放盐少许”了,这就是所谓的“业余第六感”。

05. 繁多就更好么

  • 为了满足繁多职责准则,是不是把类拆得越细就越好呢?答案是否定的。咱们还是通过一个例子来解释一下。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 类的代码,那就会导致序列化、反序列化不匹配,程序运行出错,也就是说,拆分之后,代码的可维护性变差了。

06. 总结回顾一下

  • 1. 如何了解繁多职责准则(SRP)?

    • 一个类只负责实现一个职责或者性能。不要设计大而全的类,要设计粒度小、性能繁多的类。繁多职责准则是为了实现代码高内聚、低耦合,进步代码的复用性、可读性、可维护性。
  • 2. 如何判断类的职责是否足够繁多?

    • 不同的利用场景、不同阶段的需要背景、不同的业务层面,对同一个类的职责是否繁多,可能会有不同的断定后果。实际上,一些侧面的判断指标更具备指导意义和可执行性,比方,呈现上面这些状况就有可能阐明这类的设计不满足繁多职责准则:
    • 类中的代码行数、函数或者属性过多;
    • 类依赖的其余类过多,或者依赖类的其余类过多;
    • 公有办法过多;
    • 比拟难给类起一个适合的名字;
    • 类中大量的办法都是集中操作类中的某几个属性。
  • 3. 类的职责是否设计得越繁多越好?

    • 繁多职责准则通过防止设计大而全的类,防止将不相干的性能耦合在一起,来进步类的内聚性。同时,类职责繁多,类依赖的和被依赖的其余类也会变少,缩小了代码的耦合性,以此来实现代码的高内聚、低耦合。然而,如果拆分得过细,实际上会事与愿违,反倒会升高内聚性,也会影响代码的可维护性。

开源我的项目:https://github.com/yangchong2…

开源博客:https://github.com/yangchong2…

正文完
 0