<<代码大全2>>中的关于类质量的观点也值得推荐:
硬性规定:
1.类层次不宜超过三层.
2.类成员数量不宜超过7个,如果全是简单数据类型最多9个,有其它类的话限制为5个,否则需要分解成更小的类.
3.避免创建万能类
4.消除不必要的成员.
5.消除无关精要的类.
6.避免用动词命名类.
抽象:
1.类是否有一个中心目的.
2.类的命名是否表现了其中心目的.
3.类的接口是否展现了一致的抽象.
4.类的接口是否让人明白了知道该如何使用它.
5.类的接口是否足够抽象,使使用者不必顾虑它是如何进行服务的.
6.类提供的服务是否足够完整,能让其它类无须动用其内部数据.
7.是否已经尽量分解.
8.在修改类是是否维持了接口的完整性.
封装:
1.是否把类成员的可访问性降至最小.
2.是否避免暴露类中的数据成员.
3.类是否已经尽可能的对其它类隐藏了实现细节.
4.类是否不依赖其它类,它是松耦合的吗?
1. 将公共操作和域放置在超类
显然这是继承最基本的目的,减少编码量,减少业务关注面。让子类更多关注自己的业务实现,而公共的由共同的超类去操心。
2. 不要使用受保护的域
很多程序员其实都很喜欢“受保护的”(protected)这种作用域,特别是应用在域上,因为在这种作用域的作用下子类可以轻松的直接访问超类中相应的 域,可能也觉得这是理所当然的事情,因为既然在子类中继承了这个域而不能直接使用它还需要使用super关键字来调用超类方法去访问往往显得很别扭和难 看。但是protected机制会带来很严重的安全问题,第一,因为子类集合是无限制的,任何人都可以由一个类派生出另外一个子类,并编写代码直接访问 protected的实例域,从而破坏封装性;第二,在Java中同一个包中的所有类都可以访问protected域,而不管它是否为这个类的子类。
所以推荐在尽可能的情况下把域设置为私有,不允许外部直接访问甚至修改。但其实很多初学者,包括一些编程老手都没办法理解和接受这种做法。我个人认为如果OO失去了封装性就好比失去了灵魂的空壳,没有了什么意义,反而变成累赘而已。
3. 使用继承实现is-a关系
如果抛开所谓的设计思想来看,实现继承最基本的目的就是节省代码量,并且很容易就做到。但这样往往很多人会滥用继承,纯粹为了节约代码而节约代码,而没有顾及超类与子类的关系导致在实现其他相关问题的时候带来很多麻烦,反而会多写很多代码,捡了芝麻丢了西瓜。
继承应该要遵循is-a关系,要判断是否遵循了is-a关系也有个很简单的办法,就是你念出这么一句话:“子类”是个“超类”,看是否合理。例如有一个雇员(Employee)类继承与人(Person),这个时候就有这样的关系:雇员是个人,显然是正确的。
4. 除非所有继承的方法都有意义,否则不要继承
当子类和超类之间遵循了is-a关系,但子类从超类继承来的某些方法对于子类是没有意义的,甚至是实现错误功能的,这个时候应该取消继承关系,因为这相当的危险。或者可以选择重新审视你的设计。
5. 在覆盖方法的时候,不要改变预期的行为
结合上面4中的观点,有些人可能会说这个很简单,把意义不同的方法在子类中重写,或者干脆什么也不做,也或者抛出一个异常不久解决了,so easy!其实这样也是违背OO思想的,应该尽量保持覆盖方法的预期行为,只可能因为各自的业务含义而改变实现方式,但是语义和行为是要保证的,就好像超 类有个方法名字叫add是用来做加法的,但是你一定要极端的在子类中重写了它,里面的代码偷偷实现了一个减法,这种做法的危险我想很容易理解。为什么说是 偷偷的,因为可能只有你知道这里是做了减法,或者说几个星期后你自己都认为这个方法实现的是一个加法运算!
6. 使用多态,而非类型信息
很多时候需要判断当前对象的类型来执行相应不同的方法,看下面的示例代码:
1
if
(x is of type
1
)
2
action1(x);
3
else
if
(x is of type
2
)
4
action2(x);
这个时候应该去考虑action1和action2是不是表示同一个概念,如果是就应该使用多态性来处理。这个时候应该定义一个方法放置在这两个类的超类或者接口中,然后就可以调用
1
x.action();
让语言提供的多态性自己去找应该调用那个类的方法来实现。是不是像极了某种模式:)
7. 不要过多的使用反射
个人觉得Java拥有了反射机制简直就是太强大了,在运行时能够查看甚至修改、调用域和方法极大的提高了程序实现的灵活性和技巧性。以至于我甚至有一段时 间把Java当作JavaScript来玩(爱死JavaScript的灵活性但同样具有那么优秀的OO)。看看现在大多数流行不流行的开发框架都是基于 Java这种强大的能力来实现的,它让大家可以编写更加通用的程序,也是为什么它在系统程序(包括一些框架、工具甚至服务器等)中使用这么广泛并都作为核 心技术;但是在编写应用程序的过程中应该减少反射的使用,因为反射其实是很脆弱的,编译器很难帮助大家发现程序中的错误,任何错误都到了运行时才被发现, 并导致一些莫名其妙的异常。说起莫名其妙其实不是说反射功能不够健壮或有问题,而是出现错误后很难跟踪和排查,给开发和维护带来很大困难。
还有一点也很重要,反射比直接调用慢,这个要时刻记住。所以应该用接口来实现回调之类的功能而不是反射。
其实这些只是继承技巧的冰山一角而已,每个人在设计过程中都会总结出自己的经验。
总的来说我们应该用理论来指导实践,而在实践中总结出理论!