编程模式--再论抽象类

      在java中有两种类:一种是具体类,另一种是抽象类。具体类可以实例化,抽象类不可以实例化。

一、什么是抽象类

      抽象类仅提供一个类型的部分实现。抽象类可以有实例变量,以及一个或多个构造函数。抽象类可以同时有抽象方法和具体方法。

      一个抽象类不会有实例,这些构造函数不能被客户端调用来实例化抽象类。一个抽象类的构造函数被其子类调用,从而使一个抽象类的所有子类都可以有一些共有的实现,而不同的子类可以在此基础上有其自己的实现。

      java抽象类和接口一样,都用来声明一个新的类型,并且作为一个类型的等级结构的起点。但是,java接口具有比抽象类更好的特性,因此,应当优先使用接口声明一个超类型。

二、抽象类的用途

     抽象类通常代表了一个抽象概念,他提供了继承的出发点。而具体类则不同,具体类可以实例化,应当给出一个有商业逻辑实现的对象模板。由于抽象类不可以实例化,因此一个设计师设计一个新的抽象类,一定是用来继承的。而这个声明倒过来也是对的:具体类不是用来继承的,只要有可能,不要从具体类继承。

1、代码重构的建议

      如果在一个原始的设计里,有两个具体类之间有继承关系,那么最可能的修改方案是怎样呢?

      假设有两个具体类,类A和类B,类B是类A的子类,那么一个最简单的修改方案应当是建立一个抽象类(或接口)C,然后让类A和类B成为抽象类C的子类。其实在这个例子中,包含更广泛的意义,这就是里氏代换原则。会在里氏代换原则那里详解。

2、抽象类应当拥有尽可能多的共同代码

      在一个从抽象类到多个具体类的继承关系中,共同的代码应当尽量移动到抽象类里。在一个继承的等级结构中,共同的代码应当尽量向等级结构的上方移动,这样可以提升代码的复用率。这样如果代码发生改变,设计师只需要修改超类,而子类无须修改。

     一个对象从超类继承而来的代码,在不使用时不会造成对资源的浪费。因此应当尽可能把子类中拥有的共同特征代码上移到超类中,这样可以保证最大限度的复用。一个典型的例子就是策略模式,在策略模式中,抽象类角色的分量越重越好,也就是说尽可能将公共的方法移动到抽象策略角色中。

3、抽象类应当拥有尽可能少的数据

      与代码的移动方向相反的是,数据的移动方向是从抽象类到具体类,也即从继承的等级结构的高端向低等级结构的低端移动。这是因为一个对象的数据不论是否使用都会占用资源,因此数据应当尽量放到具体类或者等级结构的低端。因此在前面的例子中,我们应该将类A和类B的数据保持在各自的类中,而不是移动到抽象超类C里面,这样可以保证节省内存资源。

三、在什么时候使用继承复用

      继承代表“一般化/特殊化”关系,其中基类代表一般,而衍生类代表特殊,衍生类将基类特殊化和扩展化,当满足下面条件时,才应当使用继承关系:

     ①子类是超类的一个特殊类,而不是超类的一个角色,也就是要区分“Has-A”和“Is-A”两种关系的不同。Has-A关系应当使用聚合关系描述,而只有Is-A关系才符合继承关系。

     ②永远不会出现需要将子类转换成另一个子类的情况。如果我们不是很肯定一个类会不会再将来变成另一个类的子类的话,就不应当将这个类设计成当前这个超类的子类。

     ③子类具有扩展超类的责任,而不是具有置换掉(Override)或注销掉(Nullify)超类的责任。如果子类需要将大量的置换掉超类的行为,那么这个子类不应当成为这个超类的子类。

     ④只有在分类学角度有意义时,才可以使用继承,不要从工具类继承。

 

本文通过总结《java与模式》而来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值