http://www.cnblogs.com/leoo2sk/archive/2009/04/09/1432103.html

目录 Catelog

序言 Perface

真经第一章:世界 Waltanschauung

真经第二章:抽象 Abstraction

真经第三章:层次 Arrangement

真经第四章:继承 Inheritance

真经第五章:耦合 Couple

真经第六章:运作 Moving

真经第七章:建造 Build

真经第八章:刻画 Delineate

真经第九章:模式 Pattern

真经第十章:悟道 Doctrine

后记 Afterword

参考文献 Reference

序言 Perface

      “佛曰:苦海无涯,回头是岸。——佛教用语”

面向对象(Object-Oriented),这是一条令无数开发人员魂牵梦绕的短语。几乎每个软件分析师、设计师和程序员都时刻将它铭记于心,对它顶礼膜拜。然而,对大多数人来说,它又像是天边的霞光,可望而不可及,无数次伸出双手,总是抓不住这虚无缥缈的圣物。于是,我们依然每天将面向对象高高供其,却始终无法悟得其道,更不要谈娴熟运用其道法了。
      面向对象像一滩苦海,无数人游弋其中,却久久不得其要领;类、对象、继承、多态、接口、UML、设计模式……无数概念看得我们眼花缭乱,却也悟不透其真谛。佛教有云:苦海无涯,回头是岸。如果置身苦海中无法脱离,那么,我们是否应该提高一个层面去看这片苦海:从哲学及科学的角度,去审视面向对象。
      曾有人说:艺术的极致是科学,科学的极致是哲学。此话不无道理,牛顿、爱因斯坦等科学界泰斗,在其后期都不约而同地转向哲学研究。当然,这里本人无意更不敢将自己与上面两位大师相提并论,而且本人也不奢求此文能成为一篇颇有思想的佳作。只不过,本人在平时的实践和思考中,略有小得,于是,在这里拿出,和大家一起分享讨论。虽然肤浅,但希望本文能成为一丝波纹,为各位脱离苦海提供一点点的推动作用。

真经第一章——世界 Weltanschauung

“世界观(德文:Weltanschauung)意为‘着眼世界之上’,是人们对世界的总的根本的看法。任何哲学问题的探讨,归其出发点和本源,都是世界观的问题。什么样的世界观决定了什么样的哲学观点。——马克思”

1.1、看世界

我们知道,哲学领域中,最根本的对立是唯物主义和唯心主义的对立,而附属其下,又有许多对立,如形而上学和辩证法的对立、可知论和不可知论的对立等等。这些对立形成了哲学的基本体系、派别和出发点。实际上,这些对立,都是世界观的对立。世界观,简而言之即如何看待这个世界。世界观是一切哲学问题的本源和出发点。
      同样,在程序世界里,也有着不同的世界观。而这其中最根本的对立便是过程论和对象论的对立,这个对立,衍生出了面向过程和面向对象两种方法论。于是,要真正理解面向过程和面相对象,我们就不得不先深究一下程序世界中这两种世界观。
      首先要提到的是,不论是过程论还是对象论,都承认一点,那就是程序世界本质上只有两种东西——数据和逻辑。数据天性喜静,构成了程序世界的本体和状态;逻辑天性好动,作用于数据,推动程序世界的演进和发展。尽管上述观点是统一的,但是在数据和逻辑的存在形式和演进形式上,过程论和对象论的观点截然不同。

过程论认为:数据和逻辑是分离的、独立的,各自形成程序世界的一个方面(Aspect)。所谓世界的演变,是在逻辑作用下,数据做改变的一个过程。这种过程有明确的开始、结束、输入、输出,每个步骤有着严格的因果关系。过程是相对稳定的、明确的和预定义的,小过程组合成大过程,大过程还可以组合成更大的过程。所以,程序世界本质是过程,数据作为过程处理对象,逻辑作为过程的形式定义,世界就是各个过程不断进行的总体。

对象论认为:数据和逻辑不是分离的,而是相互依存的。相关的数据和逻辑形成个体,这些个体叫做对象(Object),世界就是由一个个对象组成的。对象具有相对独立性,对外提供一定的服务。所谓世界的演进,是在某个“初始作用力”作用下,对象间通过相互调用而完成的交互;在没有初始作用力下,对象保持静止。这些交互并不是完全预定义的,不一定有严格的因果关系,对象间交互是“偶然的”,对象间联系是“暂时的”。世界就是由各色对象组成,然后在初始作用力下,对象间的交互完成了世界的演进。

1.2、一道智力题引发的思考

上面的描述也许有些不够直观,那么,下面我们通过一个实际的例子,直观感受一下在两种世界观下,对同一件事物是怎么看的。
      大家都听过这么个智力题吧:

