访问者模式 Visitor Pattern

 访问者模式

  作者:崔涛涛(07770225)

  今天我要在这里介绍的是C#设计模式中的访问者模式。

  在介绍访问者模式之前,我们先在这里看个我虚构与今天我要讲解的模式有关的简短的问题。

问题:有一天,有两个不同公司的业务员来到两家相同的公司进行业务商讨。两个业务员对同一个公司的业务不相同。试着编程实现给过程。

 

  倘若您作为编写者,一般情况下您会如何来处理这个问题

  我在这里展示我的不假思索的解决方案:

  类图如下:

  

  实现代码:

  

代码
1 SalesmanA:
2  public class SalesmanA
3 {
4 public string Name
5 {
6 get
7 {
8 return "张三";
9 }
10 }
11 }
12 SalesmanB:
13  public class SalesmanB
14 {
15 public string Name
16 {
17 get
18 {
19 return "李四";
20 }
21 }
22 }
23 CorporationA:
24  public class CorporationA
25 {
26 private string BusinessA(string pSalesmanName)
27 {
28 return "CorporationA 与SalesmanA " + pSalesmanName + "商讨业务!";
29 }
30
31 private string BusinessB(string pSalesmanName)
32 {
33 return "CorporationA 与SalesmanB " + pSalesmanName + "商讨业务!";
34 }
35
36 /// <summary>
37 /// 根据传入的参数做类型判断,然后选择相应的业务
38 /// </summary>
39 /// <param name="pSalesman"></param>
40 /// <returns></returns>
41   public string TalkBusiness(object pSalesman)
42 {
43 if (pSalesman is SalesmanA)
44 {
45 return BusinessA(((SalesmanA)pSalesman).Name);
46 }
47 else if (pSalesman is SalesmanB)
48 {
49 return BusinessB(((SalesmanB)pSalesman).Name);
50 }
51 else
52 {
53 return "没有业务往来!";
54 }
55 }
56 }
57 CorporationB:
58  public class CorporationB
59 {
60 private string BusinessA(string pSalesmanName)
61 {
62 return "CorporationB 与SalesmanA " + pSalesmanName + "商讨业务!";
63 }
64
65 private string BusinessB(string pSalesmanName)
66 {
67 return "CorporationB 与SalesmanB " + pSalesmanName + "商讨业务!";
68 }
69
70 /// <summary>
71 /// 根据传入的参数做类型判断,然后选择相应的业务
72 /// </summary>
73 /// <param name="pSalesman"></param>
74 /// <returns></returns>
75 public string TalkBusiness(object pSalesman)
76 {
77 if (pSalesman is SalesmanA)
78 {
79 return BusinessA(((SalesmanA)pSalesman).Name);
80 }
81 else if (pSalesman is SalesmanB)
82 {
83 return BusinessB(((SalesmanB)pSalesman).Name);
84 }
85 else
86 {
87 return "没有业务往来!";
88 }
89 }
90 }
91

    现在就让我们开探讨下这种方案中的缺陷。

         缺陷1:当增加一名别的公司的业务员时,看看我们要增加的代码量。我们要增加一个业务员类SalesmanC, 增加公司CorporationA中的一个if else 分支,并增加新的BusinessC(string pSalesmanName)处理过程。就是将访问行为分散到了各个节点中,为增加新的访问者增加了难度。  

         缺陷 2:还是老问题,代码中存在多重条件转移语句(if else),这增加了代码维护的难度。

        

 

         下面是我用访问者模式来解决不假思索带来的缺陷。

在这展示访问者模式之前,我们先了解访问者模式的目的或者是其所针对的问题。

首先。访问者模式的封装一些施加于某种数据结构元素之上的操作。力求保证一旦这些操作被修改时,保持这些操作的数据结构不变。适用于数据结构相对稳定的系统,它把数据结构和作用于该结构之上的一系列操作解耦开,让这些操作可以自由地演化,而数据结构不变。

 

  访问者模式的结构图:

  

  下面,我让我们来试试访问者模式。

 

  运用访问者模式后的类图:

  注:其中的Client类在一个窗体的按钮事件中实现其逻辑。

  好的,现在让我们来看看访问者模式是否解决了之前不假思索的缺陷。

  1. 先来看看增加一个访问者的情况。当增加一个,访问者时,只需增加一个继承接口Salesman 的ConcreteSalesmanC,该类的结构与ConcreteSalesmanA一样,只是其中的两个作用于数据结构Corporation的操作的具体逻辑不同。

  2. 在来看看多重条件转移语句是否消除,我们可以看到

