设计模式之禅之设计模式-访问者模式

一:访问者模式定义
        --->封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

二:访问者模式角色

● Visitor——抽象访问者
        抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。
● ConcreteVisitor——具体访问者
        它影响访问者访问到一个类后该怎么干,要做什么事情。
● Element——抽象元素
        接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。
● ConcreteElement——具体元素
        实现accept方法,通常是visitor.visit(this),基本上都形成了一种模式了。
● ObjectStruture——结构对象
        元素产生者,一般容纳在多个不同类、不同接口的容器,如List、Set、Map等,在项目中,一般很少抽象出这个角色。

三:访问者模式的应用
【1】访问者模式的优点

● 符合单一职责原则
        具体元素角色也就是Employee抽象类的两个子类负责数据的加载,而Visitor类则负责报表的展现,两个不同的职责非常明确地分离开来,各自演绎变化。

● 优秀的扩展性
        由于职责分开,继续增加对数据的操作是非常快捷的,例如,现在要增加一份给大老板的报表,这份报表格式又有所不同,直接在Visitor中增加一个方法,传递数据后进行整理打印。

● 灵活性非常高
        不同的访问者不同的操作

【2】访问者模式的缺点
● 具体元素对访问者公布细节
         访问者要访问一个类就必然要求这个类公布一些方法和数据,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的。

● 具体元素变更比较困难
        具体元素角色的增加、删除、修改都是比较困难的,就上面那个例子,你想想,你要是想增加一个成员变量,如年龄age,Visitor就需要修改,如果Visitor是一个还好办,多个呢?业务逻辑再复杂点呢?

● 违背了依赖倒置转原则
        访问者依赖的是具体元素,而不是抽象元素,这破坏了依赖倒置原则,特别是在面向对象的编程中,抛弃了对接口的依赖,而直接依赖实现类,扩展比较难。

【3】访问者模式的应用场景
● 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作,也就说是用迭代器模式已经不能胜任的情景。
● 需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。
● 总结一下,在这种地方你一定要考虑使用访问者模式:业务规则要求遍历多个不同的对象。这本身也是访问者模式出发点,迭代器模式只能访问同类或同接口的数据(当然了,如果你使用instanceof,那么能访问所有的数据,这没有争论),而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,然后执行不同的操作,也就是针对访问的对象不同,执行不同的操作。访问者模式还有一个用途,就是充当拦截器(Interceptor)角色,这个我们将在混编模式中讲解。

四:访问者模式中涉及的概念:双分派

---->说到访问者模式就不得不提一下双分派(double dispatch)问题,什么是双分派呢?我们先来解释一下什么是单分派(single dispatch)和多分派(multiple dispatch),
---->单分派:处理一个操作是根据请求者的名称和接收到的参数决定的,在Java中有静态绑定和动态绑定之说,它的实现是依据重载(overload)和覆写(override)实现的
---->重载在编译器期就决定了要调用哪个方法,它是根据role的表面类型而决定调用act(Role role)方法,这是静态绑定;而Actor的执行方法act则是由其实际类型决定的,这是动态绑定。
---->看到没?不管演员类和角色类怎么变化,我们都能够找到期望的方法运行,这就是双反派。双分派意味着得到执行的操作决定于请求的种类和两个接收者的类型,它是多分派的一个特例。从这里也可以看到Java是一个支持双分派的单分派语言。

五:访问者模式案例

