wordpress 免费企业网站 模板下载,免费软件库下载,佛山网站seo哪家好,wordpress主题页面如何添加vc-container设计模式之六大原则
这篇博客非常有意义#xff0c;希望自己能够理解的基础上#xff0c;在实际开发中融入这些思想#xff0c;运用里面的精髓。
先列出六大原则#xff1a;单一职责原则、里氏替换原则、接口隔离原则、依赖倒置原则、迪米特原则、开闭原则。
一、单一职…设计模式之六大原则
这篇博客非常有意义希望自己能够理解的基础上在实际开发中融入这些思想运用里面的精髓。
先列出六大原则单一职责原则、里氏替换原则、接口隔离原则、依赖倒置原则、迪米特原则、开闭原则。
一、单一职责原则
1、单一职责定义
单一职责原则一个类只负责一个功能领域中的相应职责或者可以定义为就一个类而言应该只有一个引起它变化的原因。
单一职责原则告诉我们一个类不能太“累”在软件系统中一个类承担的职责越多它被复用的可能性就越小而且一个类承担的职责过多就相当于将这些职责耦合在一起当其中一个职责
变化时可能会影响其他职责的运作因此要将这些职责进行分离将不同的职责封装在不同的类中即将不同的变化原因封装在不同的类中如果多个职责总是同时发生改变则可将它们封装在同一类中。
2、单一职责优点 1降低了类的复杂度。一个类只负责一项职责比负责多项职责要简单得多。 2) 提高了代码的可读性。一个类简单了可读性自然就提高了。 3) 提高了系统的可维护性。代码的可读性高了并且修改一项职责对其他职责影响降低了可维护性自然就提高了。 4) 变更引起的风险变低了。单一职责最大的优点就是修改一个功能对其他功能的影响显著降低。
3、案例说明
在网上找了个比较好理解也比较符合实际开发中用来思考的小案例。
有一个用户类我们先看它的接口 这个接口是可以优化的用户的属性Property和用户的行为Behavior没有分开这是一个严重的错误非常正确这个接口确实设计得一团糟应该把用户的信息抽取成一个BOBussiness Object业务对象把行为抽取成一个BIZ(Business Logic业务逻辑)按照这个思路对类图进行修正如图1-2所示。 图1-2 职责划分后的类图
重新拆封成两个接口IUserBO负责用户的属性简单地说IUserBO的职责就是收集和反馈用户的属性信息IUserBiz负责用户的行为完成用户信息的维护和变更。
然后IUserInfo来实现这两个接口重写方法。 代码清单1-1 分清职责后的代码示例
.......IUserBiz userInfo new UserInfo();//我要赋值了我就认为它是一个纯粹的BOIUserBO userBO (IUserBO)userInfo;userBO.setPassword(abc);//我要执行动作了我就认为是一个业务逻辑类IUserBiz userBiz (IUserBiz)userInfo;userBiz.deleteUser();.......
思考上面这样是单一职责原则吗当然不是了你实现了两个接口不还是把行为和属性写在一个类了和最上面又有什么区别呢这里只能说实现了接口隔离原则下面会说
那如何来确保单一原则在实际的使用中我们更倾向于使用两个不同的类一个是IUserBO 一个是IUserBiz很简单如图所示 4、自己理解
单一职责原则有两个难点 1) 职责划分 一个职责一个接口但问题是“职责”是一个没有量化的标准一个类到底要负责那些职责这些职责该怎么细化细化后是否都要有一个接口或类这些都需要从实际的项目去考虑。
比如上面写成一个类他的单一职责就是修改用户信息为什么一定要分修改行为和修改属性。那是不是又可以在细分修改密码和修改属性呢 2类的冗余
如果可以追求单一职责也是没有必要的本来一个类可以搞定的实现如果非得修改用户名一个类修改密码一个类来实现单一原则这样也会让你的类变得非常多反而不容易维护。
我自己的感悟 1首先要培养单一职责的思想特别是如果代码可以复用的情况下经常思考能不能用单一职责原则来划分类。 2) 类的单一职责实现在好多时候并不切实际但是方法上一定要保持单一职责原则。比如你修改密码的方法就是用来修改密码。这样做有个很大的好处就是便于代码调试容易将代码的Bug找出来一个方法只完成
一件事情相对调试能简单很多让其他人员能更快更好的读懂代码、理解这个类或者方法的功能。
二、里氏代换原则 这个和单一职责原则比起来显然就好理解多了而且也不那么模糊不清。
1、定义 官方定义所有引用基类父类的地方必须能透明地使用其子类的对象。 简单理解就是子类一般不该重写父类的方法因为父类的方法一般都是对外公布的接口是具有不可变性的你不该将一些不该变化的东西给修改掉。
是不是感觉这个原则不太招人喜欢因为我们在写代码的时候经常会去重写父类的方法来满足我们的需求。而且在模板方法模式缺省适配器装饰器模式等一些设计模式都会采用重写父类的方法。
怎么说呢里氏代换原则的主要目的主要是防止继承所带来的弊端。
继承的弊端
继承作为面向对象三大特性之一在给程序设计带来巨大便利的同时也带来了弊端。
继承会增加了对象间的耦合性如果一个类被其他的类所继承则当这个类需要修改时必须考虑到所有的子类并且父类修改后所有涉及到子类的功能都有可能会产生故障。
2、案例说明
SomeoneClass类其中有一个方法调用了某一个父类的方法。
//某一个类
public class SomeoneClass {//有某一个方法使用了一个父类类型public void someoneMethod(Parent parent){parent.method();}
}
父类代码
public class Parent {public void method(){System.out.println(parent method);}
}
SubClass子类把父类的方法给覆盖。
public class SubClass extends Parent{//结果某一个子类重写了父类的方法说不支持该操作了public void method() {throw new UnsupportedOperationException();}}
测试类
/**这个异常是运行时才会产生的也就是说我的SomeoneClass并不知道会出现这种情况结果就是我调用下面这段代码的时候*本来我们的思维是Parent都可以传给someoneMethod完成我的功能我的SubClass继承了Parent当然也可以了但是最终这个调用会抛出异常。*/
public class Client {public static void main(String[] args) {SomeoneClass someoneClass new SomeoneClass();someoneClass.someoneMethod(new Parent());someoneClass.someoneMethod(new SubClass());}
}
这就相当于埋下了一个个陷阱因为本来我们的原则是父类可以完成的地方我用子类替代是绝对没有问题的但是这下反了我每次使用一个子类替换一个父类的时候我还要担心这个
子类有没有给我埋下一个上面这种炸弹。
3、自己理解 感觉自己在开发中不太会出现上面这么愚蠢的错误。理由 1自己水平有限平时在开发中使用继承的时候都是基础API的类然后重写很少继承自己写的类一般都是实现接口比较多。 2第二就算我用了继承我在传参的时候我只要稍微注意下就应该知道这个方法的参数是Parent而如果我要放入SubClass时就应该考虑自己有没有重写这个方法如果重写这样肯定不行。所以也不多发生上面的错误了。
所以总的来说要知道继承的这个隐患在开发中注意就是。 三、接口隔离原则
1、定义 当一个接口太大时我们需要将它分割成一些更细小的接口使用该接口的客户端仅需知道与之相关的方法即可。
为什么要这么做呢 其实很好理解因为你实现一个接口就是实现它所有的方法但其实你并不需要它的所有方法那就会产生一个类实现了一个接口里面很多方法都是空着的只有个别几个方法实现了。 这样做不仅会强制实现的人不得不实现本来不该实现的方法最严重的是会给使用者造成假象即这个实现类拥有接口中所有的行为结果调用方法时却没收获到想要的结果。
2、案例说明 比如我们设计一个手机的接口时就要手机哪些行为是必须的要让这个接口尽量的小或者通俗点讲就是里面的行为应该都是这样一种行为就是说只要是手机你就必须可以做到的。
下面是手机接口。
public interface Mobile {public void call();//手机可以打电话public void sendMessage();//手机可以发短信public void playBird();//手机可以玩愤怒的小鸟}
上面第三个行为明显就不是一个手机必须有的那么上面这个手机的接口就不是最小接口假设我现在的非智能手机去实现这个接口那么playBird方法就只能空着了因为它不能玩。
3、自己理解
这个没啥说的很好理解最上面我写单一职责原则的时候的那个案例中间那部分就是接口隔离原则。这个思想自己要慢慢培养然后更多的运用到实际开发中去。 四、依赖倒置原则
1、定义
依赖倒置原则包含三个含义 1 高层模块不应该依赖低层模块,两者都应该依赖其抽象 2 抽象不应该依赖细节 3细节应该依赖抽象
2、案例说明
大家都喜欢阅读阅读文学经典滋润自己的内心心灵下面是小明同学阅读文学经典的一个类图 文学经典类
//文学经典类
public class LiteraryClassic{//阅读文学经典public void read(){System.out.println(文学经典阅读滋润自己的内心心灵);}
}
小明类
//小明类
public class XiaoMing{//阅读文学经典public void read(LiteraryClassic literaryClassic){literaryClassic.read();}
}
场景类
public class Client{public static void main(Strings[] args){XiaoMing xiaoming new XiaoMing();LiteraryClassic literaryClassic new LiteraryClassic();//小明阅读文学经典xiaoming.read(literaryClassic);}}
看我们的实现小明同学可以阅读文学经典了。
小明同学看了一段文学经典后忽然他想看看看小说来放松一下自己,我们实现一个小说类
小说类
//小说类
public class Novel{//阅读小说public void read(){System.out.println(阅读小说放松自己);}
}
现在我们再来看代码发现XiaoMing类的read方法只与文学经典LiteraryClassic类是强依赖紧耦合关系小明同学竟然阅读不了小说类。这与现实明显的是不符合的代码设计的是有问题的。那么问题在那里呢 我们看小明类此类是一个高层模块并且是一个细节实现类此类依赖的是一个文学经典LiteraryClassic类而文学经典LiteraryClassic类也是一个细节实现类。这是不是就与我们说的依赖倒置原则相违背呢
依赖倒置原则是说我们的高层模块实现类细节类都应该是依赖与抽象依赖与接口和抽象类。
为了解决小明同学阅读小说的问题我们根据依赖倒置原则先抽象一个阅读者接口,下面是完整的uml类图 IReader接口
public interface IReader{//阅读public void read(IRead read){read.read();}}
再定义一个被阅读的接口IRead
public interface IRead{//被阅读public void read();
}
再定义文学经典类和小说类
文学经典类
//文学经典类
public class LiteraryClassic implements IRead{//阅读文学经典public void read(){System.out.println(文学经典阅读滋润自己的内心心灵);}
}
小说类
//小说类
public class Novel implements IRead{//阅读小说public void read(){System.out.println(阅读小说放松自己);}
}
再实现小明类
//小明类
public class XiaoMing implements IReader{//阅读public void read(IRead read){read.read();}
}
然后我们再让小明分别阅读文学经典和小说
public class Client{public static void main(Strings[] args){XiaoMing xiaoming new XiaoMing();IRead literaryClassic new LiteraryClassic();//小明阅读文学经典xiaoming.read(literaryClassic);IRead novel new Novel();//小明阅读小说xiaoming.read(novel);}
}
至此小明同学是可以阅读文学经典又可以阅读小说了目的达到了。 为什么依赖抽象的接口可以适应变化的需求这就要从接口的本质来说接口就是把一些公司的方法和属性声明然后具体的业务逻辑是可以在实现接口的具体类中实现的。所以我们当依赖
对象是接口时就可以适应所有的实现此接口的具体类变化。
3、依赖的三种方法
依赖是可以传递A对象依赖B对象B又依赖CC又依赖D……依赖不止。只要做到抽象依赖即使是多层的依赖传递也无所谓惧。
1构造函数传递依赖对象
在类中通过构造函数声明依赖对象按照依赖注入的说法这种方式叫做构造函数注入
//小明类
public class XiaoMing implements IReader{private IRead read;//构造函数注入public XiaoMing(IRead read){this.read read;}//阅读public void read(){read.read();}
}
2Setter方法传递依赖对象
在类中通过Setter方法声明依赖关系依照依赖注入的说法这是Setter依赖注入
//小明类
public class XiaoMing implements IReader{private IRead read;//Setter依赖注入public setRead(IRead read){this.read read;}//阅读public void read(){read.read();}
}
3接口声明依赖
在接口的方法中声明依赖对象在为什么我们要符合依赖倒置原则的例子中我们采用了接口声明依赖的方式该方法也叫做接口注入。
4、依赖倒置原则的经验
依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立不互相影响实现模块间的松耦合。我们在项目中使用这个原则要遵循下面的规则 1每个类尽量都有接口或者抽象类或者抽象类和接口两都具备 2变量的表面类型尽量是接口或者抽象类 3任何类都不应该从具体类派生 4尽量不要覆写基类的方法 如果基类是一个抽象类而这个方法已经实现了子类尽量不要覆写。类间依赖的是抽象覆写了抽象方法对依赖的稳定性会有一定的影响。 5结合里氏替换原则使用
依赖倒置原则是6个设计原则中最难以实现的原则它是实现开闭原则的重要方法在项目中大家只要记住是”面向接口编程”就基本上是抓住了依赖倒置原则的核心了。 五、迪米特原则 这个原则在开发中还是非常有用的。
1、定义
大致意思是即一个类应该尽量不要知道其他类太多的东西不要和陌生的类有太多接触。
迪米特原则还有一个解释Only talk to your immediate friends只与直接朋友通信。
什么叫直接朋友呢每个对象都必然会与其他对象有耦合关系两个对象之间的耦合就成为朋友关系这种关系类型有很多例如组合聚合依赖等。朋友类也可以这样定义出现在成员
变量方法的输入输出参数中的类称为朋友类。
2、案例说明
上体育课我们经常有这样一个场景
体育老师上课前要体育委员确认一下全班女生到了多少位也就是体育委员清点女生的人数。如图 分析这里其实体育老师和体育委员是朋友因为他们是有业务来源而女生人数是和体育委员有业务来源它们是朋友但是体育老师和女生人数是没有直接业务来源的所以体育老师类中
不应该参杂女生相关信息这就是迪米特原则
(1)没有才有迪米特原则
体育老师类
public class Teacher{//老师对体育委员发一个命令让其清点女生人数的方法public void command(GroupLeader groupLeader){ListGirl listGirls new ArrayList();//初始化女生发现老师和女生有耦合for(int i0;i20;i){listGirls.add(new Girl());}//告诉体育委员开始清点女生人数groupLeader.countGirls(listGirls);}
}
体育委员类
public class GroupLeader{//清点女生数量public void countGirls(ListGirl listGirls){System.out.println(女生人数是listGirls.size());}
}
女生类
publci class Girl{
}
测试类
public class Client{public static void main(Strings[] args){Teacher teacher new Teacher();//老师给体育委员发清点女生人数的命令teacher.command(new GroupLeader());}
}
分析我们再回头看Teacher类Teacher类只有一个朋友类GroupLeaderGirl类不是朋友类但是Teacher与Girl类通信了这就破坏了Teacher类的健壮性Teacher类的方法竟然与一个不是
自己的朋友类Girl类通信这是不允许的严重违反了迪米特原则。 (2)采用迪米特原则
我们对程序进行如下修改将类图修改如下 修改后的老师类注意这里面已经没有女生信息了
public class Teacher{//老师对体育委员发一个命令让其清点女生人数public void command(GroupLeader groupLeader){//告诉体育委员开始清点女生人数groupLeader.countGirls();}
} 修改后的体育委员类
public class GroupLeader{private ListGirl listGirls;public GroupLeader(ListGirl listGirls){this.listGirls listGirls;}//清点女生数量public void countGirls(){System.out.println(女生人数是listGirls.size());}
}
修改后的测试类
public class Client{public static void main(Strings[] args){//产生女生群体ListGirl listGirls new ArrayListGirl();//初始化女生for(int i0;i20;i){listGirls.add(new Girl());}Teacher teacher new Teacher();//老师给体育委员发清点女生人数的命令teacher.command(new GroupLeader(listGirls));}
} 对程序修改把Teacher中对Girl群体的初始化移动到场景类中同时在GroupLeader中增加对Girl的注入避开了Teacher类对陌生类Girl的访问降低了系统间的耦合提高了系统的健壮性。
在实践中经常出现这样一个方法放在本类中也可以放到其它类中也可以。那怎么处理呢你可以坚持一个原则如果一个方法放在本类中即不增加类间关系也对本类不产生负面影响那就放到本类中。
迪米特原则的核心观念就是类间解耦弱耦合只有弱耦合后类的复用率才可以提高。其结果就是产生了大量的中转或跳转类导致系统复杂为维护带来了难度。所以我们在实践时要反
复权衡即要让结构清晰又做到高内聚低耦合。
3、自己理解
迪米特原则在自己开发中一定要培养这种思想因为它没有那么模糊而且这个原则没啥争议。 六、开闭原则 这个原则更像是前五个原则的总纲前五个原则就是围着它转的只要我们尽量的遵守前五个原则那么设计出来的系统应该就比较符合开闭原则了相反如果你违背了太多那么你的系统或许也不太遵循开闭原则。
1、定义 一句话对修改关闭对扩展开放。 就是说我任何的改变都不需要修改原有的代码而只需要加入一些新的实现就可以达到我的目的这是系统设计的理想境界但是没有任何一个系统可以做到这一点哪怕我一直最欣赏的
spring框架也做不到虽说它的扩展性已经强到变态。 这个就不说了字面上也能理解个八九分它对我来讲太抽象。虽然它很重要。
总结
如果你理解会运用了这六大原则那么你写出的代码一定是非常漂亮的二不是那么臃肿遍地第都是垃圾代码了。