分享
 
 
 

《设计模式解析》第4章 一个标准的面向对象解决方案

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

第4章

一个标准的面向对象解决方案

概述

本章对我们在第3章讨论的问题,“一个迫切需要灵活代码的问题”,给出一个初步的解决方案。这是一个合理的初步尝试,它能够迅速地解决问题。不过它却漏掉了一个重要的系统需求:CAD/CAM系统持续演化时所需要的灵活性。

在本章,我基于面向对象描述了一个解决方案。它并不大,但确实能起作用。

注意:在本章的主体部分,我将仅展示Java代码示例。对应的C++代码示例在本章的末尾。

用特殊手段来解决问题

考虑第3章“一个迫切需要灵活代码的问题”里描述的那两个不同的CAD/CAM系统。我该怎样构建一个信息析取系统,才能使得不管使用哪一个CAD/CAM系统,对客户对象而言它看起来都是一样的呢?

通过思考如何解决这个问题,我推断如果我能解决槽的问题,那么我也能用同样的方案来处理剪切块、洞等特性的问题。通过对槽的思考,我发现我能够轻易地特化每一种情况。也就是说,我将有一个Slot类。在面对V1系统时,我将为它生成一个派生类,在面对V2系统时,我将为它生成另一个派生类。如图4-1所示。

图4-1 槽的设计

通过对每一个特性类型进行扩展,我就完成了这个解决方案,如图4-2所示。

图4-2信息析取问题的原始解决方案

当然,图4-2展示的是相当高层的设计。每个V1xxx类将会和相应的V1库通信,而每个V2xxx类则会和V2模型中的相应对象通信。

如果单独地来看每一个类,这个解决方案就更加容易想象了。

l V1Slot可通过记住它所从属的模型以及被实例化时它在V1系统中的ID来实现。这样,任何时候当V1Slot的一个方法被调用以获取信息时,这个方法将不得不调用V1中的一序列子例程从而得到相关的信息。

l V2Slot将会以一种更加简单的方式被实现,即每个V2Slot对象将会包含V2系统中的一个相应的槽对象。这样,任何时候当该对象被查询某种信息时,它将简单地将这个请求传送给OOGSlot对象并将响应回送到先前发出请求的那个客户对象。

图4-3展示了一张包含V1和V2系统的更为详细的图。

图4-3 初始方案

我将为这个设计中的两个类提供代码示例。这些示例仅仅用于帮助你理解如何实现这个设计。如果你觉得你能够实现这个设计,请随意略过下面的Java代码示例(C++代码示例在本章的末尾)。

示例 4-1 Java代码片段:

实例化V1特性

// 实例化特性的代码片断

// 不提供错误检验——仅用作演示

// 每一个特性对象需要知道和它对应的模型号码以及特性ID

// 以便在收到请求时获取信息

// 注意这样的信息是如何传送进每个对象的构造函数的

// 打开模型

modelNum = V1OpenModel(modelName);

nElements = V1GetNumberofElements(modelNum);

Feature features[] = new Feature[MAXFEATURES];

// 为模型中的每一个特性做

for(i= 0; i < nElements; i++) {

// 确定当前的特性并创建适当的特性对象

switch(V1GetType(modelNum, i)) {

case SLOT:

features[i] = new V1Slot(modelNum,

VlGetID(modelNum, i));

break;

case HOLE:

features[i] = new VlHole(modelNum,

VlGetID(modelNum, i));

break;

}

}

示例 4-2 Java代码片断:

V1方法的实现

// modelNum和myID是私有成员

// 它们包含对应模型和特性(在V1中的)的有关信息

class V1Slot {

double getx () {

// 为V1调用适当的方法以得到所需要的信息。注意:

// 为得到信息,这个方法实际上可能调用V1中的几个方法

return V1GetXforSlot(modelNum, myID);

}

}