说有甲、乙、丙三人住店,一间房30。于是每人10元,共计给店老板30元住进一间房。后来店老板发现弄错了,房价应该是25元,于是给小二5元让小二退给房客。小二黑心,贪污了2元,退给甲乙丙每人1元。这样房客每人付了10-1=9元,三九27,加上小二贪污的2元,共29元,问那1元哪里去了?

不知各位聪明的看官是否已经参透其中玄机。不过参不透也没有关系,这不是重点,重点是,我们现在来分别用过程论和对象论分析一下这件事。
      首先,我们来看看过程论是怎么看这件事情的。

图1.1、过程论看世界

如图1.1所示,这就是过程论下看这件事的样子。左边是过程的各个步骤,而右边红字表示在每个过程步骤的数据情况,这种数据情况反映了世界当前的状态。为简单起见,我们只考虑在这个过程中参与分配的数据。
      初始时甲乙丙各10元,老板和小二没有钱,这可以认为是这个过程的初始状态,这些数据是输入。随着各个步骤的进行,数据不断更新,而在每个步骤,数据如何更新、更新多少,都是由步骤严格确定的。经历五个步骤后,数据变为甲乙丙各1元,老板25元,小二2元,这就是终止状态,也是这个过程的输出。

下面,再来看看对象论下如何看这件事。

图1.2、对象论看世界

对象论眼中,世界是由各种对象组成的,每个对象有自己的数据和逻辑,如图1.2所示。在这件事里,有五个基本对象:甲、乙、丙、小二和老板(注意,这里我们还没有提到类和抽象等概念,所以不要让固有思维跳出来,在这里要只认识对象,不认识类等概念。现在我们只讨论世界观的基本问题:程序世界的本质,至于更具体的问题,留待后面讨论)。每个对象有自己的一系列数据和逻辑,这里只列出了我们关心的部分。
      然后呢?没有然后了。没错,在对象论眼里,这就是这件事的本质模样,这件事所涉及的东西就是这么几个对象,本来它们各自独立,老死不相往来。只不过在“住店”这个外部驱动力下,几个对象“偶然”、“暂时”互相联系,利用其他对象提供的公开服务,完成了一些交互。在交互中,各自的数据可能会发生一些变化,但对象的本质没有变。这里也要注意,这种交互虽然在一定程度上由既定逻辑预定义,但不像过程论认为“万事万物都已注定”,在对象论下,对象间的交互是“偶然的”、“暂时的”,这次五个人因为住店这个外部驱动力交互了一次。但下次如果魏国和蜀国交战变为驱动力,他们间的交互就不是拿钱给钱了,而是刀兵相见。所以,对象论不认为“一切都已注定”。

1.3、总结

通过上面一个例子,不知各位是否已经明白程序世界中两种世界观看事物的不同。下面,有一些问题还要明确一下。
      I. 过程论和对象论是两种看世界的观点,没有孰对孰错、孰好孰坏之分。
      II. 过程论和对象论不是一种你死我活的绝对对立,而是一种辩证统一的对立,两者相互渗透、在一定情况下可以相互转化,是一种“你中有我、我中有你”的对立。如果将对象论中的所有交互提取出来而撇开对象,就变成了过程论,而如果对过程论中的数据和逻辑分类封装并建立交互关系,就变成了对象论。
      III. 过程论相对确定,有利于明晰演进的方向,但当事物过于庞大繁杂,将很难理清思路。因为过程繁多、过程中又有子过程,容易将整个世界看成一个纷繁交错的过程网,让人无法看清。
      IV. 对象论相对不确定,但是因为以对象为基本元素,即使很庞大的事物,也可以很好地分离关注,在研究一个对象的交互时,只需要关系与其相关的少数几个对象,不用总是关注整个流程和世界。但是,对象论也有困难。例如,如何划分对象才合理?对于同一个驱动力,为什么不同情况下参与对象和交互流程不一样?如何确定?其实,这些困难也正是面向对象技术中的困难。

综上,我们知道在程序世界中,存在着过程论和对象论两种对立的世界观,并且其各有千秋,无法定夺孰好孰坏。但是,对象论似乎更有助于分析规模较大的事物。本文是探讨面向对象的,所以,在下文中,都会选择对象论作为世界观。这种以对象为本的世界观,也是本文后续一切的基础和出发点。

真经第二章——抽象 Abstraction

“金、木、水、火、土元素,构成宇宙万物,并作为各种自然现象变化之基础——五行说”

2.1、导言