【1】数据公共部分

 1 package com.yeepay.sxf.template20;
 2 /**
 3  * 数据公共部分
 4  * @author sxf
 5  *
 6  */
 7 public abstract class Employee {
 8     public final static int MALE=0;//0代表是男性
 9     public final static int FEMALE=1;//1代表是女性
10     //甭管是谁,都有工资
11     private String name;
12     //薪水
13     private int salary;
14     //性别很重要
15     private int sex;
16     //构造函数
17     public Employee(String name,int salary,int sex){
18         this.name=name;
19         this.salary=salary;
20         this.sex=sex;
21     }
22     /**
23      * 访问者访问内容
24      * @param iVisitor
25      */
26     public abstract void accept(IVisitor iVisitor);
27
28     //set get 方法
29     public String getName() {
30         return name;
31     }
32     public void setName(String name) {
33         this.name = name;
34     }
35     public int getSalary() {
36         return salary;
37     }
38     public void setSalary(int salary) {
39         this.salary = salary;
40     }
41     public int getSex() {
42         return sex;
43     }
44     public void setSex(int sex) {
45         this.sex = sex;
46     }
47
48 }

View Code

【2】员工数据

 1 package com.yeepay.sxf.template20;
 2 /**
 3  * 小兵(员工数据)
 4  * @author sxf
 5  *
 6  */
 7 public class CommonEmployee extends Employee {
 8     //工作内容
 9     private String job;
10     //构造函数
11     public CommonEmployee(String name, int salary, int sex, String job) {
12         super(name, salary, sex);
13         this.job = job;
14     }
15
16     /**
17      * 访问者实现
18      */
19     @Override
20     public void accept(IVisitor iVisitor) {
21         iVisitor.visit(this);
22     }
23
24
25     public String getJob() {
26         return job;
27     }
28     public void setJob(String job) {
29         this.job = job;
30     }
31
32
33
34 }

View Code

【3】管理层数据

 1 package com.yeepay.sxf.template20;
 2 /**
 3  * 管理者
 4  * @author sxf
 5  *
 6  */
 7 public class Manager extends Employee {
 8     //业绩
 9     private String refcestr;
10
11     //构造函数
12     public Manager(String name, int salary, int sex, String refcestr) {
13         super(name, salary, sex);
14         this.refcestr = refcestr;
15     }
16
17     /**
18      * 访问者访问
19      */
20     @Override
21     public void accept(IVisitor iVisitor) {
22         iVisitor.visit(this);
23     }
24
25
26     public String getRefcestr() {
27         return refcestr;
28     }
29
30     public void setRefcestr(String refcestr) {
31         this.refcestr = refcestr;
32     }
33
34
35 }

View Code

【4】访问者接口

 1 package com.yeepay.sxf.template20;
 2 /**
 3  * 访问者接口
 4  * @author sxf
 5  *
 6  */
 7 public interface IVisitor {
 8     //首先,定义我可以访问普通员工
 9     public void visit(CommonEmployee commonEmployee);
10     //其次,定义我还可以访问部门经理
11     public void visit(Manager manager);
12 }

View Code

【5】普通展示访问者实现

 1 package com.yeepay.sxf.template20;
 2 /**
 3  * 具体的访问者
 4  * @author sxf
 5  *
 6  */
 7 public class Visitor implements IVisitor {
 8
 9     //访问普通员工,打印出报表
10     @Override
11     public void visit(CommonEmployee commonEmployee) {
12         System.out.println(this.getCommonEmployee(commonEmployee));
13     }
14
15     @Override
16     public void visit(Manager manager) {
17         System.out.println(this.getManagerInfo(manager));
18     }
19     //组装出基本信息
20     private String getBasicInfo(Employee employee){
21     String info = "姓名:" + employee.getName() + "\t";
22     info = info + "性别:" +(employee.getSex() == Employee.FEMALE?"女":"男");
23     info = info + "薪水:" + employee.getSalary() + "\t";
24     return info;
25     }
26     //组装出部门经理的信息
27     private String getManagerInfo(Manager manager){
28     String basicInfo = this.getBasicInfo(manager);
29     String otherInfo = "业绩:"+manager.getRefcestr() + "\t";
30     return basicInfo + otherInfo;
31     }
32     //组装出普通员工信息
33     private String getCommonEmployee(CommonEmployee commonEmployee){
34     String basicInfo = this.getBasicInfo(commonEmployee);
35     String otherInfo = "工作:"+commonEmployee.getJob()+"\t";
36     return basicInfo + otherInfo;
37     }
38
39 }