class VlHole {

double getx () {

// 为V1调用适当的方法以得到所需要的信息。注意:

// 为得到信息,这个方法实际上可能调用V1中的几个方法

return V1GetXforHole(modelNum, myID);

}

}

示例 4-3 Java代码片段:

初始化V2特性

// 实例化特性的代码片断

// 不提供错误检验——仅用作演示

// 每一个特性对象需要知道它在V2中对应的特性

// 以便在收到请求时获取信息

// 注意这样的信息是如何传送进每个对象的构造函数的

// 打开模型

myModel = V2OpenModel(modelName);

nElements = myModel.getNumElements();

Feature features[] = new Feature[MAXFEATURES];

(待续)

示例 4-3 Java代码片段:[1]

初始化V2特性(继续)

OOGFeature *oogF;

// 为模型中的每一个特性做

for (i= 0; i < nElements; i++) {

// 确定当前的特性并创建适当的特性对象

oogF = myModel->getElement(i);

switch(oogF->myType()){

case SLOT:

features[i] = new V2Slot(oogF);

break;

case HOLE:

features[i] = new V2Hole(oogF);

break;

}

}

示例4-4 Java代码片段:

V2方法的实现

// oogF是V2中对应的特性对象

class V2Slot {

double getX (} {

// 调用oogF的适当方法以得到所需的信息。

return oogF->getX();

}

}

class V2Hole {

double getX () {

// 调用oogF的适当方法以得到所需的信息。

return oogF->getX();

}

}

在图4-3中,我增加了几个特性所需要的方法。注意它们是如何因特性的类型而不同的。这表明我没有在整个特性体系中使用多态。然而这并不成问题,因为不管怎样,这个专家系统总是需要知道它所拥有的特性的类型,它需要从不同类型的特性得到不同种类的信息。

This brings up the point that I am not so interested in polymorphism of the features. Rather,我需要能够插即用不同的CAD/CAM系统而不需要改变这个专家系统。

我想要做的——透明地处理多个CAD/CAM版本——给我几点暗示,这不是一个好方案:

方法中的重复——我能轻易想象那些调用V1系统的方法,它们之间将会有许多相似之处。比如Slot的V1getx和Hole的V1getx将会非常相似。

凌乱——它不总是一个好预示,但采用这个方案,它是另外一个增强我不适的因素。

高耦合——这个方案是高耦合的,因为各个特性彼此间接地相关联。这些关系表明如果以下事情发生,我们很可能需要修改所有的特性:

——需要一个新的CAD/CAM系统。

——修改一个已有的CAD/CAM系统。

低内聚——内聚是相当之低,因为执行核心功能的方法被分散在这些类之中。

然而我最关心的问题出现在我深入观察特性的时候。想象在CAD/CAM系统的第三个版本出现时会发生什么。组合爆炸将会杀死我们!看看图4-3中类图的第三行。

l 这里有五类特性。

l 每一类特性有两个类,每个CAD/CAM系统一个类。

l 当第三个版本加入时,每组将拥有三个类,而不是两个类。

l 我拥有不再是十个类,而是十五个类。

可以肯定,维护这样一个系统,我不会从中获得任何乐趣!

分析的一个陷阱:过早关注太多细节。

在开发过程中,我们的分析可能出现的一个普遍问题是我们过早的陷入到细节之中。这是自然的,因为细节问题容易处理。细节的解决方案通常是显而易见的,但却不一定是最好的开端。在拥抱细节之前,能延迟多久,就延迟多久。

在这个案例中,我达到一个目标:特性信息的一个共同的API。同时我还从职责的视角定义了对象。然而,我为此付出的代价是为每一件事情创建特殊的case。在我得到新的特殊的case时,我将不得不以同样的方式实现它们,并因此付出高昂的维护代价。

这是我第一个令人羞愧的解决方案,我很快就开始讨厌它。相比我在上面所给出的更多逻辑上的理由,我的直觉更多地助长了这种感觉。我觉得这里面有问题。