上文探讨了世界观问题。我们知道,要想真正理解面向对象,首先要用对象论去审视世界。而在对象论中,万事万物的本源是对象,对象是组成世界的基本元素。但是,要真正看透一个世界,只有基本元素是不行的。
      中国古代的朴素唯物主义哲学中,比较有代表性的是五行说。五行说认为,世界的基本元素是“金、木、水、火、土”,但若说世界只有“金、木、水、火、土”,也是不成的,所以后续有云:五行相生相克,相互交织结合,组成了大千世界。虽然从现代科学角度看,五行说并不完全准确,但其有一点事非常正确的,那就是世界首先有基本元素,然后基本元素还要衍生出各种其它东西。
      在第一章中,我们说了在对象论中,对象是组成世界的基本元素,但这还不能构成真正的世界。下面,我们来看看对象是如何构成和衍生出其它事物的。

2.2、类是怎么来的

和真实世界中构成和衍生方式不同,程序世界中,最重要的衍生方式是抽象。例如,众所周知的类(Class),就是从对象上首先抽象出来的概念。下面我们看一看类是怎么来的。
      从哲学角度说,先有对象,然后才有类,类和对象是“一般和特殊”这一哲学原理在程序世界中的具体体现。这可能和很多人的直觉不同,因为在具体写程序时,是先定义类,然后才能实例化对象。在这里,我们是从哲学层面进行探讨,所以,对象是本源,类的概念是衍生。为什么?因为从认识论来说,首先有具体认知能力,才能有抽象认知能力,抽象认知能力是一种高层的,人类特有的认知能力,它使我们可以从大量具体认知中,舍弃个别的、非本质的属性,提取出共同的、本质的属性,是形成概念的必要手段。
      还是以住店的故事为例吧。在我们的世界观中,那个故事涉及了五个对象,刚开始我们没有抽象的概念,而只是从具体认知角度对这五个对象进行认知:首先是甲,他有头、有身子、有胳膊有腿,头上有眼睛鼻子耳朵,他还有个名字叫刘备,有个身份是顾客……除了这些数据,这个对象还可以做一些事情,可以吃饭、呼吸、喝水,还能给钱和拿钱……好的,一通认知后,我们对甲这个对象有具体认知了;然后,我们对乙进行认知:他有头、有身子、有胳膊有腿,头上有眼睛鼻子耳朵,他还有个名字叫关羽,有个身份是顾客……除了这些数据,这个对象还可以做一些事情,可以吃饭、呼吸、喝水,还能给钱和拿钱……认知完了,接着是丙、小二和老板……当具体认知足够多后,我们发现一件事情:这几个对象很相似啊,有相似的数据(但具体值可能不同),有相同的逻辑,于是,我们的抽象认知能力告诉我们,这五个对象很相似,可以看做一类东西,于是,我们给出一个类,叫“人”,并且认为这五个对象都是“人”这个类的具体例子,我们叫其为实例。以后遇到类似的对象,我们都可以知道,这个对象属于“人”类。

图2.1、“人”类的由来

所以,类其实是抽象认知能力作用于程序世界的基本元素——对象后所衍生出来的抽象概念,是抽象思维在程序世界中物化后的产物。当然,现实世界中每个对象都有无数的数据和逻辑,但在具体到程序世界时,我们往往只关心具体场景中相关的数据和逻辑。例如,在住店场景中我们关心现金这则数据,至于这个人力气大不大无所谓;而如果上战场打仗,我们就关心攻击力和力量,现金就不重要了。

2.3、为什么要有类

知道了类是怎么来的,那么类的作用是什么,我们为什么需要类呢?
      类可以帮助我们方便地认识和定义世界中的对象。这个作用是显而易见的。例如当今世界有60几亿人,如果不会抽象思维,我们每遇到一个人,都要认知一遍:啊!这个对象有眼睛,有耳朵,有鼻子有嘴,有胳膊有腿……要是真这样,世界也太疯狂了。有了类的概念,我们就可以只记类的数据和逻辑,而对于具体对象,只要知道它属于什么“类”,一切就都知道了,所需要区分的只是不同对象的数据具有不同值而已。
      其实,这不仅仅是类的作用,我们进行抽象思维,就是为了这个目的。

2.4、总结

这一章叙述了类的哲学本质、衍生过程和作用。要记住,抽象是形成和衍生概念的基本方法,不只是类,后面的很多概念,都是通过抽象形成的。所以,我们可以说:上天只给了这个世界各种对象,但我们用抽象去更好地认识世界。

真经第三章——层次 Arrangement

“道生一,一生二,二生三,三生万物——老子”

3.1、导言