View Code

【6】报表展示访问者的扩展接口

 1 package com.yeepay.sxf.template20;
 2 /**
 3  * 对访问者接口的扩展
 4  * 展示访问者的报表
 5  * @author sxf
 6  *
 7  */
 8 public interface IShowVisitor extends IVisitor{
 9     //展示报表
10     public void report();
11 }

View Code

【7】统计访问者的扩展接口

 1 package com.yeepay.sxf.template20;
 2 /**
 3  * 统计访问
 4  * @author sxf
 5  *
 6  */
 7 public interface ITotalVisitor extends IVisitor {
 8
 9     //统计所有员工的工资总和
10     public void totalSalary();
11 }

View Code

【8】报表展示访问者的扩展接口实现类

 1 package com.yeepay.sxf.template20;
 2 /**
 3  * 展示的访问者
 4  * @author sxf
 5  *
 6  */
 7 public class ShowVisitor implements IShowVisitor {
 8
 9     private String info="";
10
11     //组装出基本信息
12     private String getBasicInfo(Employee employee){
13     String info = "姓名:" + employee.getName() + "\t";
14     info = info + "性别:" +(employee.getSex() == Employee.FEMALE?"女":"男");
15     info = info + "薪水:" + employee.getSalary() + "\t";
16     return info;
17     }
18     //组装出部门经理的信息
19     private String getManagerInfo(Manager manager){
20     String basicInfo = this.getBasicInfo(manager);
21     String otherInfo = "业绩:"+manager.getRefcestr() + "\t";
22     return basicInfo + otherInfo;
23     }
24     //组装出普通员工信息
25     private String getCommonEmployee(CommonEmployee commonEmployee){
26     String basicInfo = this.getBasicInfo(commonEmployee);
27     String otherInfo = "工作:"+commonEmployee.getJob()+"\t";
28     return basicInfo + otherInfo;
29     }
30
31
32     @Override
33     public void visit(CommonEmployee commonEmployee) {
34         this.info=this.info+this.getBasicInfo(commonEmployee);
35     }
36
37     @Override
38     public void visit(Manager manager) {
39         this.info=this.info+this.getManagerInfo(manager);
40     }
41
42     /**
43      * 展示
44      */
45     @Override
46     public void report() {
47         System.out.println("ShowVisitor.report()"+this.info);
48     }
49
50
51 }

View Code

【9】统计访问者的扩展接口实现

 1 package com.yeepay.sxf.template20;
 2 /**
 3  * 统计访问者接口实现
 4  * @author sxf
 5  *
 6  */
 7 public class TotalVisitor implements ITotalVisitor{
 8     //管理层的工资系数
 9     private final static int MANAGER_COEFFICIENT=5;
10     //员工的工资系数
11     private final static int COMMONEMPLOYEE_COEFFICIENT=2;
12
13     //普通员工工资总和
14     private int commonTotalSalary=0;
15
16     //部门经历的工资总和
17     private int managerTotalSalary=0;
18
19     @Override
20     public void visit(CommonEmployee commonEmployee) {
21         this.commonTotalSalary=this.commonTotalSalary+commonEmployee.getSalary()*COMMONEMPLOYEE_COEFFICIENT;
22     }
23
24     @Override
25     public void visit(Manager manager) {
26         this.commonTotalSalary=this.commonTotalSalary+manager.getSalary()*MANAGER_COEFFICIENT;
27     }
28
29     @Override
30     public void totalSalary() {
31         System.out.println("TotalVisitor.totalSalary(工资总和为===>:)"+(commonTotalSalary+managerTotalSalary));
32     }
33
34
35 }

View Code

