里氏替换:爱恨纠葛的父子关系

来源:互联网 发布:mysql读写分离 编辑:程序博客网 时间:2024/04/29 03:08

里氏替换:爱恨纠葛的父子关系

设计模式之禅里的这句话真的很形象。6大原则里,里氏替换可能是矛盾最大的一个。

=========

通俗了讲,依赖于面向对象的继承机制,父类能出现的地方,子类都可以无缝替换。

这在整个架构设计中占据了举足轻重的地位。我相信大家对这一点都深有体会

既然里氏替换这么矛盾,那么我们就从正反两个方面聊聊它好了。

(里氏替换讲的除了继承类以外,还包括实现接口哦~接口你也可以理解为父类)

正方

  • 父类能出现的地方,子类都可以无缝替换。

    这个我觉得没什么好讲的了,太普通常见的好处,我都有点不会总结语言描述了。

    这就是面向对象的多态嘛。突然想起来当初教同学面试讲多态的例子:

    你老刘家是县里的挑水能手。县老爷叫你老刘家去挑水,你爸爸老刘挑水用大桶走的慢,你小刘挑水用小桶走的快。县老爷才不管你家谁挑水呢,反正它就要结果,命令下达了。县老爷不管,县老爷的管家不能不管啊,他就让老刘去给水缸蓄水,让小刘去给县老爷家厨房挑水做饭。

    上层不需要修改代码(县老爷不用修改命令)。根据需求调用不同的实现类(管家根据需求分派了大刘和小刘)。子类多父类的功能实现了扩展(老刘家能挑水,小刘继承了老刘家的技能,但是有了自己的特性,跑得快)

  • 强制性的规则约束

    抽象类中的抽象方法,是子类必须实现的。这就保证了规则的执行力度。

  • 让代码更智能让程序员更傻瓜。

    换一种说法就是,“创造便利,让程序员更关注业务”。

    这个问题怎么说呢?只要你稍微知道一点java,就一定知道每个对象都可以通过toString()方法转成字符串。不知道你在用这个方法的时候有没有想过它的实现。有的对象toString()方法是用一系列拼接才实现的,但你现在什么都不需要做直接用就好了。

    Android 的 Activity设置Title,你什么都需要做,只要实现或重写这个方法就好了,完全不需要管要设置这个Title需要做多少工作。

  • AOP切面

    一种简单AOP切面的实现就是在父类中提前对关键路径做好准备。比如复写Android中在每个生命周期方法,做Log日志,打点等工作。

  • 避免重复的工作量

    我有10个界面类,每个界面类都需要一个Loading控件,难道我要在每个界面类中都写,复制粘贴10遍吗?NO!!!对代码有追求的程序猿是拒绝重复的。

    那么在父类中实现就好了,子类是拥有父类所有属性的嘛。

反方

  • 继承是侵入性的。

    这句话每一次提到继承的时候都会听到,怎么个侵入法呢?

    我有10个界面类,每个界面类都需要一个Loading控件,用继承的方式解决这个问题了。但是第11个界面类不需要啊,但是因为某些原因不得还要继承这个父类。我根本用不到,但你还是给我了,这是浪费啊,本来内存就不多,你还浪费!!

    解决方案: 申明不同的父类。

    BaseWindow是所有window的父类,有界面类必须的东西。
    BaseLoadingWindow extends BaseWindow. 需要loading控件的继承这个类。
    不需要的还是继承BaseWindow。

    缺点: 容易导致类爆炸与层级过深,难以维护。

    同样这个例子要提一点:这里不适用组合(虽然常听说要用组合代替继承),因为我们的初衷是避免重复写10次Loading。实际过程中权衡吧,毕竟类爆炸并不是随处可见的

  • 过深的继承树降低代码可读性

    在我还是一个小小白的时候,经常会爆出一句:“这东西哪来的啊?说用就用啊……”

    从实际生活角度来说,人们更习惯于明显的调用关系。这是继承的缺点,同样也是过于解耦的缺点之一,降低了代码的可读性。

  • 从MVP的角度

    接口可以理解为继承的一种。
    MVP的架构设计是对接口的过度利用,其最大缺点是,前期接口的修改极其痛苦。接口有任何改动,子类必须跟着变动,这对开发人员是极其痛苦的。

  • 向下转型

    里氏替换可以理解为向上转型,是默认的。

    但既然有了向上转型,就不可避免的会存在向下转型。

    向下转型是需要强转的,这是有风险的。

结论

总体来说,里氏替换是面向对象架构设计中不可缺少的。其优点应该发扬光大,其缺点也并不是致命,不可避免的,只要在使用过程中多加注意就好。权衡利弊永远都是一个架构师家常便饭。

0 0
原创粉丝点击