上文提到,在对象论中,抽象是衍生概念的基本方法。但是你有没有一个疑问?所谓抽象,是对许多对象撇开个性,抽出共性,这样,抽象过程就不是确定的、唯一的。例如,我们在看过很多对象后,发现有一类对象有四个轮子、有发动机、可以驾驶、是可以被意识反映的客观实在。我们抽象出一个叫“汽车”的类。这次抽象中,我们将有四个轮子看做了共性,但是,如果撇开这条性质,仅看后三条,摩托车、轮船、飞机都符合,于是,我们又可以抽象出“机动交通工具”类。再把有发动机撇掉,自行车、脚踏三轮车,甚至马都符合,所以,又得出个“代步工具”类,最后,把可以驾驶也撇掉,只剩下“是可以被意识反映的客观实在”,如果这样,所有物质都符合,这样,就得出一个“物质”类。
      这下子困难就来了,你说我家的奔驰应该归到哪一类呢?我家的奔驰和一只是不是一类东西呢?如果从前三类看,当然不是,但是从最后一个“物质”类看,又确实是一类东西。那到底哪一个对?事情究竟是怎样的?其实答案很简单:归到哪一类都正确。至于后一个问题,无法回答,因为这个问题单独问根本没有意义。为什么?
      关键在于:抽象是有层次的。

3.2、世界是一棵树

上文说到,对象是基本,我们从对象上抽象出类。但是,世界可并不是一层对象一层类那么简单,对象抽象出类,在类的基础上可以再进行抽象,抽象出更高层次的类。所以经过抽象的对象论世界,形成了一个树状结构。

图3.1、抽象层次树示例

图3.1展示了一棵抽象层次树的示例。不要怀疑,在对象论中,经过初步抽象思维加工后的世界就是这样样子。本来,世界只有各个具体对象(最底下紫色文字表示的层次),这是第0层,是一切抽象的本源和起始,然后,抽象思维作用其上,抽象出初步的类,然后在既有类和对象的基础上可以再进行抽象……如此归纳下去,最终整个世界归结于树的根节点:本体。所谓本体,即万物之源、万物之本,是哲学层面上最高层次的抽象。在这里,我们将其看成是一个特殊的类,作为抽象层次树的根。
      千万不要小看了这棵抽象层次树,如果能参透其中的奥秘,就能明白很多面向对象中的玄机,而且很多问题就都迎刃而解了。这种抽象层次树理论也是后续诸多内容的理论基础。例如,OO中重要的概念——继承(Inheritance)和多态(Polymiorphism),如若探究其哲学本源,就是从这里来的。

下面,对这棵树做一些必要的说明。
      I. 这是一棵单根树,最顶层“本体”为唯一的根,最下层叶子节点为基本对象。一切中间节点都为类。
      II. 越往上的类抽象层次越高,具体度越低,其内涵越小,外延越大;越往下的类抽象层次越低,具体度越高,其内涵越大,外延越小。说明一下,所谓类的内涵,是指类对属于自己的对象的说明力度,而外延是指类能包含的具体对象的总和。例如,家用电器这个类,其内涵是使用电作为能源并完成特定功能的家用器具,各个电冰箱、洗衣机、电磁炉、游戏机、DVD机等都在其外延之内;而娱乐家用电器这个类,作为比家用电器更低层次的类,其内涵除了“使用电作为能源并完成特定功能的家用器具”外,还要是具有娱乐功能,其内涵明显大了,但外延却缩小了,只包括了各个游戏机、DVD机等对象。
      III. 抽象层次树不是从根部向下长的,而是从叶子节点向上归纳生成的。
      IV. 某一个叶子节点所代表的对象可以归入所有其祖先结点所代表的类
      V. 直接问两个叶子节点属不属于一个类没有意义,而要指定抽象层次才有意义。例如在较低层,一辆宝马属于汽车,而一只苍蝇属于昆虫,不是一类。但如果指定在较高层比较,两个都属于具体物质,属于一个类。
      VI. 我们定义,如果一个节点CNode非叶子节点也非根节点,那么在哲学意义上,这个节点继承于其父节点PNode,并且说PNode是CNode的泛化。
      VII. 我们定义,如果一个节点CNode非叶子节点也非根节点,如果强行将它看成其任何一个祖先节点ANode,并当做ANode使用,那么在哲学意义上,叫做多态性。

3.3、总结