【10】测试类

 1 package com.yeepay.sxf.template20;
 2
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 /**
 6  * 访问者测试
 7  * 双分派测试
 8  * @author sxf
 9  *
10  */
11 public class ClientTest {
12
13     public static void main(String[] args) {
14         //test01();
15     //    test02();
16         test03();
17     }
18
19
20     //一个访问者测试
21     public static void test01(){
22         //数据
23         List<Employee> list=mockEmployee();
24         for(Employee mo:list){
25             mo.accept(new Visitor());
26         }
27     }
28
29
30
31
32     //多个访问者测试
33     public static void test02(){
34         //数据
35         List<Employee> list=mockEmployee();
36
37         //数据展示访问者
38         IShowVisitor showIVisitor=new ShowVisitor();
39         //工资计算访问者
40         ITotalVisitor totalVisitor=new TotalVisitor();
41
42         for(Employee employee:list){
43             employee.accept(showIVisitor);
44             employee.accept(totalVisitor);
45         }
46
47         //数据展示
48         showIVisitor.report();
49         //工资计算
50         totalVisitor.totalSalary();
51     }
52
53
54     //制造员工集合
55     public static List<Employee> mockEmployee(){
56         List<Employee> list=new ArrayList<Employee>();
57         CommonEmployee bin1=new CommonEmployee("sxf", 15000, 1, "java开发");
58         CommonEmployee bin2=new CommonEmployee("sxs", 15000, 1, ".net开发");
59         CommonEmployee bin3=new CommonEmployee("sxy", 15000, 0, "前端开发");
60         Manager manager1=new Manager("aaaaaaa", 100000, 0, "怕啊啊");
61         Manager manager2=new Manager("bbbbbb", 100000, 0, "公官");
62         list.add(bin1);
63         list.add(bin2);
64         list.add(bin3);
65         list.add(manager1);
66         list.add(manager2);
67         return list;
68     }
69
70
71     //测试双分派
72     public static void test03(){
73         //重载,看参数类型
74         //重写,看调用者类型
75         AbsActor absActor=new OldActor();
76         Role role=new KongFuRole();
77         KongFuRole role2=new KongFuRole();
78         absActor.act(role);
79         absActor.act(role2);
80
81         AbsActor absActor2=new YongActor();
82         absActor2.act(role);
83         absActor2.act(role2);
84     }
85 }

View Code

【11】双分派
 [1]角色

 1 package com.yeepay.sxf.template20;
 2 /**
 3  * 角色
 4  * @author sxf
 5  *
 6  */
 7 public interface Role {
 8     //演员要扮演的角色
 9 }
10
11
12 package com.yeepay.sxf.template20;
13 /**
14  * 功夫角色
15  * @author sxf
16  *
17  */
18 public class KongFuRole implements Role{
19
20 }
21
22 package com.yeepay.sxf.template20;
23 /**
24  * 白痴角色
25  * @author sxf
26  *
27  */
28 public class IdiotRole implements Role{
29
30 }

View Code

 [2]演员

 1 package com.yeepay.sxf.template20;
 2 /**
 3  * 抽象演员
 4  * @author sxf
 5  *
 6  */
 7 public abstract class AbsActor {
 8
 9     public void act(Role role){
10         System.out.println("AbsActor.act(演员可以演所有角色)");
11     }
12
13     public void act(KongFuRole kongFuRole){
14         System.out.println("AbsActor.act(演员可以演功夫角色)");
15     }
16 }
17
18
19 package com.yeepay.sxf.template20;
20 /**
21  * 年龄大的演员
22  * @author sxf
23  *
24  */
25 public class OldActor extends AbsActor{
26     //不演功夫角色
27     @Override
28     public void act(KongFuRole kongFuRole){
29         System.out.println("OldActor.act(年龄老的演员不能演功夫角色)");
30     }
31 }
32
33
34 package com.yeepay.sxf.template20;
35 /**
36  * 年轻演员
37  * @author sxf
38  *
39  */
40 public class YongActor extends AbsActor{
41
42     //年轻演员最喜欢演功夫戏
43     public void act(KongFuRole kongFuRole){
44         System.out.println("YongActor.act(年轻演员最喜欢演功夫角色)");
45     }
46 }