if (pSalesman is SalesmanA)

 {

                return BusinessA(((SalesmanA)pSalesman).Name);

 }

 else if (pSalesman is SalesmanB)

 {

                return BusinessB(((SalesmanB)pSalesman).Name);

 }

的条件转移语句没有了,而是用Accept() 来自动选择。

  

相关代码:

 

代码
1 接口Salesman:
2 public interface Salesman
3 {
4 string WorkInfo
5 {
6 get;
7 }
8
9 /// <summary>
10 /// 与公司A的业务
11 /// </summary>
12 /// <param name="pCorporationA"></param>
13 void CorporationA(Corporation pCorporationA);
14
15 /// <summary>
16 /// 与公司B的业务
17 /// </summary>
18 /// <param name="pCorporationB"></param>
19 void CorporationB(Corporation pCorporationB);
20 }
21 ConcreteSalesmanA:
22 public class ConcreteSalesmanA : Salesman
23 {
24 private string m_WorkInfo = "";
25
26 public string WorkInfo
27 {
28 get
29 {
30 return m_WorkInfo;
31 }
32 }
33 #region Salesman 成员
34
35 public void CorporationA(Corporation pCorporationA)
36 {
37 m_WorkInfo += "\n业务员A" + pCorporationA.Info;
38 }
39
40 public void CorporationB(Corporation pCorporationB)
41 {
42 m_WorkInfo += "\n业务员A" + pCorporationB.Info;
43 }
44
45 #endregion
46 }
47 ConcreteSalesmanB:
48 public class ConcreteSalesmanB : Salesman
49 {
50 private string m_WorkInfo;
51
52 public string WorkInfo
53 {
54 get
55 {
56 return m_WorkInfo;
57 }
58 }
59
60 #region Salesman 成员
61
62 public void CorporationA(Corporation pCorporationA)
63 {
64 m_WorkInfo += "\n业务员B" + pCorporationA.Info;
65 }
66
67 public void CorporationB(Corporation pCorporationB)
68 {
69 m_WorkInfo += "\n业务员B" + pCorporationB.Info;
70 }
71
72 #endregion
73 }
74
75
76 接口Corporation:
77 public interface Corporation
78 {
79 string Info
80 {
81 get;
82 }
83
84 /// <summary>
85 /// 接受业务员来商讨业务
86 /// </summary>
87 /// <param name="pSalesman"></param>
88 void Accept(Salesman pSalesman);
89 }
90
91 ConcreteCorporationA:
92 public class ConcreteCorporationA : Corporation
93 {
94 public string Info
95 {
96 get
97 {
98 return "与公司A进行商讨!";
99 }
100 }
101 #region Corporation 成员
102
103 public void Accept(Salesman pSalesman)
104 {
105 pSalesman.CorporationA(this);
106 }
107
108 #endregion
109
110 }
111 ConcreteCorporationB:
112 public class ConcreteCorporationB : Corporation
113 {
114 public string Info
115 {
116 get
117 {
118 return "与公司B进行商讨!";
119 }
120 }
121 #region Corporation 成员
122
123 public void Accept(Salesman pSalesman)
124 {
125 pSalesman.CorporationB(this);
126 }
127
128 #endregion
129 }
130 ObjectStructureCorporation:
131 public class ObjectStructureCorporation
132 {
133 private List<Corporation> m_CorpList = new List<Corporation>();
134
135 public void AddCorporation(Corporation pCorporation)
136 {
137 m_CorpList.Add(pCorporation);
138 }
139
140 public void RemoveCorporation(Corporation pCorporation)
141 {
142 m_CorpList.Remove(pCorporation);
143 }
144
145 public void Visit(Salesman pSalesman)
146 {
147 int corpCount = m_CorpList.Count;
148 for (int i = 0; i < corpCount; i++)
149 {
150 m_CorpList[i].Accept(pSalesman);
151 }
152 }
153 }

 

 

  

访问者模式的缺点:

1.    增加一个新的数据结构比较困难。就上面的例子来说,增加一个新的公司(既增加一个新的数据结构),就要修改所有的业务者,向所有的业务者添加对该公司的业务。要是其中一个访问者没有被修改,那么系统在运行时就会出现异常。 还有对系统的大规模修改会违背“开-闭”原则(对扩展开放,对修改关闭)。

2.     访问者模式必须暴露数据结构节点的一些内部细节,这破坏了类的封装性。

 

 

参考资料:C#设计模式。

posted @ 2010-11-01 12:28  天津城建学院软件工程  阅读(598)  评论(0编辑  收藏  举报