先说明这么多了,随着后续内容的深入,还会有更多丰富的内容进来。例如,后面会看到,所谓的“里氏代换原则(LSP)”,在哲学本质上不过是在这棵树上所加的一条限制规则,而“面向接口编程”、“低耦合、高内聚”、“依赖倒置”等一系列耳熟能详的短语,归结到哲学上也只是这棵树的一些精化。
      另外,看了上面的理论,我想本章开头留下的疑问也已经烟消云散了吧。
      再提示一遍,这棵树非常重要,得其精髓,就能理解诸多OO中概念、原则和方法的本质。后续讨论中,抽象层次树理论将作为重要的理论基础。

真经第四章——继承 Inheritance

“子类型必须能够替代掉其父类型——Barbara Liskov”

4.1、原来是先有儿子才有父亲

这一章我们讨论继承(Inheritance)。
      我们先看一看继承在哲学意义上时怎么来的。对象论的世界观认为,世界的基本元素是对象,我们将抽象思维作用于对象,形成了类的概念,而抽象的层次性形成了抽象层次树的概念。接着,我们就可以定义:在抽象层次树上,除根节点和叶子节点外,任一节点CNode非严格继承其所有祖先节点所组成的集合中的任一元素,而CNode严格继承其父节点PNode。
      继承概念,看似简单,若深入思考,却隐藏众多玄机。首先,继承描述的实际是抽象层次树上祖先节点与子孙节点的关系,但我个人一直不赞成使用继承(Inheritance)一词来描述这种关系,而推荐使用泛化(Generalization)一词。为什么呢?因为我们已经知道,从哲学和认识论角度来说,是先有对象,然后有类;先有子类,然后有父类,是一种自底向上形成的体系。而继承一词,明显带有自顶向下的暗示,因为往往是先有爷爷、有父亲继承爷爷、然后才能有儿子继承父亲。这样,就容易让人误解成是先有父类才有子类。所以,为了更好的体现继承的哲学本质,我更倾向于使用“泛化”代替“继承”。当然,由于继承一词已经被普遍使用和接受,接下来我还是会沿用继承一词,只不过希望各位时刻牢记,其实是先有了子类,才从子类泛化出父类。
      当然,当父类被抽象出来后,可能还会有新的子类加进来。但是,当初父类一定是从某些子类中泛化出来的,而不会是凭空突然出现的。

4.2、继承的作用

探讨了继承的本质,然后我们来探讨继承存在的意义。一切存在的东西都是有意义的,否则就不可能存在。注意,这里的“意义”是中性词,指事物存在的原由,不要理解成褒义。
      我们需要继承这个概念,本质上是因为对象论中世界的运作往往是在某一抽象层次上进行的,而不是在最低的基本对象层次上。举个例子,某人发烧了,对其他人说:我生病了,要去医院看医生。这句简短的话中有一个代词“我”和三个名词“病”、“医院”、“医生”。这四个具有名词性的词语中,除了“我”是运作在世界的最底层——基本对象层外,其他三个都运作在抽象层次,在这个语境中,“病”、“医院”、“医生”都是抽象的,他并没有在医院里拉着某个医生对别人说:我生了这个,需要去这里看这个。但是,本质上他确实是生了一个具体的病,要去一个具体的医院看一个具体的医生,那么在哲学上要如何映射这种抽象和具体呢?就是靠继承, 拿医生来说吧,所有继承自“医生”类的类所指的所有具体对象都可以替换掉这里具体的医生,这都不影响这句话语义的正确性。
      所以,继承的哲学作用就是:规定了抽象与具体之间的可映射性。形式化一点说:设G(c1,c2)意为c1非严格泛化自c2,I(c,o)意为对象o属于c的外延,其中c1,c2,c均为类,o为对象。那么,c可在哲学语义上映射成o,当且仅当o∈{o|I(c,o)}∪{o|I(c’,o) 且 G(c,c’)}

4.3、开放-关闭

如果你讨厌看形式化的东西,那么上面蓝色文字不看也罢,但是,有一条原则你一定很感兴趣,那就是著名的开放-关闭原则(OCP)。

开放-关闭原则(OCP):软件实体应该可以扩展,但不可以修改。

