经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
设计模式(23) 访问者模式
来源:cnblogs  作者:zhixin9001  时间:2020/11/9 16:08:29  对本文有异议

由于应用开发过程中先前完成的类型会因为需求变化(无论是业务功能,还是技术实现或是出于集成的需要)增加新的方法,如果直接在基类中增加新的方法,其派生类型可能需要相应进行比较繁琐的处理。而使用访问者模式可以做到在不改变既有类型层次的前提下,运行时动态为类型层次的每个类增加新的操作。

访问者模式

GOF对策略模式的描述为:
Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates...
— Design Patterns : Elements of Reusable Object-Oriented Software

UML类图

访问者模式包含五种角色:

  • IVistor(抽象访问者):为该对象结构中具体元素角色声明一个访问操作接口。
  • ConcreteVisitor(具体访问者):每个具体访问者都实现了IVistor中定义的操作。
  • IElement(抽象元素):定义了一个accept操作,以IVisitor作为参数。
  • ConcreteElement(具体元素):实现了IElement中的accept()方法,调用IVistor的访问方法以便完成对一个元素的操作。
  • ObjectStructure(对象结构):可以是组合模式,也可以是集合,能够枚举它包含的元素,并提供一个接口,允许IVistor访问它的元素。

代码示例

设想有这样一个HR系统,系统只能按照标准的工作时间、时薪计算薪金,在系统交付后发现需要提供加班计算功能,而且还需要安排休假、晋升等功能,考虑到类似的需求在将来还会出现,所以改造的时候考虑采用访问者模式。在HR系统的对象上增加了Accept某个IVisistor接口的能力,在添加新功能的时候可以实现IVisitor接口。

  1. public interface IEmployee
  2. {
  3. string Name { get; set; }
  4. double Income { get; set; }
  5. int VacationDays { get; set; }
  6. void Accept(IVisitor visitor);
  7. }
  8. public interface IVisitor
  9. {
  10. void VisitiEmployee(IEmployee employee);
  11. void VisitManager(Manager manager);
  12. }
  13. public class Employee : IEmployee
  14. {
  15. public string Name { get; set; }
  16. public double Income { get; set; }
  17. public int VacationDays { get; set; }
  18. public Employee(string name, double income, int vacationDays)
  19. {
  20. this.Name = name;
  21. this.Income = income;
  22. this.VacationDays = vacationDays;
  23. }
  24. public void Accept(IVisitor visitor)
  25. {
  26. visitor.VisitiEmployee(this);
  27. }
  28. }
  29. public class Manager : IEmployee
  30. {
  31. public string Department { get; set; }
  32. public string Name { get; set; }
  33. public double Income { get; set; }
  34. public int VacationDays { get; set; }
  35. public Manager(string name, double income, int vacationDays, string department)
  36. {
  37. this.Name = name;
  38. this.Income = income;
  39. this.VacationDays = vacationDays;
  40. this.Department = department;
  41. }
  42. public void Accept(IVisitor visitor)
  43. {
  44. visitor.VisitManager(this);
  45. }
  46. }
  47. public class EmployeeCollection : List<IEmployee>
  48. {
  49. public void Accept(IVisitor visitor)
  50. {
  51. foreach (IEmployee employee in this)
  52. {
  53. employee.Accept(visitor);
  54. }
  55. }
  56. }
  57. public class ExtraVacationVisitor : IVisitor
  58. {
  59. public void VisitiEmployee(IEmployee employee)
  60. {
  61. employee.VacationDays += 1;
  62. }
  63. public void VisitManager(Manager manager)
  64. {
  65. manager.VacationDays += 2;
  66. }
  67. }
  68. public class RaiseSalaryVisitor : IVisitor
  69. {
  70. public void VisitiEmployee(IEmployee employee)
  71. {
  72. employee.Income *= 1.1;
  73. }
  74. public void VisitManager(Manager manager)
  75. {
  76. manager.Income *= 1.2;
  77. }
  78. }

调用端代码

  1. public class Test
  2. {
  3. public static void Entry()
  4. {
  5. EmployeeCollection employees = new EmployeeCollection();
  6. employees.Add(new Employee("joe", 25000, 14));
  7. employees.Add(new Manager("alice", 22000, 14, "sales"));
  8. employees.Add(new Employee("peter", 15000, 7));
  9. employees.Accept(new ExtraVacationVisitor());
  10. employees.Accept(new RaiseSalaryVisitor());
  11. }
  12. }

Employee类型并没有加薪和修改休假天数的方法,但借助访问者模式,时期具有了对应的功能。访问者模式的关键代码是在数据基础类里面有一个方法接受访问者,将自身引用传入访问者,这样访问者就可以操作数据类了。

访问者模式的适用场景

  • 一个类型需要依赖于很多不同接口的类型,在结构尽量松散的前提下,希望可以用到这些类型不同接口方法。
  • 经常需要为一个结构相对固定的对象结构添加一些新的操作。
  • 需要用一个独立的类型来组织一批不相干的操作,使用它的类型可以根据应用需要进行定制。

访问者模式的特点

优点

  • 符合单一职责原则。
  • 优秀的扩展性。
  • 灵活性。
    缺点
  • 具体元素对访问者公布细节,违反了迪米特原则。
  • 具体元素变更比较困难。
  • 违反了依赖倒置原则,依赖了具体类,而不是依赖抽象。

参考书籍:
王翔著 《设计模式——基于C#的工程化实现及扩展》

原文链接:http://www.cnblogs.com/zhixin9001/p/13726848.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号