View Code

 

时间: 2024-08-01 14:36:00

设计模式之禅之设计模式-访问者模式的相关文章

Java设计模式(二十一)----访问者模式

访问者模式 一.概述 1.定义 2.分派的概念 3.分派的类型 4.双重分派 二.结构 三.具体案例 一.概述 1.定义 访问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变. 2.分派的概念 变量被声明时的类型叫做变量的静态类型(Static Type),有些人又把静态类型叫做明显类型(Apparent Type):而变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type).比如: List list = n

实例讲解iOS应用的设计模式开发中的Visitor访问者模式_IOS

为了方便向大家展示,先给出简短的定义: 访问者模式(Visitor),表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 紧接着,给出其类结构图. 访问者模式适用于数据结构相对稳定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作结合可以相对自由地演化. 访问者模式的目的是要把处理从数据结构分离出来.很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,

设计模式之禅之设计模式-桥梁模式

一:桥梁模式定义        --->桥梁模式(Bridge Pattern)也叫做桥接模式,是一个比较简单的模式        --->将抽象和实现解耦,使得两者可以独立地变化. 二:桥梁模式角色 ● Abstraction--抽象化角色        它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类.● Implementor--实现化角色        它是接口或者抽象类,定义角色必需的行为和属性.● RefinedAbstraction--修正抽象

设计模式(三)之访问者模式

被访问者接口: public interface ITarget { public void doSomeThing(); public void accept(IVisitor visitor); } 访问者接口: public interface IVisitor { public void visit(ITarget target); } 被访问者: public class TargetOne implements ITarget { @Override public void doSo

设计模式之禅之设计模式-装饰者模式

一:装饰模式的定义        --->动态地给一个对象添加一些额外的职责.就增加功能来说,装饰模式相比生成子类更为灵活.        --->如果大家还记得代理模式,那么很容易看懂这个类图,装饰类的作用也就是一个特殊的代理类.        --->在装饰模式中,必然有一个最基本.最核心.最原始的接口或抽象类充当Component抽象构件 二:装饰模式的角色        ● Component抽象构件                Component是一个接口或者是抽象类,就是定

设计模式之禅之设计模式-策略模式

一:策略模式的定义        --->是一种比较简单的模式,也叫做政策模式        --->定义一组算法,将每个算法都封装起来,并且使它们之间可以互换 二:策略模式的三个角色 ● Context封装角色        --->它也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略.算法的直接访问,封装可能存在的变化.● Strategy抽象策略角色        --->策略.算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性● ConcreteStr

设计模式之禅之设计模式-迭代器模式

一:迭代器模式的定义        --->迭代器模式(Iterator Pattern)目前已经是一个没落的模式,基本上没人会单独写一个迭代器,除非是产品性质的开发        --->它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节.        --->迭代器是为容器服务的,那什么是容器呢? 能容纳对象的所有类型都可以称之为容器,例如Collection集合类型.Set类型等,迭代器模式就是为解决遍历这些容器中的元素而诞生的        --->迭

设计模式之禅之设计模式-模板方法模式

一:模板方法模式的定义        --->定义一个操作中的算法的框架,而将一些步骤延迟到子类中.使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.        --->模板方法模式确实非常简单,仅仅使用了Java的继承机制,但它是一个应用非常广泛的模式.其中,AbstractClass叫做抽象模板,它的方法分为两类:        ● 基本方法                基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法被调用.        ● 模板方法   

设计模式之禅之设计模式-建造者模式

一:建造者模式的定义        --->建造者模式(Builder Pattern)也叫做生成器模式,其定义如下:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示        ● Product产品类                通常是实现了模板方法模式,也就是有模板方法和基本方法,这个参考模板方法模式.例子中的BenzModel和BMWModel就属于产品类.        ● Builder抽象建造者                规范产品的组建,一般是由子类