Fork me on GitHub
面向对象闲话(二)——面向对象设计

惭愧,一个系列第二篇能跟第一篇隔两年之久,我还真是……

对象与类

上篇文章谈到了"什么是对象"问题。而事实上,我们所见过和学习的大多数面向对象语言,迎面而来的一个概念是:类。

遗憾的是,大部分程序语言的书籍,都是直接开始讲解类的概念,并没有着重强调类和对象的关系。所以,面向"对象"的语言,为何引入了这样一个"类"的概念呢?最简单的回答是,你不能够一个一个地去描述对象,那样太愚蠢了。

类对于一般的人类而言,同样是一个朴素的概念,在比对象认知稍晚些时候,人类开始具有抽象能力:小孩子不再说“我要那个”,而是开始表达“我要苹果”。

再更大一些时候(也许五六岁?),小孩子开始能够把苹果、梨子、香蕉等概念抽象成“水果”。这个时候,类层次关系开始出现在人的认识中。

面向对象编程的最重要意义在于它提供了一种接近人类思维的表达方式,只要人类的思维模式不发生根本性改变,面向对象绝对不会过时——它的表达形式可能多种多样,但是任何从根本上否认面向对象的所谓“反思”,皆不可相信。

分类与归类

对于类层次关系,还有个有趣的问题:分类与归类。

一种建立类层次关系的方法是,分类。就是,把所有对象放在一起,称作一个类Object,接下来,根据特征看看Object能分成哪些类,这些类又能分成哪些子类……以次类推。这样的方法得出的结果,类与类之间是不存在交集的,并且所有类最终都会继承一个基类Object。使用这样的逻辑的语言典型代表有Java和C#。

另一种方法是,归类。就是对于每一个对象,根据它的特征,看看它属于哪些类。这时,一个类可能有多个父类,这种方法,就会产生所谓的“多继承”关系。使用这样的逻辑的语言典型代表就是C++。

所以,实际上流传甚广的说法“C#和Java不能多继承类,只能提供多继承接口”是不恰当的(不能说不对),如果你使用C#或者Java这样的语言,从设计开始,就完全不可能出现需要多继承的情况。只有理解了语言背后采用的哲学,才能够正确使用语言。

面向对象设计

《C++程序设计语言》一书中,讲到了一系列设计的步骤:

  1. 发现类
  2. 描述操作
  3. 描述依赖性
  4. 描述接口

