首先感谢宗传奇大神 送了我一本《设计模式之禅》,春节期间把这本书快速的读了一遍。
很久前对设计原则有过学习,但并没有去在意。最近几天写代码,发现写代码的时候脑子里会冒出这用的是‘开闭原则’,或 准备写代码的 问一下自己会用到什么原则。虽然设计原则不能帮助你现实任何业务,也不能明显的减少代码的开发量,但是很神奇。说它是禅,一点都不过分。
由于领悟尚浅,方便回味,自己写了简洁的案例;同时也希望各位大神,建议指导;
-
开闭原则(Open-Closed Principle, OCP)
一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
重构前,只要节目有变就要改春晚代码
//春晚演节目 (重构前)
public void chunWan(String name){
if("贾玲".equals(name)){
JiaLing jiaLing=new JiaLing();
jiaLing.xiaoPin();
}else if("王菲".equals(name)){
Wangfei wangfei=new Wangfei();
wangfei.changGe();
}
}
重构后,让春晚演节目更灵活。
//春晚演节目 (重构后)
public void chunWan(YanYuan yanYuan){
yanYuan.biaoYan();
}
//抽象出一个演员抽象类,添加一个biaoYan抽象方法.
public abstract class YanYuan {
public abstract void biaoYan();
}
//让贾玲继承 演员抽象类 并实现biaoYan抽象方法.
public class JiaLing extends YanYuan{
@Override
public void biaoYan() {
System.out.println("贾玲开始小品表演");
}
}
//王菲亦如此,实现表演唱歌. 从此所有人继承演员就可以上春晚了!
...
-
里氏替换原则(Open-Closed Principle, OCP)
子类可以扩展父类的功能,但不能改变父类原有的功能。所有引用父类的地方必须能透明地使用其子类的对象。
重构前,功能混乱没有约束,容易出错,弄不好就挂了。
public static void main(String[] args) {
Guo guo = new PingDiGuo();
//找个 锅 来 烹饪
zuoFan(guo);
//结果 拿锅拍人去了
}
//传入锅来做饭
public static void zuoFan(Guo guo){
guo.function();
}
//可以做饭的锅类
public static class Guo{
public void function(){
System.out.println("我来烹饪了");
}
}
//一只平底锅
public static class PingDiGuo extends Guo{
public void function(){
System.out.println("发现平底锅,可以杀人越货,我要维护世界和平");
}
}
重构后,保留锅的本质功能,平底锅特有功能应该独自实现。
public static void main(String[] args) {
Guo guo = new PingDiGuo();
//找个 锅 来 烹饪
zuoFan(guo);
//锅的本质功能 被保留
}
//传入锅来做饭
public static void zuoFan(Guo guo){
guo.function();
}
//可以做饭的锅类
public static class Guo{
public final void function(){
System.out.println("我来烹饪了");
}
}
//一只平底锅
public static class PingDiGuo extends Guo{
public void newFunction(){
System.out.println("发现平底锅,可以杀人越货,我要维护世界和平");
}
}
小结: 子类可以扩展父类的功能,但不能改变父类原有的功能。
里氏替换原则 举例非常困难,简单的总结一下
1:不能重写父类本质的非抽象方法。
2:父类本质功能,不想被重写可以加 final。
3:在子类中增加自己特有的方法。
4:当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
5:当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
-
依赖倒置原则(Dependency Inversion Principle, DIP)
高层模块不应该依赖低层模块,两者都应该依赖其抽象。抽象不应该依赖于细节。
细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
重构前,扩展差,不符合开闭原则。并且功能容易混乱。
//有个唱歌节目 传进不同的人唱歌
public void play(People ren){
ren.changGe();
}
//过了一段时间突然有只 鸟 也来节目 唱歌 ,然后节目 扩展 了一个 方法
public void play(Bird niao){
niao.changGe();
}
//渐渐时间旧了,发现两个问题 ,
// 1:不是所有的人或鸟都会唱歌;
// 2:还有其他动物也会唱歌;
重构后,想去参加唱歌节目,就必须实现唱歌,简单清楚。
//抽象出一个 歌曲类 , 有开始唱的功能;
public abstract class Song{
public abstract void play();
}
// 节目组做了调整 , 需求更明确 , 要求会唱歌的来, 不会唱歌的就不能来
public void play(Song geQu){
geQu.play();
}
//所以 接下来 想去 参加节目的 就必须要会唱歌 (实现Song)
-
接口隔离原则(Interface Segregation Principle, ISP)
客户端不应该依赖它不需要的接口。类间的依赖关系应该建立在最小的接口上。
重构前,
//有个演员抽象类 会唱歌 继承了唱歌 功能
public interface YanYuan extends Song{
void playSong();
}
//节目 邀请 演员 来唱歌
public void JieMu(YanYuan yanYuan){
yanYuan.playSong();
}
重构后,
//演员抽象类 会唱歌
public interface YanYuan {
void playSong(Song song);
}
//节目开始 歌 被演员唱了起来
public void JieMu(YanYuan yanYuan){
yanYuan.playSong(getSong());
}
//接口隔离原则能从很多 维度举例; 拆分细化等;
//个人看标题的表面意思 理解 更偏向于,接口回调,接口解耦方面....
-
迪米特法则(Law of Demeter, LoD)
一个软件实体应当尽可能少地与其他实体发生相互作用。
重构前,
//一个教室类 里面有很多电器
public class JiaoShi{
//各种电器
public KongTiao kongTiao;//空调
public Deng deng;//灯
public YinShuiJi yinShuiJi;//饮水机
....
}
//一个老师类 有检查关闭电源 的方法
public class Teacher{
public void jianChaDianYuan(JiaoShi jiaoShi){
jiaoShi.kongTiao.close();//关空调
jiaoShi.deng.close();//关灯
jiaoShi.yinShuiJi.close();//关饮水机
}
}
//老师需要 知道所有电器 并关闭
重构后,
//一个教室类 里面有很多电器
public class JiaoShi{
//各种电器
public KongTiao kongTiao;//空调
public Deng deng;//灯
public YinShuiJi yinShuiJi;//饮水机
....
//一个 开关
public void closeAll(){
jiaoShi.kongTiao.close();//关空调
jiaoShi.deng.close();//关灯
jiaoShi.yinShuiJi.close();//关饮水机
}
}
//一个老师类 只需要知道一个开关即可
public class Teacher{
public void jianChaDianYuan(JiaoShi jiaoShi){
jiaoShi.closeAll();
}
}
//还有一个层面的理解: 抽出一个 总闸开关 接口, 有个抽象方法 closeAll();
//只需要传给老师 总闸开关 , 老师调用 总闸开关的closeAll(),就能关闭所有电源
//然而不需要关心具体关闭什么电器, 这样电器 和 老师 就解耦了
-
单一职责原则(Single Responsibility Principle, SRP)
一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。
重构前,
//茶农类, 从前有个很辛苦的 茶农 ,
public class ChaNong{
//他要负责种茶叶
public void zhongCha(){
System.out.println("种茶,收茶");
}
//还需要加工茶叶
public void jiaGongCha(ChaYe chaYe){
System.out.println("加工了100盒");
}
//然后去集市卖茶
public void maiCha(ChaYe chaYe){
System.out.println("成功卖了100盒,收到了很多钱");
}
}
重构后,
//加工厂类, 由于茶农辛苦 ,收益底 ,一个很有眼光的人, 开了一家 加工茶叶销售公司
public class JiaGongChang{
//加工茶叶
private void jiaGongCha(ChaYe chaYe){
System.out.println("加工了100盒");
}
//然后批发茶叶到市场
public Money maiCha(ChaYe chaYe){
jiaGongCha(chaYe);
System.out.println("成功卖了100盒,收到了很多钱");
...
return money;
}
}
//茶农类, 从此 茶农 就有了 清闲 的生活
public class ChaNong{
//他只负责种茶
public void zhongCha(){
System.out.println("劳动最光荣");
}
//然后去集市卖茶
public void maiCha(ChaYe chaYe){
Money money =new JiaGongChang().maiCha(chaYe);
System.out.println(money);
}
}
本文是作者通过再次学习后的理解总结,希望能为大家带来帮助; 谢谢,谢谢宗传奇赠书
推荐链接: 设计模式之六大原则(转载)
推荐链接: 迪米特法则
网友评论