企业网站设计制作教程,腾讯云网站建设流程图,网站开发公司 杭州,wordpress faq模板前言
前面我们提到过 SOLID 原则#xff0c;实际上 SOLID 由 5 个设计原则组成#xff0c;分别是#xff1a;单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖反转原则。它们分别对应 SLOID 中的 S、O、L、I、D 这 5 个英文字母。
今天来学习下 SOLID 原则中的第…前言
前面我们提到过 SOLID 原则实际上 SOLID 由 5 个设计原则组成分别是单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖反转原则。它们分别对应 SLOID 中的 S、O、L、I、D 这 5 个英文字母。
今天来学习下 SOLID 原则中的第一个原则单一职责原则。 如何理解单一职责原则SRP
单一职责原则 Single Responsibility Principle缩写为 SRP。 英文原文的描述A class or module should have a single responsibility。翻译成中文就是一个类或模块只负责完成一个职责或功能。 注意这个原则描述的对象包含两个一个是类一个是模块。不管是哪个对象单一职责在应用到这两个面对对象的时候道理都是相通的。接下来我会只从“类”设计的角度来讲解如何应用这个原则。
一个类只负责完成一个职责或者功能。也就是说不要设计大而全的类要设计粒度小、功能单一的类。要把大而全的类拆分成多个功能更加单一、粒度更细的类。 换个角度来说一个类包含了两个或者两个以上业务不相干的功能就不符合单一职责原则。 例如一个类里面包含订单的一些操作又包含用户的一些操作。而订单和用户是两个独立的业务领域模型我们将两个不相干的功能放到同一个类中就违反了单一职责原则。为了满足单一职责原则需要将这个类拆分成两个粒度更细、功能更单一的两个类订单类和用户类。
如何判断类的职责是否足够单一
从刚刚的例子来看单一职责原则看似不难应用。那是因为我举的例子比较极端一眼就能看出订单和用户毫不相干。但大部分情况下类里的方法是归类为同一类功能还是归为不相关的两类功能并不是那么容易判定的。在真实的软件开发中对一个类是否职责单一的判定是很难拿捏的。
在一个社交产品中用下面的 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 detailOfAddress; // 详细地址// 省略其他属性和方法...
}对于这个问题有两种不同的观点。
一种观点是UserInfo 类包含的都是跟用户相关的信息所有的属性和方法都隶属于用户这样一个业务模型满足单一职责原则另一种观点是地址信息在 UserInfo 类中所占比重较高可以继续拆分成独立的 UserAddressUserInfo 只保留出 UserAddress 之外的属性拆分之后的两个类的职责更加单一。
实际上要做出选择我们不能脱离具体的应用常见。如果在这个社交产品中用户地址信息和其他信息一样只是单纯地用于展示那 UserInfo 现在的设计就是合理的。但是如果这个社交产品发展地比较好之后又在产品中添加了电商的模块用户的地址信息还会用在电商物流中那我们最好将地址信息从 UserInfo 中拆分出来独立成用户物流信息。
再进一步延伸下如果做这个社交产品的公司发展地越来越好公司内部有开发出了很多其他产品。公司希望支持统一账号系统也就是用户一个账号可以在公司内的所有产品中登录。这个时候我们就需要对 UserInfo 进行拆分将跟身份认证相关的信息比如 email、telephone 等抽取成独立的类。
从上面的例子可以总结出在不同的应用场景、不同阶段的需求背景下对同一个类的职责是否单一的判定都是不一样的。在某种应用场景或者当下的需求背景下一个类的设计可能符合单一职责原则了但是如果换个应用场景或者未来的某个需求背景下可能就不满足了需要继续拆分成粒度更细的类。
此外从不同的业务层面去看待同一个类的设计对类是否职责单一也会有不同的认识。比如例子用的 UserInfo 类。如果从“用户”这个业务层面来看UserInfo 包含的信息都属于用户满足单一职责原则。如果从更加细分的“用户展示信息” “地址信息” “登录认证信息”等等这些细细粒度的业务层面来看那 UserInfo 就应该继续拆分。
综上所述评价一个类的职责是否足够单一我们并没有非常明确的、可以量化的标准。实际上在真正的软件开发中也没必要过于未雨绸缪过度设计。所以我们可以先写一个粗粒度的类满足业务需求。随着业务的发展如果粗粒度的类越来越大代码越来越多这个时候我们就可以将这个粗粒度的类拆分成几个更细粒度的类。这个就是所谓的重构。
这里还有些小技巧能够辅助你从侧面判断一个类的职责是否足够单一
类中的代码行数、函数或属性过多会影响代码的可读性和可维护性我们就需要考虑读类进行拆分。类依赖的其他类过多或者依赖的类的其他类过多不符合高内聚、低耦合的设计思想就需要考虑拆分。私有方法过多就要考虑是否将私有方法独立到新的类中设置为 public 方法供更多的类适用从而提高代码的复用性。比较难给类起合适的名字很难用一个业务名词概括或者只能用一些笼统的 Manager 、Context 之类的词语来命名这就说明类的职责定义得可能不够清楚。类中的大量方法都是集中操作类中的某几个属性比如在 UserInfo 中如果有一办的方法都是在操作 address 信息那就可以考虑将这几个属性和对应的方法拆分出来。
不过你可能有这样的疑问在上面的小技巧中提到的类的代码行数、函数或者属性过多就有可能不满足单一职责原则。那多少行代码才算是行数过多呢多少个函数、属性才称得上过多呢
实际上这个问题很不好定量的回答。实际上可以给你一个凑活能用的、比较宽泛的、可量化的标准那就是一个类的代码行数最好不能超过 200 行函数及属性的个数最好不要超过 10 个。
实际上从另一种角度来看当一个类的代码读起来让你头大了实现某个功能时不知道该用哪个函数了想用哪个函数翻半天找不到了只能用到一个小功能却要引入整个类的时候这说明类的行数、函数、属性过多了。
类的职责是否设计地越单一越好
为了满足单一职责是不是把类拆得越细就越好呢答案是否定的。通过一个例子来解释下。 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(MapString, String object) {StringBuilder textBuilder new StringBuilder();textBuilder.append(IDENTIFIER_STRING);textBuilder.append(gson.toJson(object));return textBuilder.toString();}public MapString, 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(MapString, 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 MapString, 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 类那就会导致序列化和反序列化不匹配程序运行出错拆分之后代码的可维护性变差了。
实际上不管是应用设计原则还是设计模式最终的目的还是提高代码的可读性、可扩展性、可维护性、复用性等。我们在考虑应用某一设计原则是否合理的时候也可以以此作为最终的考量标准。
回顾
1.如何理解单一职责原则SRP
一个类只负责完成一个职责或功能。不要设计大而全的类要设计粒度小、功能单一的类。单一职责原则是为了提高代码的高内聚、低耦合提高代码的复用性、可读性、可维护性。
2.如何判断类的职责是否足够单一
不同的应用场景、不同阶段的需求、不同的业务背景对同一个类的职责是否单一可能会有不同的判定结果。实际上一些侧面的指标更具有指导意义和可执行性比如出现下面这些情况就有可能说明这个类的设计不满足单一职责原则
类中的代码行数、函数、属性过多。类依赖的其他类过多或者依赖类的其他类过多。私有方法过多。比较难给一个类起一个合适的名字。类中的大量方法都是集中操作类中的几个属性。
3.类的职责是否设计的越细越好
单一职责原则通过避免设计大而全的类避免将不相关的功能聚合在一起来提高类的高内聚性。同时类职责单一类依赖和被依赖的其他类也会变少减少了代码的耦合性以此来实现代码的高内聚、低耦合。但是如果拆分地过细实际上会适得其反反倒会降低代码的内聚性也会影响代码的可维护性。