为什么忽然扯到OCP呢?因为,OCP正是上文讨论的哲学原理在程序世界的具体表述。我们来对比看一下,到底OCP是个什么意思。
      还是上面看病那个例子,什么叫可以扩展?就是说,因为在某个抽象层次是进行表述,就不能把话说死了,不能全是这个、那个的把每个对象都指派明白。如,那句话改成“我的右脚扭到了,要去北京航空航天大学医院去看胡青牛医生”,这句话就没有扩展性可言了,所有话都说死了,你如果去的是北医三院或临沂市人民医院,那么语义就不对了,而如果找的不是胡青牛而是华佗或扁鹊,语义也不对了。为什么无法扩展?因为所有点都指定了具体的对象。
      而原话“我生病了,要去医院看医生”则扩展性很大,因为只要不违反可映射性定义,映射到任何符合条件的对象都正确。扩展性和灵活性大大提高了。所以,“可以扩展”四字从哲学上其实是要我们在设计和开发软件时提高抽象层次,不要总在具体对象层面上进行处理。这下,你明白为什么说OCP可以提高软件的可扩展性和灵活性了吧。
      再来说说“不可以修改”,因为如果随便乱改,那就天下大乱了。还是医院那个例子,“医院”这个类所映射到的对象,一定是治病的地方。如果这东西随便改,例如明天“医院”和“食堂”的概念对换了,那麻烦了,我们所有人都要改,要把两个概念从脑子中对换过来,全世界的书、报纸、Internet……凡是依赖这两者进行表述的地方都要改,那不是天下大乱么?软件世界中也会发生这种牵一发而动全身的问题。所以我们提倡设计好的类一定要“对修改关闭”。
      以上,就是OCP的哲学意义。

4.4、儿子,你要能完全替代老爹才行

不过,要想世界正常运作,只有OCP似乎还有点问题。到目前为止,我们都是在抽象层次树已经存在,并且假定它完全正确的前提下讨论的,可是,我们并没有任何规则限制抽象层次树的正确性,例如,如果我把食堂挂到医院下,让食堂成为医院的子类,在理论上时没有错的,但如果这样随便乱规定继承关系,那么一切依赖继承正确性的原则、概念都没有意义了。所以,只有OCP是不够的,需要对继承进行一个限制。
      Barbara Liskov在1987年的OOPSLA大会上发表了一篇文章——《Data Abstraction and Hierarchy》,其中提出了一个非常重要的原则,叫里氏代换原则(LSP)。

里氏代换原则(LSP):子类型应该能代替掉其父类型,且代替后程序运行情况不会错乱。

我们还是用例子去理解LSP。
      现代办公几乎都要用到个人计算机,个人计算机本身是一个抽象概念,台式PC是其中一个子类。后来,发明了笔记本电脑,我们想把笔记本电脑归为个人计算机的子类,是否合理呢?根据LSP,我们将台式PC都替换成笔记本电脑,世界应该是照常运行的(当然,实际情况可能复杂些,有些地方不能用笔记本电脑替换,但这里我们忽略这种差别)。我们办公时依赖的类是“个人计算机”,而笔记本电脑完全可以替代这个类型而使得世界运行正常,所以,我们说将笔记本电脑归于个人计算机的子类是符合LSP的。
      后来,又发明了转基因黄瓜,我们也想将它归到个人计算机的子类中去,行不行呢?好的,现在我们再运用LSP,将世界上每个依赖个人计算机的地方都替换成一根转基因黄瓜。好的,世界人民都疯了!明显这种替换会令世界运行错乱。所以,我们不能让转基因黄瓜继承个人计算机。
      上面的例子是显而易见的,但有些却不那么明显。例如,现在问,兽医是医生的子类吗?这个问题,一下子还真不是很好回答,但我们可以LSP一下,现在,我们把医院里的医生都替换为兽医,你还敢去医院看病吗?嗯,这下子不用我多说了吧。
      最后一定要说明的是,LSP应用于程序世界和现实世界时有很大差别的,现实世界繁杂、不确定性因素多,而程序世界简单、确定。总之,LSP就是让你记住一条,凡是系统中有继承关系的地方,子类型一定能代替父类型,而且替换后程序运行要正常。换言之,继承是一种严格的“IS-A”关系,也是“一般和特殊”的哲学原理在程序世界中的体现。

4.5、总结

继承的话题就讨论到这里了。很多朋友在运用继承时有疑惑,或不能很好的确定继承关系,归其根本是没有真正理解继承的意义。只要能理解继承的本质意义,加上OCP和LSP的运用,是可以写出正确的继承体系。

真经第五章——耦合 Couple

“一只蝴蝶在巴西轻拍翅膀,可以导致一个月后德克萨斯州的一场龙卷风——蝴蝶效应”

5.1、为耦合平反

做程序的人,往往感觉“耦合(Couple)”不是什么好东西。经常有人、有书、有文章对我们谆谆教导:要降低耦合,要降低耦合……久而久之,好像耦合在程序界成了贬义词,弄得我们恨不得把耦合从程序里全部拿掉。
      这误解可委屈耦合了。要是哪天没了耦合,这世界还真玩不转。其实耦合还有另一个名字,叫“联系”,试问要是世界上所有对象间的联系都没了,世界还能运作么?耦合的存在是世界演进的途径,如果没有耦合,世界就变成了“死世界”,无法演进和发展。所以,耦合可是好东西,我们要感谢它!但是任何东西都有两面性,过度的耦合确实会令世界的运作产生困难,所以我们提倡降低耦合,这些是后话。

