继承作为面向对象编程中的一项重大特性,其有着独特的威力——代码复用、功能扩展、运行多态 这些都与继承有着密切的关系。
代码复用:
将一组平行类中的相同或相似功能提取到父类,利用继承父类、重用父类代码,可大大减少的代码量。
功能扩展:
如果某个类已经拥有了 A、B、C 功能,这时想添加一项功能D,但却不想或者不能修改该类,则可以新建子类、利用继承,获得A、B、C 功能,并在子类中添加 D功能,即可实现目标。
运行多态:
继承的关键字:C#用冒号、Java用extends,其有两种形式的用法。
1)当作占位符去使用:【关于这一点仔细思考静态语言和动态语言的差异性就能理解了】
2)搭建继承体系
无论是以上的那种用法,其和面向对象编程的另一个特性——多态,它们之间的亲密度可以用“如胶似漆”去形容,在很多情况下继承中都会存在多态的“影子”。
以上三点可以作为继承的优点,但是有利必有弊:
1)子类将强制继承父类的全部非私有功能。
2)父类中任何修改都有可能会影响到全部子类。
3)如果继承体系过长,很容易造成代码冗余。
4)等等。
当然如果继承体系搭建的很合理、足以应对需求变化,
那从当前角度出发考虑问题(PS:人没有足够强的预知能力),上述的缺点是可以忽略的。
但是往往继承体系搭建的并不合理,也就是通常意义上的滥用继承。
我想许多人在继承的使用方面都会经历以下的几个阶段:
1)不会使用继承
2)滥用继承,即遇到任何可以搭建继承体系的代码都会去使用继承【当然这个描述有点极端了】。
3)少用继承
4)使用组合代替继承
5)继承与组合+ 封装完美协作
PS:
工厂方法和模板方法是继承的典型用法。
状态模式和装饰者模式是组合 + 封装的极致体现。
本文标题为《两个“不合理继承”的判定标识》,现在进入正题,当然前面的铺垫在我看来是必不可少的,且正文篇幅也不是足够大:
如果满足如下的这个条件,即可判定为“不合理的继承”,背景条件,有如下代码
public class Base{
protected int id;
public int getId() {
return id;
}
}
public class A extends Base{
// 在此类中可以自由访问 id
}
现状:
Base类:封装了 Base类、或者说是封装了id ,使id的访问收到保护。
A类:扩展了Base 类,但是在 A 中可以自由访问 id。
判定条件:如何在A类中自由访问id是不合理的,则可以判定为不合理继承,即破坏了封装的继承是不合理的。
往往破坏了封装的继承实际上使用组合更合理,往往破坏了封装的继承的两个类实际上是两个独立的职责,也就是违反了单一职责原则。
此判定方式是利用封装的特性去盘判定的,此外利用现实世界和继承体系是否冲突也可以作为判定条件,案例如:
有普通游戏玩家和职业游戏玩家两个类,但是不应该建立一个“所有人都玩游戏”的父类,而可以建立一个“所有玩游戏的人”的父类。进而搭建继承体系。因为:不是所有人都玩游戏,这与“真实世界”相冲突了。
当然还有许多其他类型的不合理继承的,比如不能满足特殊业务需求等等,总之:继承没有对与错,而是合理与不合理,而是否合理最终的判定条件是要视情况而定,以上只是列举了两种简易的判别方式。
————————————————————————————————————————————————————————————
续写:2013-11-28
————————————————————————————————————————————————————————————
不合理继承的“危害”:
1)造成结构混乱,增加代码复杂度,加大学习成本、尤其是“新人”,同时给代码命名也带来一定困扰。
2)增加维护复杂度、无论是修改功能还是添加新功能,牵一发而动全身:在维护一项功能的时候,会牵扯出许多其他的功能,给测试带来不必要的麻烦。