设计模式系列:单一职责原则
问题由来??问题由来:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。 ??解决方案:遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。 ??但是即便是经验丰富的程序员写出的程序,也会有违背这一原则的代码存在。为什么会出现这种现象呢?因为有职责扩散。所谓职责扩散,就是因为某种原因,职责P被分化为粒度更细的职责P1和P2。 ??比如:类T只负责一个职责P,这样设计是符合单一职责原则的。后来由于某种原因,也许是需求变更了,也许是程序的设计者境界提高了,需要将职责P细分为粒度更细的职责P1,P2,这时如果要使程序遵循单一职责原则,需要将类T也分解为两个类T1和T2,分别负责P1、P2两个职责。但是在程序已经写好的情况下,这样做简直太费时间了。所以,简单的修改类T,用它来负责两个职责是一个比较不错的选择,虽然这样做有悖于单一职责原则。(这样做的风险在于职责扩散的不确定性,因为我们不会想到这个职责P,在未来可能会扩散为P1,P2,P3,P4……Pn。所以记住,在职责扩散到我们无法控制的程度之前,立刻对代码进行重构。) 定义
对职责的解释
如何识别破坏了单一职责原则?? 类有太多依赖 ? 方法有太多参数 ? 测试类变得复杂 ? 类或方法太长 ? 描述性名称 ? 低聚合Cohesion的类 ? 在一个地方改动影响另外一个地方 ? 猎枪效果Shotgun Effect ? 不能够封装模块 优缺点优点: 缺点: 案例例1:举例说明,用一个类描述动物呼吸这个场景: class Animal{
public void breathe(String animal){
System.out.println(animal+"呼吸空气");
}
}
public class Client{
public static void main(String[] args){
Animal animal = new Animal();
animal.breathe("牛");
animal.breathe("羊");
animal.breathe("猪");
}
}
运行结果:
牛呼吸空气
羊呼吸空气
猪呼吸空气
??程序上线后,发现问题了,并不是所有的动物都呼吸空气的,比如鱼就是呼吸水的。修改时如果遵循单一职责原则,需要将Animal类细分为陆生动物类Terrestrial,水生动物Aquatic,代码如下: class Terrestrial{
public void breathe(String animal){
System.out.println(animal+"呼吸空气");
}
}
class Aquatic{
public void breathe(String animal){
System.out.println(animal+"呼吸水");
}
}
public class Client{
public static void main(String[] args){
Terrestrial terrestrial = new Terrestrial();
terrestrial.breathe("牛");
terrestrial.breathe("羊");
terrestrial.breathe("猪");
Aquatic aquatic = new Aquatic();
aquatic.breathe("鱼");
}
}
运行结果:
牛呼吸空气
羊呼吸空气
猪呼吸空气
鱼呼吸水
??我们会发现如果这样修改花销是很大的,除了将原来的类分解之外,还需要修改客户端。而直接修改类Animal来达成目的虽然违背了单一职责原则,但花销却小的多,代码如下: class Animal{
public void breathe(String animal){
if("鱼".equals(animal)){
System.out.println(animal+"呼吸水");
}else{
System.out.println(animal+"呼吸空气");
}
}
}
public class Client{
public static void main(String[] args){
Animal animal = new Animal();
animal.breathe("牛");
animal.breathe("羊");
animal.breathe("猪");
animal.breathe("鱼");
}
}
??可以看到,这种修改方式要简单的多。但是却存在着隐患:有一天需要将鱼分为呼吸淡水的鱼和呼吸海水的鱼,则又需要修改Animal类的breathe方法,而对原有代码的修改会对调用“猪”“牛”“羊”等相关功能带来风险,也许某一天你会发现程序运行的结果变为“牛呼吸水”了。这种修改方式直接在代码级别上违背了单一职责原则,虽然修改起来最简单,但隐患却是最大的。还有一种修改方式: class Animal{
public void breathe(String animal){
System.out.println(animal+"呼吸空气");
}
public void breathe2(String animal){
System.out.println(animal+"呼吸水");
}
}
public class Client{
public static void main(String[] args){
Animal animal = new Animal();
animal.breathe("牛");
animal.breathe("羊");
animal.breathe("猪");
animal.breathe2("鱼");
}
}
??可以看到,这种修改方式没有改动原来的方法,而是在类中新加了一个方法,这样虽然也违背了单一职责原则,但在方法级别上却是符合单一职责原则的,因为它并没有动原来方法的代码。这三种方式各有优缺点,那么在实际编程中,采用哪一中呢?其实这真的比较难说,需要根据实际情况来确定。 例2:男生一般是站着尿尿,所以代码是:
function Male (name,age) {
this.name = name;
this.age = age;
this.sex = 'male';
}
Male.prototype = {
coustructor: Male,//尿尿的行为
pee: function () {
console.log('站着尿尿');
}
};
女生一般是蹲着尿尿,所以代码是:
function FeMale (name,age) {
this.name = name;
this.age = age;
this.sex = 'female';
}
FeMale.prototype = {
coustructor: FeMale,//尿尿的行为
pee: function () {
console.log('蹲着尿尿');
}
};
所以结果男生lilei是站着尿尿的,女生hanmeimei是蹲着尿尿的 var lilei = new Male('lilei',20);
//站着尿尿
lilei.pee();
var hanmeimei = new FeMale('hanmeimei',20);
//蹲着尿尿
hanmeimei.pee();
这一切是不是看起来好像很完美? 但是lilei虽然是男生,但是他却喜欢蹲着尿尿(或者都喜欢女生行为),这时候lilei的行为就与自己的性别产生了耦合,其实性别与行为分别负责不同的职责 /** * 人类的基类 * @param person: 人类的各种属性,包括姓名、年龄、性别等 * behavior: 人类的行为 */
var Human = function (person,behavior) {
this.person = person;
this.behavior = behavior;
}
//人的属性
var Person = function (name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//行为类
var Behavior = function () {
}
Behavior.prototype = {
courstructor: Behavior,//尿尿的行为
pee: function () {
}
};
//一般男生的行为
var MaleBehaior = function (){
}
MaleBehaior.prototype = Object.create(Behavior.prototype,{
pee: {
writable:true,configurable:true,value: function () {
console.log('站着尿尿');
}
}
});
//一般女生的行为
var FeMaleBehaior = function (){
}
FeMaleBehaior.prototype = Object.create(Behavior.prototype,value: function () {
console.log('蹲着尿尿');
}
}
});
基本类都构造完毕了,现在看下lilei是怎么实例出来的: var lilei = new Human(new Person('lilei',20,'male'),new FeMaleBehaior());
//此时,lilei就是蹲着尿尿的,lilei虽然是男生,但是他喜欢女生的各种行为。
lilei.behavior.pee();
var hanleilei = new Human(new Person('lilei','female'),new FeMaleBehaior());
//hanleilei是萌妹子
hanleilei.behavior.pee();
var peter = new Human(new Person('lilei',new MaleBehaior());
//perter是纯爷们
perter.behavior.pee();
此时,职责就分明了,Person负责个人的基本属性,Behaior负责行为,各自直接就完全解耦了。 虽然上面例子解耦了属性与行为,但是如果粗粒度来分析,Male类与Female类其实也符合单一职责原则,Male负责男性个体,Female负责女性个体,并没有违反单一职责原则,所以我们在开发的时候,单一职责原则不能死搬书上的例子,要根据项目的大小与计算以后维护成本来衡量是否要更细粒度的解耦,实现粒度更细的单一职责。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- c++中的break,continue,goto语句
- const,readonly 这些你真的懂吗? 也许会被面试到哦。。。
- dspace安装及应用
- 一日一点RakNet(51)--SQLite3LoggerPlugin设置
- 使用React.js 重新构建HipChat
- xcode – com.apple.iokit.CHUDKernLib内核恐慌修复
- vue3.0 CLI - 2.4 - 新组件 Forms.vue 中学习表单
- Sqlite3内置函数(转自:http://www.cnblogs.com/igouz/arc
- 41.Oracle深度学习笔记——shared_pool底层知识大集合
- 如何在iOS中以编程方式捕获__cxa_throw