在这个案例中,我强烈地感觉到一定有一个更好的解决方案。然而,即便是两个小时以后,这依然是我能够想到的最好的解决方案。正如你将会在本书后面看到的,这个问题是我的通用途径。

注意你的直觉

对于设计质量,肠胃的直觉是一个强大得令人吃惊的指标。我建议开发人员学着去聆听他们的直觉。

说肠胃的直觉,我指的是当你看到某种你不喜欢的东西时,你胃里的那种感觉。我知道这听起来是不科学的(它确实不科学),但是我的经验常常告诉我,每当我的直觉不喜欢一个设计时,一个更好的设计就在某个角落。当然,有时候附近可能有好几个不同的角落,而且我不能总是确定它藏身何处。

总结

我展示了如果对每一件事情都使用special-casing,解决这个问题是多么容易。它允许我在不改变现状的前提下增加额外的方法。然而,这也有几个不利之处:高度的重复,低内聚以及类爆炸(来自将来的变化)。

对继承的过度信任将会导致比本该需要的(或者至少是,比我认为应该需要的)更加高昂的维护代价。

补充:C++代码示例

示例4-5 C++代码片段:

实例化V1特性

// 实例化特性的代码片断

// 不提供错误检验——仅用作演示

// 每一个特性对象需要知道和它对应的模型号码以及特性ID

// 以便在收到请求时获取信息

// 注意这样的信息是如何传送进每个对象的构造函数的

// 打开模型

modelNum = V1OpenModel(modelName);

nElements = V1GetNumberofElements(modelNum);

Feature *features[MAXFEATURES];

// 为模型中的每一个特性做

for(i = 0; i < nElements; i++) {

// 确定当前的特性并创建适当的特性对象

switch( V1GetType(modelNum, i)) {

case SLOT:

features[i] = new V1Slot(modelNum,

VlGetID(modelNum, i));

break;

case HOLE:

features [i] = new VlHole(modelNum,

VlGetID( modelNum, i));

break;

}

}

示例 4-6 C++代码片断:

V1方法的实现

// modelNum和myID是私有成员

// 它们包含对应模型和特性(在V1中的)的有关信息

double VlSlot::getX () {

// 为V1调用适当的方法以得到所需要的信息。注意:

// 为得到信息,这个方法实际上可能调用V1中的几个方法

return V1GetXforSlot( modelNum, myID);

}

double VlHole::getX (} {

// 为V1调用适当的方法以得到所需要的信息。注意:

// 为得到信息,这个方法实际上可能调用V1中的几个方法

return V1GetXforHole( modelNum, myID);

}

示例 4-7 C++ 代码片段:

初始化V2特性

// 实例化特性的代码片断

// 不提供错误检验——仅用作演示

// 每一个特性对象需要知道它在V2中对应的特性

// 以便在收到请求时获取信息

// 注意这样的信息是如何传送进每个对象的构造函数的

// 打开模型

myModel = V2OpenModel(modelName);

nElements = myModel->getNumElements();

Feature *features[MAXFEATURES];

OOGFeature *oogF;

// 为模型中的每一个特性做

for (i= 0; i < nElements; i++) {

(待续)

示例 4-7 C++代码片段:

初始化V2特性(继续)

// 确定当前的特性并创建适当的特性对象

oogF = myModel->getElement(i);

switch(oogF->myType()){

case SLOT:

features[i] = new V2Slot(oogF);

break;

case HOLE:

features[i] = new V2Hole(oogF);

break;

}

}

示例4-8 C++代码片段:

V2方法的实现

// oogF是V2中对应的特性对象

double V2Slot::getX (} {

// 调用oogF的适当方法以得到所需的信息。

return oogF->getX();

}

double V2Hole::getX () {

// 调用oogF的适当方法以得到所需的信息。

return oogF->getX();

}

[1] 译者注:紫色的代码片段在笔者参照的电子书中并无对应原文,是参照本章末尾的C++代码片断补充的。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有