OO经过这么多年的锤炼和考验证明是极好的,虽然也有些的人提出一些反面的意见,证明时代在发展、进步大家需要更先进的设计方法,不过OO终归是目前应用最为广泛也最为适用的程序设计方法学。个人觉得适用最为重要,毕竟OO是能够很好解决大多数复杂系统设计的。不过同时OO也让设计者陷入一个两难的境地。一来容易设计过轻,也就是设计的不够,发挥不了OO的强大和精妙之处,面向过程和面向对象的夹杂反而使得实现者摸不着头脑非常痛苦(很多时候这里指的设计者和实现着会是同一个人);二来容易设计过度,让OO从一个手中的工具变成了一块脚下的石头,让实现者工作起来异常的不顺利,觉得做了很多脱了裤子放屁的事情(请容许我的粗鲁,因为我再也找不到比这个更好的比喻了)。
要解决这些问题往往需要经验的积累和技巧的总结,知道OO理论的人不少,真正在设计中运用的好的确实不多。当然OO设计中最最重要的一个部分就是继承了,下面列举一些常见的继承设计技巧,读过《Core Java》的人可能对它们非常熟悉,我结合我自己的理解做一些说明,也当作一个学习。当然不是记住了这些所谓的技巧就学会了继承设计,总的来讲设计是需要遵循一定规律、结合实际情况、发挥自己的经验而做出的,还是需要自己多多总结和积累。
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这种强大的能力来实现的,它让大家可以编写更加通用的程序,也是为什么它在系统程序(包括一些框架、工具甚至服务器等)中使用这么广泛并都作为核心技术;但是在编写应用程序的过程中应该减少反射的使用,因为反射其实是很脆弱的,编译器很难帮助大家发现程序中的错误,任何错误都到了运行时才被发现,并导致一些莫名其妙的异常。说起莫名其妙其实不是说反射功能不够健壮或有问题,而是出现错误后很难跟踪和排查,给开发和维护带来很大困难。
还有一点也很重要,反射比直接调用慢,这个要时刻记住。所以应该用接口来实现回调之类的功能而不是反射。
其实这些只是继承技巧的冰山一角而已,每个人在设计过程中都会总结出自己的经验。
总的来说我们应该用理论来指导实践,而在实践中总结出理论!
posted on 2007-04-12 01:51
cresposhi 阅读(2075)
评论(12) 编辑 收藏