发现类最简单和行之有效的方法就是从需求描述中寻找。一些名词往往对应着一个类。而TC++PL中还提到了几种情况:

  1. 动词可能意味着对象上的操作、全局函数或者类
  2. “重复”、“将……作用于”往往意味着迭代器对象
  3. 形容词“可存储的”、“并行的”、“注册的”、“约束的”可能成为类(winter注:C#或者Java中,它们更可能作为接口或者attribute)

有趣的是,自然语言非常自由,所以程序未必应该完全对应于需求描述,比如著名的“狗咬人”问题:

A dog bites a person. A person is bited by a dog.

写成程序,就是:

Person person = new Person;

Dog dog = new Dog;

dog.bite(person);

person.isBitedBy(dog);

两种设计哪个更好呢?

我们在写程序的时候,不可能受到需求文档使用句型的影响,这个时候,我们必须回到对象的本质上面:标识、状态、行为。而对象的行为,必定是改变对象自身状态或者对外输出对象状态的。

这样,在这个场景里面,答案就是显而易见的了:

人的状态改变,所以人应该有hurt方法。

狗的状态未改变,但是咬这个动作必须根据它的内部状态输出伤害,所以dog应该有biteDamage方法。

最后,一个良好的设计是:

person.hurt(dog.biteDamage());

所以,不要陷入“面向对象语言描述要尽量跟需求描述一致”,正确的抽象才是根本。

设计实践

一个常见的错误是把类与模块相混淆。比如以下类就很可疑:

Login类

DBHelper类

BusinessLogic类

模块是一个相对独立的功能单元,一般来说,模块可能包含很多类。如何避免这样的错误呢?

我自己喜欢使用先对象、后类的设计方法。也就是说,先完全不考虑类的问题,将系统中的具体对象识别出来。我在OOD阶段做的第一件事,就是在设计图中间画一条线,线上面的对象是可见的,线下面是不可见的逻辑对象。

下图是我编写的一个黑白棋游戏(shaofei.name/othelloAI/othello.html)时画的对象图:

image

这种方法可以帮助我们发现一些坏味道,比如,每一条跨过分割线的依赖线条,都应该是同一个方向的。

应用了MVC模式以后,Controller位于线的中间,它阻止了UI对象直接控制业务逻辑:

image

因为是one man project我使用的图形比较简单,在正式的项目中,UML的对象图和时序图都是非常强有力的工具。

软件设计

 
摘要: 惭愧,一个系列第二篇能跟第一篇隔两年之久,我还真是…… 对象与类 上篇文章谈到了"什么是对象"问题。而事实上,我们所见过和学习的大多数面向对象语言,迎面而来的一个概念是:类。 遗憾的是,大部分程序语言的书籍,都是直接开始讲解类的概念,并没有着重强调类和对象的关系。所以,面向"对象"的语言,为何引入了这样一个"类"的概念呢?最简单的回答是,你不能够一个一个地去描述对象,那样太愚蠢了。 类对于一般的人类而言,同样是一个朴素的概念,在比对象认知稍晚些时候,人类开始具有抽象能力:小孩子不再说“我要那个”,而是开始表达“我要苹果”。 再更大一些阅读全文
posted @ 2012-04-02 14:24 winter-cn 阅读(750) | 评论 (1) 编辑
 
摘要: 主要是现在这设计模式的文章太多,而且各种烂各种曲解,看的人心烦,烦到忍不住想自己写一个系列把它们说清楚——但是呢,转念一想,我写的再怎么清楚能有GoF清楚呢,怎么能有GoF的影响力大呢,GoF明明白白地摆着,还有这么多人乱搞,我又能做什么呢?所以想了半天,我觉得就写一篇文章来吐槽好了。 - -!阅读全文
posted @ 2012-03-10 22:15 winter-cn 阅读(10854) | 评论 (136) 编辑
 
摘要: 写这个文章,主要是因为网上对C#字符串和享元模式的误解比较多。 Flyweight模式 先说这名字,fly呢,就是苍蝇,没错这里面不是飞的意思,是苍蝇的意思,weight大家都知道,就是重量,苍蝇的重量,就是非常非常轻的意思。所以Flyweight模式就是处理非常非常轻量级对象的一个东西。 Flyweight的目标是解决大量细粒度对象的内存消耗问题,当然,巧妇难为无米之炊,任何模式和手法都不能凭空造出内存来,所以享元模式针对的情况是这些细粒度对象的中数据有重复的情况。 Flyweight的做法是,把对象的状态(通常用属性表示),分成两个部分,一部分是内部状态,另一部分是外部状态。内部状...阅读全文
posted @ 2012-01-21 02:09 winter-cn 阅读(1524) | 评论 (11) 编辑
 
摘要: 在网上常常能看到文章讲面向对象的三大基本特征"继承"、"封装"、"多态",我以为这是坊间流传的最不靠谱的一个说法。本文先谈谈其中讲到的一个特性:多态。多态是一个跟面向对象完全正交的概念,两者之间可以说没有任何必然联系。阅读全文
posted @ 2011-05-03 21:57 winter-cn 阅读(3418) | 评论 (67) 编辑
 
摘要: 1.把大象关进冰箱里问题面向过程:把大象关进冰箱里: 把冰箱门打开=> 大象放冰箱里=> 冰箱门关上面向对象:冰箱.开门()冰箱.放入(大象)冰箱.关门()函数式:关进(冰箱,大象): 关门(放入(开门(冰箱),大象))————————————...阅读全文
posted @ 2010-07-02 19:55 winter-cn 阅读(2461) | 评论 (29) 编辑
 
摘要: "客户说是这样的!","客户根本没这个需求!"需求对程序员而言,往往犹如圣经,客户说了,我们就要这样做。但是,往往客户明天就变了一幅嘴脸,原本明明说好按钮在下面的,结果现在一定要挪到上面去,明明不需要保存颜色的,结果现在一定要把颜色也加上。于是我们从头改到尾,从下改到上,好不容易改完,好了,客户明天想法又变了!怎么办?继续改!好吧,厌倦了这种可怕的生活了吧,于是我们希望应对这种问题,于是我们决定,...阅读全文
posted @ 2010-05-09 01:40 winter-cn 阅读(1594) | 评论 (37) 编辑
 
摘要: 原型模式的意图经常被误解为复制对象,本来我觉得设计模式完全无必要(也不太敢)写任何文章,GoF书里写的清晰准确,还有无数例子,但我看到过无数文章把原型模式彻彻底底地变成了从已有对象方便地复制一个新对象,所以决定趟趟浑水也来白话一篇,说的不对的地方欢迎大家拍砖,猛拍,狂拍,往死里拍……阅读全文
posted @ 2009-12-02 02:32 winter-cn 阅读(2479) | 评论 (70) 编辑
 
摘要: 先来看一个例子:[代码]Fibnacci数列,相信是个程序员都能写出来,重点是,这个Fibnacci数列的计算完全是在编译时完成!后面的print也是如此,当你把参数调得很大时,运行时间不会有任何改变,但是你会花费长时间在编译阶段。如果你听说过一些模板元编程,你一定会知道"C++模板是图灵完备的"这个说法。模板元是如何图灵完备的?答案是,模板元跟Functional原理是一样的。模板的本质是定义与...阅读全文
posted @ 2009-09-05 23:00 winter-cn 阅读(1531) | 评论 (12) 编辑
 
摘要: 最近跟一些朋友讨论关于工厂模式的一些东西,觉得很有必要写篇东西把抽象工厂的应用方法写清楚。GoF在抽象工厂一章中没有花很多篇幅在如何应用抽象工厂上,希望本文理解抽象工厂能起到一点作用,能让网络上少一些诸如"工厂用来重构switch的言论",若能对各位实际开发有点启发,则非常荣幸。阅读全文
posted @ 2009-04-15 21:12 winter-cn 阅读(2617) | 评论 (43) 编辑
 
摘要: 如果你要把一只猫卖给一个计算机专家,千万不要说它有多聪明多可爱,而要说,这是一只面向对象的猫。这是程序员对程序员的揶揄。 面向对象渗透到软件的各个领域,既然找不到银弹,这颗铜子弹成了我们对抗人狼的最佳武器。 在这样的世界里,你有没有想过,什么是面向对象呢? 如果你习惯性地说:继承、封装和多态,那么请你继续读完这篇随笔吧,它会带你认识一个真实的OO 阅读全文
posted @ 2008-11-18 00:31 winter-cn 阅读(647) | 评论 (8) 编辑
 
摘要: 软件体系结构是具有一定形式的结构化元素,即构件的集合,包括处理构件、数据构件和连接构件。处理构件负责对数据进行加工,数据构件是被加工的信息,连接构件把体系结构的不同部分组组合连接起来。这一定义注重区分处理构件、数据构件和连接构件,这一方法在其他的定义和方法中基本上得到保持。 
  下面是Garlan和Shaw对通用体系结构风格的分类: 
  (1)数据流风格:批处理序列;管道/过滤器 
  (2)调用/返回风格:主程序/子程序;面向对象风格;层次结构 
  (3)独立构件风格:进程通讯;事件系统 
  (4)虚拟机风格:解释器;基于规则的系统 
  (5)仓库风格:数据库系统;超文本系统;黑板系统 
阅读全文
posted @ 2008-06-01 22:27 winter-cn 阅读(253) | 评论 (0) 编辑
 
摘要: 在这个js框架随处乱跑的时代,你是否考虑过写一个自己的框架?下面的内容也许会有点帮助。 
PrototypeJS JQuery YUI Mootools DWR DOJO Ext 这些框架并非遥不可及也不是完美无瑕。阅读全文
posted @ 2008-05-22 14:21 winter-cn 阅读(1434) | 评论 (5) 编辑
posted on 2012-04-04 21:38  HackerVirus  阅读(258)  评论(0编辑  收藏  举报