5.2、形形色色的耦合

下面,我们探讨各种耦合式怎么出现的。
      上一章讲述了继承,其实,继承的概念出现后,有父子、祖孙关系的类就有了一种联系,这种联系叫做“泛化耦合”。这就是我们认识的第一种耦合。

泛化耦合(Generalization Couple):由于泛化(继承)关系的存在,在两个有祖孙、父子关系的类间形成的一种逻辑关联。

然后,我们讨论另一种耦合。
      在文章开始,我们说对象论将对象看做基本元素,而对象中有数据和方法。在现实世界中,数据并不总是简单数据。客观存在一些对象,它们的数据是另一个或另一些对象。例如,一个具体的羊群,有一项数据是很多具体的羊。其中羊也是对象。当抽象成抽象的“羊群”和“羊”类的时候,这种包含关系也随之被抽象到了类中,由此在两个类之间就形成了耦合。
      这种耦合出现的哲学基础是,对象本身固有的包含关系,在进行事物抽象时被同时抽象到了类中。所以,我个人将其称为包含耦合。
      包含耦合又分为两种情况,一种是被包含对象单纯聚合在包含对象中,但没有形成哲学意义上“整体与部分”的关系,这是一种相对较弱的联系,叫做聚合。例如,上例中羊群和羊就是聚合关系,如果拿掉一两只羊,羊群还是羊群。

聚合(Aggregation):一种弱的拥有关系,体现A对象可以包含B对象,但B对象不是A对象的一部分。

另一种情况是,被包含对象和包含对象形成了哲学意义上“整体与部分”的关系,如汽车和轮子,把轮子拿掉,汽车就不再是完整意义上的汽车了。这种关系叫做组合。

组合(Composition):一种强的拥有关系,体现了严格的部分和整体的关系,部分和整体具有一样的生命周期。

通过上面的探讨,我们认识了泛化耦合、聚合和组合三种耦合形式,最后,还有一种耦合叫依赖。什么是依赖呢?我们知道,在对象论中,将世界的演进看成是在初始作用力下,对象之间相互调用、相互协作完成的。如果两个类在需求范围内,既定逻辑上存在协作的可能,那么这两个类就存在依赖关系(或叫关联关系)。其实,我们常说的“低耦合,高内聚”、“降低耦合”等建议,主要是针对依赖说的。

依赖(Dependency):由于逻辑上相互协作可能,而形成的一种关系。

好的,到目前为止,我们已经认识了四种基本耦合。下面用一副图,直观感受一下世界的各种耦合。

图5.1、耦合示例

图5.1展示了几种耦合的示例。其中汽车和交通工具属于泛化耦合,轮子和方向盘组合于汽车,汽车聚合成车队,而汽车和司机具有依赖关系。这幅图只是耦合的一个小片段,实际上,世界上各种对象形成了一张复杂的耦合网,正因为有耦合的存在,世界才能演进。正如马克思主义哲学所说:联系是普遍的、客观的。所以,耦合的存在,有其深刻的哲学意义。

5.3、总结

不知你是否会有这样的疑问:文章开始,不是说对象论将对象看做相互独立的吗?怎么又耦合起来了。这是矛盾的吗?实则不矛盾。因为我们所处的境界已经不同。刚开始,我们抛开一切,忘记一切,从本质的角度用对象论去看世界,我们看到的对象是相对孤立的。而后来,我们的抽象思维作用于这个世界,所衍生出来的一系列概念,是我们的抽象能 力给这个世界抹上的色彩。就如我们用唯物主义看世界时,刚开始要抛开一切,认为世界只有“可被意识所反映的客观实在”,而后,这个物质为本的世界在我们的抽象思维中衍生出各种概念。为了让我们更好的、系统的认识对象论,刚开始,我们抛开一切直取本质,而后来,我们要层层衍生,将抛却的东西再找回来,在这个“找”的过程中,我们才能领会OO中的各种概念、事物其在哲学意义上是怎么来的。

