下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、从“白箱复用与黑箱复用从“白箱复用与黑箱复用.”谈到“概要设计”与“详细设计”的划分及其它(题目女?长J斗胆与鲁迅的魏晋风度及文章与药及酒之关系比试)“封装、继承、多态”是面向对象编程的三大特性。“美丽、智慧、大方”是(我认为的)女人应具有的三大优点。然而我可以经常夸奖一个女人“最美丽,最智慧,最大方”;但我从来不敢自吹自己写的程序“最封装,最继承,最多态”。因为“封装、继承、多态”之间属于相形相克。相形者,指三者中缺少任意一个,则余下二个都将不存在;相克者,则是三者中任意一个如果被发挥或表现到极限,则余者同样无法生存。正由于此,可以说,在抽象意义上,任何时候我们进行的程序设计1,都是力图在针
2、对当前的问题,调整出这三个特性的各自的最佳“实现度”。这也是所有程序员在不断培养,苦苦追求的设计能力。什么叫高手、老手?什么叫新手、生手?虽然我懂得“盐是咸的,味精是甜的,姜是辛",但我始终做不出一手好汤。虽然我也明白“油门、刹车、方向盘”的作用,但当舒马赫在F1赛道上艺术地操作这三者时,我还在某个坡路上流汗:“又要刹车又要加油门,为难人啊!”是的,编程的难点与技艺正在于此。好的程序设计得让人几乎要归之为“艺术”;而糟糕的设计,就像一个蹩脚的厨子走了,留下一桌恶心的菜,你却不得不去咀嚼它,消化它,其间之苦,真非言语所能表达的。很不幸,我就是这样一个蹩脚的厨子,说起来“饭菜”也做了10
3、年矣,但依然无法用“封装、继承、多态”做“面向对象”这一道菜。我这学习编程的10载,倒几近完整地见证了中国软件开发行业发展的全过程。我也看到了很多程序员都和我一样,在不断的摸索、碰壁中缓慢地成长。好在,编程界的泰斗终于感到于心不忍了,开始提供编程界的“菜谱”。这就是如今火热之至的“设计模式”2。当然,正如万有引力一直存在着,而不是等到牛顿被苹果砸了以后才出现。“设计模式”其实一直存在于优秀程序员的设计里。不过是没有形诸于文字,而是表现在代码中。当然,也远远没有泰斗们所归结出来的模式那样具备抽象性、概括性和通用性。说到设计模式,我只是想进一步证明,一个程序员面对不同问题,其驾驭面向对象三大特性的
4、能力的重要性。这篇小文不准备讲那23个经典模式。我想以最基本的“白箱、黑箱复用”为例,开始我的论题。首先,不要看到“黑白箱”就想到测试。“继承、聚合”分别二者的原意,在不同的编程语言里可能有不同的术语,这里我们用最直观的“白箱”,“黑箱”来表述。所引用的代码,来自于早些日子我在“非程序员”(一个国内专讲UML的网站)的BBS的发言。那时有一位可能比我还菜的家伙在上面质疑复用为何要分“白箱”“黑箱”,我一时技痒,上去口水了一番。下面我会通过一个有关“项目”的故事,将我当时用来论证的代码,用成一次代码设计的演进。先得说说“继承”。C+提供了三种三继承"private,protected,
5、public”。考虑到JAVAf口C#均只能支持最通用的public继承,我们这里就仅以此为继承的标准。所谓的public继承,字面意思是“公开继承”。要个比方就是除了“老子”声明要带到阴间的财产,其它的,它继承者,都可以获得,使用。(这是一个蹩脚的比喻,但我想你会承认它确实表达了公开继承和其它继承的不同L)。“公开继承”就是一种最常见的白箱复用的设计。它表示:“B复用A的功能,并且B可以了解A的内部细节”。接下来我们讲“黑箱复用”。可以推测,它表示:“B复用A的功能,但B无法看到A的内部细节”。这在像C#£JAVA这样不支持私有或保护继承,也不支持多重继承的语言,是一种极其常见的设
6、计。“黑箱复用”的实现方法是:如果B类想复用A类的功能,不是从A类派生,而是将A类的对象,声明成为B类的成员数据。嗯,是该来举一个“实际项目”了。通过演示这个“项目”的实作,我想,就算你是外行人,你也应能了解一点:事实上程序员最后用指头敲写代码,其实那不算是编程,真的编程,在于之前他的大脑必须做的分析与设计(这句话一会儿我会继续重复)。(声明一下,以下故事纯属虚构)10年的编程生涯,嗯,我的家里有5台电脑了。书房和卧室各有一间,但因为搬家所以常常换,老婆也有一个(不允许再多,也不允许换)。前三年又有了一个女儿,由于出生“脑香门第”,所以最近小家伙也开始用上我的电脑。这些算是项目的背景和资源。项
7、目的初始需求是这样:结蜡后每天晚上我都在书房时和电脑打交道到很晚。于是我老婆认为应该把那台笔记本搬到卧室,并责令我写个程序,可以实现她在卧室通过电脑向我发号施令。这样就有了本项目的产生。作为“客户”,老婆当然希望她可以发各种各样的命令;而作为该项目的产品经理、技术架构师,开发负责人,代码撰写者及测试师于一身的我,当然明白正确地引导客户的需求是一个项目是否成功的最重要前提之一,同时也是对客户负责的表现。我向她解释了一个无所不包的软件,首先将让用户界面变得繁杂无比,用户极易操作失误,而失去耐心;其次是众多功能之间将互相牵制,导致表面上得到一个无所不包的软件,实际功能却强项不强,弱项更弱等等,最后我
8、也委婉地提到了它对开发周期的可能的影响,以及在开发和后期维护费用上恐怕会出现几何级的增长,最后约定是只实现最为常见两条命令的发送:a) “老公限N分种内来睡觉,否则门将反锁。”;b) “脚已洗好,请来端盆。”有了具体的需求描述,这下显得清楚多了。当然,老婆也不吃素的。在具体功能之外,也提出一些速度,性能的要求(这样就可以杜绝我在限定时间内无反应,会推托是软件传送命令太慢等后路),最重要一点也提到该系统应具备一定的扩展性,以备今后增加新的命令的要求等等,需求之后是概要设计,首先我确定通过局域网,采用SOCKE睬实现传输,而不是通过串口并口红外线或蓝牙。无论是硬件还是软件,这方面的资源均充备,这算
9、是对开发资源做了认真详实的调研并确定。然后我把数据流图画到了概要设计。在概要设计内,我也决定了将有采用.Net+C#来进行开发,当然,也提到了采用Win32+C或C+<JAVA的可能性。最后我也在概要设计里提出,由于该系统的简小,在速度,性能,及扩展性并无太多要求,所以应将设计的天平侧向于“易用性”(以博取老婆欢心)。界面上的东西,及第二条命令数据的流程,均略。之后开始详细设计,秉承概要设计的思想,我觉得将两条命令的发送分别提供。那么要不要采用“多态”?即是否将发送两条命令的发送动作取同一命名?考虑到以后可能会有新的命令扩展,这里采用多态会带来麻烦。所以我在这一步详细设计里,放弃多态特性
10、之一。很显然,我对“扩展性”虽然没有完全忽略,甚至是在概要和详细设计里都可见“扩展性”的影响,但问题我缺少对“扩展”与“易用”做深入的,更具体的考虑。所以下一步的错误的根本,已经埋下来。下面我开始提供设计的伪代码。假设C#提供基类Socket,用于在网络发送数据。我没有标出函数参数data的数据类型。但显然,作为该类的设计者,他并不知道你要发送什么样的数据(老婆的命令?老板的命令?)所以这个Send()可以发送的data肯定是无具体含义的。我们可称为无格式的数据。而我们要发送的两个有着具体意义的命令。根据前面设计。我们需要为这两个类分别提供发送函数。当然,这两个发送具体命令的函数,最终肯定是要
11、调用上述系统提供的Send(渝令来完成实际发送操作。让我们来继承它:是的,我派生了一个新类:“卧室的Socket'。这一命名表征了我心里其实很清楚,我要设计一个仅供卧室那端的人使用的Socket而我对两个具体的命令,提供了名字直观的两个函数,这也充分体现了我正在按概要设计的要求进行详细设计:请看,通过我对原来的Socket类的派生,以及我对它的Send(冽作的扩展,就在原来抽象的,无特别应用方向的类的基础上,得到一个新类,它有具体应用方向,也有具体意义的动作。这比起拿起Socket就直接使用的人(这类人往往是C的高手);或者比起为了“多态”而“多态”,从而把新加的两个函数也命名为Sen
12、d()的人(这往往是刚接触C+时几天的人),我的这个设计,确实显得很正确。然而,事实上,这个设计在面向对象编程的世界里,仍然是一个拙劣的设计。在面向对象编程领域里有经验的程序员,我想已经看出其中的欠妥之处。假设这个项目付诸实施了。当老婆的人倒也没有提出什么扩展。光阴荏苒,结蜡三年过去了,我们有了一个孩子;然后又是三年过去了,我们的孩子也开始会在电脑上施展她的天才。对这个软件提出了她看法:“爸爸,应该增加一个给我送牛奶的命令”。扩展需求终于出现了。然而,6年过去了,我对这个软件的记忆是零。没有看设计文档,我就开始看代码。然后我看到一个类:BedroomSocket我开始使用它,然后我看到它有三个
13、有关发送的方法:boolSend();boolSendSackCommanc|();boolSendFootBathCommand();作为一个使用者,我并不想去花时间了解BedroomSocket的具体细节,所以我并不知道其中那个Send()其实是来自Socket这个基类(在实际大型项目开发中,比如大型ERF;专门写上层业务逻辑的程序员,甚至是没有权限可以看到他所使用的类的设计文档,更看不到源代码)。我错误地认为当初设计BedromSocket时,可能是为了易于对付一些新加的命令,所以提供了一个通用的Send(方法。就这样,纵然有100个项目经理,也无法在第一时间内阻止我义无反顾地通过Bed
14、roomSocket的实例来调用Send(),我会发现这个Sendfl®在太好用了,什么格式的数据都可以发送。也就这样,一个项目原来的设计倾向开始出现偏差。如果这种情况在多人之间出现多次,那么一个项目的设计风格与模式,就将被每个人的理解而肢解成五花八门。不仅仅是在人的方面:理解,改错,扩展等方面会增加难度,而且对于代码本身,也必然由于模块之间接合困难,而需要增加很多附加代码,最终是程序运行效率低下。你可以怪罪后来者(在这个例子里仍然是“我”),不去深入学习需求,概要,设计文档。但正如我前面所言,对一个大的项目,会按设计的层次分成多个子项目;要每一个人都去学习每一个项目的详细设计文档,
15、并且最好是从需求开始看起,这是不可能的。再考虑那些中间件的实现,通常都凝聚了一个软企的核心技术一一这种情况下,分配在实现业务逻辑的程序员,没有权限去学习中间件的具体设计思路。大家看到的,永远只是对方的接口。类似于我看到了Bedroom接口透露出来的三个方法,但我不知道这些方法的实现背景。针对这个例子中碰上的问题。我们可以将“白箱复用”(这里是继承),改为“黑箱复用”。在这次设计中,Socket的对象成为类BedroomCommand的一个成员。类BedroomCommand不再是通过“继承”来获得网络发送的能力。而是通过“拥有"一个Socket对象来获得该对象所有公开的能力。由于So
16、cket的对象sender在BedroomCommand里被声明为私有(private,或者也可以是保护protected),所以,有关Socket网络发送的能力,仅有BedroomCommand的设计者可以直接获取和使用。这就是“黑箱复用”的一种常见方法。BedroomCommand的使用者不再需要面对Send()。它所能看见和用到的接口,是BedroomCommand提供三个意义明确的发送方法:publicboolSendSackCommand,();publicboolSendFootBathCommand,();publicboolSendMilkCommand();这样,我们就解决前
17、面的问题。我们实现了一个类,它提供了它应有的功能,同时杜绝提供它不该有的功能。这正是一个良好的设计的基本标准。这么看来,是不是黑箱复用总是白箱复用来得正确?答案当然不是如此,下面我们继续给这个设计制造问题一一想要给“设计”制造问题,最好的办法就是修改“需求”了。我们假设原来的Socket类在除了提供一个公开对外的Send()方法以外,还提供了一个保护的SetOptions(方法。该方法用于对网络发送做一些参数调整,以便可以定制出更符合具体要求的网络发送能力。classSocketpublicSend(data);/发送数据protectedSetOptions(,);/定制网络条件,;Sock
18、et的设计者,认为SetOptions这一能力是不能直接对外公开,所以SetOptions被设计为“保护(protected)”。这就使得:除非是Socket本身或它的继承它的类,否则就无法使用到SetOptions。我们前面讲的“白箱复用”,正是继承。这就给我们出现一个两难:如果使用“白箱复用”,那么我们可以获得我们想要的SetOptions,但同时我们却不得不公开了我们不想公开的Send。如果使用“黑箱复用”,那么们可以不公开Send但却无法获得SetOptions的能力。由此产生了“复合复用”。(一般来说,SetOptions()在Socket里不会被设置成virtual,所以在C#里,
19、我们加上new指示符,而在C+,最直接的方法是另取一个名字,比如叫SetMyOptions(),如止匕可以避免关于编译器说我们覆盖了基类同名函数的小问题。如果SetOptions是virtual类,则不存在该问题。另外,在C+里,base.SetOptions()应写成:Socket:SetOptions()问题得以完美解小。Socket提供的超强能力,只&BedroomCommand的设计者能获得,使用。并且通过BedroomCommand的设计者来决定要对外公开哪些能力。任何一个后来的程序员,无论他是老手还是新手,都不会在使用BedroomCommand上出现偏差。就算是我在下一个6年之后,我也能正确地使用BedroomCommand。这样的一个设计,针对当前问题,做到既有“粒度”又有“弹性”。由此引申出几个话题。第一,关于需求分析、概要设计、详细设计的划分。概要设计更多地是在将需求模块转换为设计模块。它从总体上把握了技术设计的可行性。着重表达各个设计模块之间的静态及动态关系,并由此确定各设计模块之间接口规划。一般地说,概要设计并不需求每个写代码的人都参加直接参加设计。它要求项目技术负责人了解技术实现上可行性,总体难度;它也要求技术负责人具备把握整个设计的风格、倾向、取舍;但它并不要求技术负责懂得每一个
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 培训机构师资管理与考核方案
- 2024至2030年中国项目进度管理软件行业投资前景及策略咨询研究报告
- 2024至2030年中国草莓沙蓉数据监测研究报告
- 2024至2030年中国老醋数据监测研究报告
- 小学第二课堂实施方案
- 拆除违章建筑工程承包合同
- 2024至2030年中国山茶籽油数据监测研究报告
- 2024至2030年速冻面团改良剂项目投资价值分析报告
- 2024年逆变调速器项目可行性研究报告
- 2024年炉座号位置自动检测器项目可行性研究报告
- 植物检疫证书
- 粮食仓储场建设项目可行性研究报告
- 输送机施工方案.doc
- 海澜之家特许经营协议合同
- 大众汽车入侵北美市场
- 建设银行员工劳动合同
- 医院医用气体管路的设计计算(2014)
- 人教版统编高中语文“文学阅读与写作”学习任务群编写简介
- SQE质量月报参考格式
- 初中物理实验室课程表
- CTQ-2型支线接触网故障智能切除装置概述
评论
0/150
提交评论