分享
 
 
 

VISITOR模式--《敏捷软件开发》读书笔记(三)

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

考虑一下设计一个可以包含长方形、正方形、圆形以及文字的视图类。因为视图中每种元素的显示方式都不一样,所以我们很容易做出如下的设计:

在这里,我使用了COMPOSITE模式。对于COMPOSITE模式,可以参考我那篇《重读《设计模式》之学习笔记(五)--我对COMPOSITE模式的理解》

现在,我们来分析一下上面的这个设计方案。如果说,由于客户需求的改变,视图中每个元素的显示方法变了,那么我就就要更改每个类的Show方法(当然,客户还有把需求改回去的可能,那样我们还得把Show方法给改回去。虽然这样会使程序员痛苦不堪,但是这种可能还是存在的。我想,做开发的人都会遇到类似的情况。本书中第7章就有这样两句话:“软件开发最重要的事实之一:需求总是在变化。”,“在大多数软件项目中最不稳定的东西就是需求。”)。另外一种情况,客户要求视图可以通过打印机打印,那我们就得给每一个类就要加上一个Print方法。或者,客户要求可以统计视图中每种元素的个数,那么我们还得给类CTView添加一个统计的方法。每次更改,所有使用这些类的代码都要重新编译。

从上面的分析可以看出,我们这个设计是不成功的。在这个时候我们就应该使用VISITOR模式。VISITOR模式的好处就是可以在不改变现有类层次结构的情况下向其中增加新方法。听起来似乎很神奇,下面我就用代码来说明:

首先,创建一个CVisitor的抽象类:

class CVisitor

{

public:

virtual ~CVisitor();

virtual void VisitRectangle(CTRectangle *pRectangle) = 0;

virtual void VisitSquare(CTSquare *pSquare) = 0;

virtual void VisitCircle(CTCircle *pCircle) = 0;

virtual void VisitText(CTText *pText) = 0;

virtual void VisitView(CTView *pView) = 0;

};

我们要通过该类的子类来访问视图中的各个元素。

下面是重新设计后视图及其各个元素的代码:

class CContext

{

public:

virtual ~CContext();

virtual void Accept(CVisitor& v) = 0;

};

////////////////////////////////////////////////////////////////////////////////

class CTRectangle : public CContext

{

public:

void Accept(CVisitor& v) { v.VisitRectangle(this); };

};

////////////////////////////////////////////////////////////////////////////////

class CTSquare : public CContext

{

public:

void Accept(CVisitor& v) { v.VisitSquare(this); };

};

////////////////////////////////////////////////////////////////////////////////

class CTCircle : public CContext

{

public:

void Accept(CVisitor& v) { v.VisitCircle(this); };

};

////////////////////////////////////////////////////////////////////////////////

class CTText : public CContext

{

public:

void Accept(CVisitor& v) { v.VisitText(this); };

};

////////////////////////////////////////////////////////////////////////////////

class CTView : public CContext

{

public:

~CTView();

void Accept(CVisitor& v);

void Add(CContext *pContext);

private:

vector<CContext*> m_vContext;

};

CTView::~CTView()

{

while(!m_vContext.empty())

{

CContext *pContext = (CContext*)m_vContext.back();

m_vContext.pop_back();

delete pContext;

}

}

void CTView::Accept(CVisitor& v)

{

for(vector<CContext*>::iterator i = m_vContext.begin(); i != m_vContext.end(); ++i)

{

(*i)->Accept(v);

}

v.VisitView(this);

}

void CTView::Add(CContext* pContext)

{

m_vContext.push_back(pContext);

}

下面,我们为上面的类添加一个显示视图中各个元素并且计算各个元素个数的visitor:

class CShowContextVisitor : public CVisitor

{

public:

CShowContextVisitor();

void VisitRectangle(CTRectangle *pRectangle);

void VisitSquare(CTSquare *pSquare);

void VisitCircle(CTCircle *pCircle);

void VisitText(CTText *pText);

void VisitView(CTView *pView);

private:

int m_iRectangleCount;

int m_iSquareCount;

int m_iCircleCount;

int m_iTextCount;

};

CShowContextVisitor::CShowContextVisitor()

: m_iRectangleCount(0)

, m_iSquareCount(0)

, m_iCircleCount(0)

, m_iTextCount(0)

{}

// 下面以输出一句话来代替具体的显示

void CShowContextVisitor::VisitRectangle(CTRectangle *pRectangle)

{

cout << "A Rectangle is Showed!" << endl;

m_iRectangleCount++;

}

void CShowContextVisitor::VisitSquare(CTSquare *pSquare)

{

cout << "A Square is Showed!" << endl;

m_iSquareCount++;

}

void CShowContextVisitor::VisitCircle(CTCircle *pCircle)

{

cout << "A Circle is Showed!" << endl;

m_iCircleCount++;

}

void CShowContextVisitor::VisitText(CTText *pText)

{

cout << "A Text is Showed!" << endl;

m_iTextCount++;

}

void CShowContextVisitor::VisitView(CTView *pView)

{

cout << "A View is Showed!" << endl;

cout << "Rectangle count: " << m_iRectangleCount << endl;

cout << "Square count: " << m_iSquareCount << endl;

cout << "Circle count: " << m_iCircleCount << endl;

cout << "Text count: " << m_iTextCount << endl;

}

我们可以用下面的测试函数来验证我们的设计是否正确:

void Test()

{

CTView TestView;

CTRectangle *pRectangle = new CTRectangle;

TestView.Add(pRectangle);

CTSquare *pSquare = new CTSquare;

TestView.Add(pSquare);

CTCircle *pCircle = new CTCircle;

TestView.Add(pCircle);

CTText *pText = new CTText;

TestView.Add(pText);

CShowContextVisitor TestVisitor;

TestView.Accept(TestVisitor);

}

当然,输出跟我们期望的一样:

A Rectangle is Showed!

A Square is Showed!

A Circle is Showed!

A Text is Showed!

A View is Showed!

Rectangle count: 1

Square count: 1

Circle count: 1

Text count: 1

如果客户提出其他需求,我们只要添加一个相应的visitor就行了,而不用修改我们设计好的类。就算客户把需求改回去,只要重新使用我们以前写好的visitor的文件就行了。

VISISTOR模式最大的好处就是:很容易增加新的操作,而且能把这些相关的操作应该集中在一起,也就是visitor类里。

但是,VISITOR模式有一个致命的弱点,那就是添加相关的元素类比较困难。比如上面的例子中,如果客户要求可以在视图中添加三角形,那么我们除了要写一个CTTriangle类以外,还得修改抽象类CVisitor和它所有的子类。

所以,我们应该在类的层次结构已经固定而操作需要较多的添加或修改时才选择使用VISITOR模式。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有