OO真经——关于面向对象的哲学体系及科学体系的探讨(上)相关推荐

  1. OO真经——关于面向对象的哲学体系及科学体系的探讨(下)

    真经第六章--运作 Moving "运动是绝对的--牛顿" 6.1.导言 在前五章中,我们从世界观的这话题开始,逐步引出了抽象.层次.继承和耦合.这些内容,形成了对象论中关于世界的 ...

  2. OO CSS(面向对象 CSS)

    面向对象的CSS OO CSS的概念解读 OO CSS的作用和注意事项 OO CSS代码实战 众多开发者忽视了CSS的表现(认为他太过简单,是一种机械的工作),而且更多关注在JavaScript的性能 ...

  3. linux识别科学计数法,Linux下科学计数法(e)转化为数字的方法 [shell中几种数字计算说明]...

    科学计数法使用e标识数值,将科学计算学转化为数字的思路:按e右边的数字移动小数点位数.e右边的数字如果是负数,则向左移动小数点.示例如下:1.2345678e2 = 123.45678 1.23456 ...

  4. 【转】面向对象之多态(向上转型与向下转型)

    转:https://blog.csdn.net/qq_31655965/article/details/54746235. 多态,大概每个人都知道.但是,又有几个人真的理解什么是多态.多态有哪些细节呢 ...

  5. 本科阶段计算机专业的科学体系,【学习方法】一位大三本科生的计算机科学与技术学习反思录...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 姓名:曾毅 学历:本科(大学三年级) 专业:计算机科学与技术 个人爱好:计算机,英语 英语水平:大学英语六级,四六级考试口语加试优秀 经历: 微软高校信息 ...

  6. linux bsd命令,科学网—Linux/BSD下join命令使数据以tab为分隔符的方法 - 李雷廷的博文...

    [join命令可以通过-t $'t'来把分隔符设为tab字符] 默认情况下,Linux/BSD下的join命令以空格为分隔符.即使你的输入文件是以tab为分隔符的,输出文件也会变成以一个空格为分隔的文 ...

  7. linux合并pdf命令,科学网—[转载]linux下利用命令行工具pdftk对PDF进行合并分割(转载) - 崔鹏碧的博文...

    pdftk是非常好用的PDF页面操作工具,能够切割.合并.提取指定页面等. 常用包括的功能如下:合并 PDF: 分割 PDF 页面: 旋转 PDF 页面: PDF 带密码访问: PDF 填加密码: 用 ...

  8. 【JavaSE】面向对象之多态、向上转型与向下转型

    本专栏为 JavaSE 的学习笔记及相关项目,专栏长期免费更新 ❤️ ❤️ ❤️ ❤️ 个人主页:Nezuko627 欢迎来访~~~ ⭐️往期回顾: [JavaSE]继承中内存发生了什么变化?这篇文章 ...

  9. meep php,科学网—centos5.4下meep配置 - 郑改革的博文

    centos5.4下meep配置 已有 4453 次阅读 2010-5-20 19:19 |个人分类:未分类|系统分类:科研笔记| meep 1. install BLAS sudo yum inst ...

最新文章

  1. python中文编码-python中文编码与json中文输出问题详解
  2. 《深入理解计算机系统》读书笔记四:操作系统的抽象
  3. 2021网络药理学研究的免费数据库、在线平台与软件工具汇总整理介绍
  4. spark 算子使用类变量_自己工作中超全spark性能优化总结
  5. Redis(1)---五种数据结构
  6. runtime java_Java runtime.getruntime()从执行命令行程序获得输出
  7. 为普通用户添加root权限
  8. jsp mysql demo_echart通过jsp连接查询mysql的demo - 贪吃蛇学院-专业IT技术平台
  9. ubuntu16.04下ROS操作系统学习笔记(六 )机器视觉-摄像头标定-ROS+OpenCv-人脸识别-物体跟踪-二维码识别
  10. 渗透测试基础-XSS漏洞简析
  11. 软件开发模型:瀑布模型,增量模型,原型模型,螺旋模型,喷泉模型,敏捷开发模型
  12. 在mysql内部有4种常见日志_MySQL练习题及答案
  13. 鱼之死,越狱章鱼和雾霾黑客
  14. 怎么把word目录里面的“目录”两个字去掉
  15. C#编写IE插件的一些经验
  16. unity 场景背景替换2D图片方法
  17. Zookeeper一致性级别分析,含爱奇艺,小米,腾讯,阿里
  18. Web前端开发学习笔记(2)(css3新特性)
  19. 请设计一个宠物社交APP。
  20. 【伊利丹】Hadoop2.0 NN HA实验记录

热门文章

  1. MySql 查询表字段数
  2. 为什么你不应该自行更新 Drupal 网站?
  3. Java初学者如何自学和自己定位解决问题
  4. 服务器产品选型与性价比图解
  5. iOS RunLoop详解
  6. 获取用户电脑的上网IP地址
  7. 201771010111李瑞红《面向对象的程序设计》第八周实验总结
  8. 你不知道的Vue响应式原理
  9. 经典的Java基础面试题集锦
  10. SoapUi测试,测试相关问答知识