思考面向对象

思考面向对象

2013-12-24 蔡学镛

十年前我OO(Object-Orientation,面向对象致正,看了不少OO的,有外文(例如Grady Booch、Bertrand Meyer的书),也有中文。其中,中文书为了帮助者理解,都会用现实生活中的对象做比,比方:哺乳物、交通工具,我得我读过的一个书上范例说:「斑承自「

 

我在工研院(台湾的政府科研单位实习,老板要我告OO,我于是拿了上的例子当解,当老板听到我宣称「斑马继承自,他开玩笑地:「那么子(台湾俚语,女朋友的意思应该也是承自」。我当深受羞辱,感被IT中文荼毒了。

 

【学OO的重点】

OO的三大基是封装、承、多。用现实生活的对象做OO解上的比,通常不太恰当,因只能解封装和承,却无法解。而多却是OO真正的重点,也是学OO门槛。没有解,就等于小学而大

 

于OO,比恰当的例子是「形状」,一来容易理解,二来适合同封装、承、多

 

认为OO不用看太多,只要看Bertrand Meyer的名著OOSC第二版就了,但可不薄。

 

前面提到,面向象的三大基是封装、承、多。你会在特定OO言上看到一些其他机制,例如Template,RTTI(Run-Time Type Information),但些都不是重点。学OO候,焦点应该放在封装、承、多态这三方面。

 

三者是有次序性的,没有封装就不可能有承、没有承就不可能有多。只支持封装的言称Object-Based言(例如传统的Visual Basic),同支持封装、承、多言才能称OO言(例如.NET代的Visual Basic)。

 

有没有可能,存在某个言只支持封装和承,却不支持多?不会有么无聊,基本上承往往只是一个中间过程,真正的目的是多。既然支持了承,却不支持多是没有意的。

 

【封装】

封装(encapsulation)的目的是要将代切割成多模(module),每个模的关性降到最低,么一来比不会生「全身」的状况,降低相互依的程度,也等于是降低复度,可以维护更容易。

 

上,没有人用「模」一来称呼封装的果,而是称」,把模做更高的包装用途。因此我们现应该将「视为封装的果,把「模视为整个程序切割出来的多片段。而在OO的世界,一般来,一个程序有多个模,一个模内包含多个

 

的概念不是OO独具的,多非OO言也具有模,但是OO的言几乎都具,例如Java的Package;D言的模;而.NET更是分成件(assembly )和模,其.NET件与模都具一般模的概念,但程度有件包含模)。

 

封装是以数据为核心,将相关的数据放在一起,将会用到数据的函数也放来。封装等于是将数据和函数放在一起。尽管有的有其它的西,例如event、property,但是从内部来看,些都是函数的形。

 

了和非OO的世界做出区隔,OO也做了一些名上的改,将Function(函数)改称Method(方法)、将Call(呼叫)改称Invoke用)。但是新旧词汇基本上是通用的。

 

【能度】

封装的目的既然是要「降低互相依的程度」,就涉到能度的问题个「/方法/位」暴露给别的模、同一个模的不同、自己的「次」、友伴(Friend Class)、内部(Inner Class)?就是所的「能度」(visibility)。

 

当然希望尽可能降低能度,才能「降低互相依的程度」。也就是,人不需要知道的,就不要它知道,就是所的「信息隐藏」(information hiding)。

 

藏的是数据。极致的封装主者,主所有的数据一定都不可以直接被外部(包括次访问

 

上面提到,封装将相关的数据和使用到数据的方法包成。最理想的状况是,让数据的能最低,外面完全看不。留下的外接口(Interface)只剩下method。话说,每个象的Interface是一些方法的集合,完全没有数据

 

定能度不是一件容易的事,往往需要深思熟。特设计「框架」(framework)的人来,能定得太,造成信息隐藏效果不佳,可能会来相当多面的效果(例如复度提高、程序容易出、非thread- safe…等);能定得太,造成效率差、充程度差(有些设计因而做不出来)。

 

承】 

承的象称基底(base)或超(super)或亲类(parent),承者称衍生(derived)或次(sub-)或子(child)。

 

承的目的,是要达到「代码复用」(Code Reuse)或「接口用」。而承的手段,就是「充」或「修改」。承的重点,请务必牢

 

承所致的代复用,是指次能自沿的所有代,好你可以不用写太多代,只需要稍微充或修改,就能符合你的需求。 「充」指的是定新的方法(Method),修改指的是「针对中的某方法重新定其行」。

 

注意,承所生的次,和其超,两者在记忆体内是独立的。承所做的充与修改,并不会影响到超。在Windows程序设计中,有所的SubClassing技巧,其并不是承的概念,因它会修改到原本记忆体。

 

承所致的接口复用,是在OO的下一个段(也就是多)作准。接口复用,搭配方法的修改,就形成了多

 

如果你不想复用代,也不想复用接口,或者你不充、也不行修改,那么透过继生次,几乎是没有意的。

 

唯一的一个小小的意是,次和超两者是不同的,你可以在程序中依据一点做判断,做不同的行。但是是一种琐细的程序技巧,和OO无关,而且OO也不鼓励你么做。OO,透的机制造成行的差异,才是正确的作法。但即使是了此目的,我也会使用空Interface当作特殊标签(Mark),而不会使用当作标签,因Interface标签的副作用小,成本低,且不是垂直的关系。

 

承关系,制出一关系,如果制的候依循「超在上,次在下」,或者「超在左,次在右」,就可以形成一个类阶层(Class Hierarchy)。由于大多数的类阶层设计都是采用承(Single Inheritance),而非多重承(MultipleInheritance),所以阶层图往往是构,符合构的阶层图,也称为继类树

 

【多重承与接口】

承指的是,只有一个超;多重承指的是,具有多个超用框架设计几乎都是采用承(例如MFC、.NET Framework、Borland VCL、Adobe AIR),只有极少数以前的设计会采用多重承(例如Borland OWLEiffel)。

 

不只是如此,连语言本身的设计上,也往往禁止多重承(例如Java、Delphi、C#、VB.NET),只剩下极少数言允多重承(例如C++、Eiffel)。趋势似乎会延下去,主要是,多重承「可能」会造成「不知承的方法是来自那个超或祖先的困」。 C++要求要主指明承的方法来自何,但Eiffel的作法更巧妙(参考http://www.eiffel.com/)。

 

姑且不多重承的缺点,多重然表达能力比承更佳,至少,有不少原本在AOP(Aspect-Oriented Programming)解决的问题,在多重承之下可以易解决,不需要AOP。

 

从Java开始,多数的言使用Interface来解决多重承的问题,它号称『利用Interface可以享用多重承的点,又没有多重承的困』。但事根本不是如此!

 

Interface只能承到Interface,无法承到代(Interface不)。因此,如果你在Java中承多个Interface,你必须亲自定所有Interface的每个方法,也就是,你必多代。但如果是在C++/Eiffel中,你可以,不需要再定义这些方法。

 

所以接口是在「舍弃多重承缺点的同,也舍弃了多重承的点」。也就是,接口舍弃了「代复用」,保留了「接口复用」。从个角度来看,「接口复用」比「代复用」更重要。是因故,多才是OO的极目的。

 

【其他和承相关的问题

承某些程度上破坏了一部份的封装,造成次和超的相依程度提高。超如果改,且次没有跟着做出改,可能会造成次问题似DLL Hell的念。

 

(台湾)法律上有所的「限定承」与「抛弃承」,目前的言似乎都没有这样的概念,就算有,力也是放在超上,由超所控制,而不是在次上。

 

设计继,必先考接口是否共享,再考是否共享,再考。但是经验不足的程序员,反倒会先考和代复用,而忽略了「接口复用」是其中最重要的事。

 

【多与虚

Polymorphism中文一般称「多」,早期也有人称「同名异式」。我比叫喜前者,不喜后者。 「多象可以以「多种面貌」出,同名异式强调「不同的函数」。其,「型的不同」是因,而「函数的不同」是果。当一个象具有不同的型,就有可能会引机制。

 

一个有不同的型是因为继承而来,象可以扮演所有祖先的角色。例如当某象的是Sub,当此象被「型」成超Super之后,此象就具有两种不同的,「实际类」是Sub,「形式」是Super,此呼叫此象的方法m,会行到的是Super定的方法m?是Sub定(修改)的方法m?

 

答案是实际类的方法,也就是Sub定的方法m。所以所的多就是:不管形式是什么,一定会行到实际类的方法。

 

你可能会得疑惑,何当初要将祖先致「形式」(宣告)和「实际类」(定义类)不一问题留待下一节时再回答。

 

如果你的程序中,大量使用switch/case法,很有可能是你的设计不良,而没有好好地使用多。你最好能「重构」(Refactoring)你的程序。

 

的方法,可以分成虚(Virtual)与非虚两种。只有虚方法才能搭配多机制使用。如果是非虚方法,行到形式(而非实际类)的方法,因没有发挥作用。

 

关于虚,每个言有不同的作法。 Java强调动态,所以默认是虚;C++注重效率,所以默认不是虚

 

用框架】

了方便软件的开软件厂商都会提供用框架(ApplicationFramework),今流行的框架相当多,例如:.NET Framework、BorlandVCL、Java Class Library。 1980年代OO开始起,1990年代框架开始起。有了框架,我们终于可以享受到OO的好,重复利用人写好的代,不用一切自己重写。

 

框架厂商先将一大部分的程序先写好,只需要「利用承来做修改」,就能套用整个框架,了要你修改的部分能行到(而不是行到框架本身的方法),所以些允修改的方法都是定成虚的。

 

为编「利用承来做修改」所以生了次和重新定的方法。框架比个次更早被定,当然不认识这个次,所以框架内都是以此次的祖先类为「形式上」的象(理接口)。当此次类对象被入框架中,就会被自动转型成祖先,因此生「形式」和「实际类」的差异。正因为这样差异,加上次有重新定方法,所以多机制出了。

 

承架构中,良好的框架设计(例如Java Swing)会将代不需要被修改的部分,设计。至于需要被承修改的部分,设计Interface(Interface的方法全都是虚的),以及些Interface的。框架内的尽量只使用到些接口。

 

框架往往需要付出相当多心力,以JavaSwing来,就是一个相当复,不好学的框架。

 

框架设计上,近年来比不一的是,阶层深的趋势(例如AIR和WPF的框架);也就是的叶点到根点之的距离大了。这样的好是代重复利用度增加(所以框架档案的体积变小),接口重复利用度增加(学速度可以加快)。

 

【OO是生力的最解答? 】

和面向象程序设计关系密的是前一个段「设计」和下一个段「测试」。 「设计模式」(Design Pattern)将多好的设计整理出来,们设计功力大增。如果既有的设计不太好,你可以利用「重构」(Refactoring)的技巧来重新整理你的程序。求TDD(Test-DrivenDevelopment),OO,「测试」(Unit Test)正是以类为最小元的。

 

忘了UML!设计OO候,UML可以整理你的想法,方便大家沟通,甚至当MDA(Model Driven Architecture)成熟之后,号称可以用UML把架构设计图画出来,用OCL(Object Constraint Language)描述一些范,然后就可以生出代了。

 

OO太美好了! OO是软件的极致灵丹! OO真棒!我OO。 … 你醒醒吧! … 尽管OO主宰今的主流言,OO的开效率似乎有一些提升,但我可没看什么人用了OO之后就若有神助。更不用OO有学习门槛、各种OO框架的学线设计模式的学线度工程化的问题

 

最近,我得真正可以达到更高生力的关在于更高的抽象,也就是DSL(Domain SpecificLanguage)。尽管有的技号称有支持DSL,依然有程度上的差异。

 

至于OO,在我认识到DSL的威力之后,已被我打入冷了。因当DSL发挥到极致的候,OO似乎是派不上用的。

posted @ 2013-12-27 22:55  光光96  阅读(228)  评论(0编辑  收藏  举报