设计模式系列篇(十八)——访问者模式

What

访问者模式(Visitor Pattern),允许一个或者多个操作应用到一组对象上,解耦操作和对象本身。我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

Why

  1. 符合单一职责原则。 访问者模式将操作和对象解耦,每个类的职责非常单一。
  2. 优秀的扩展性。 如果想增加操作类型,无须对已有的稳定的对象类进行更改,具有很好的扩展性。
  3. 灵活性。优秀扩展性的必然结果,使得增加或删除操作类型都很方便。

When

  1. 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
  2. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。

How

访问者模式的实现是比较难理解的,但访问者的实现大家不必掌握,只需要见到能认出是访问者模式就好了。
下面,我们以实验室年底考核产生报表为例介绍访问者模式的实现。年底了,你的实验室迎来了一年一度的考核,实验室参与年终考核的有学硕和专硕,考核官包括导师和辅导员。对于导师来说,他关注的是学生的科研或者项目情况,而辅导员则更关注学生的课程成绩和社会实践。在这个例子中,学硕和专硕就是对象;而学生的科研、项目情况就是操作,而访问者的类型包括导师和辅导员。
UML图如下所示:


访问者模式

包含以下几部分:

  1. Visitor:接口或者抽象类,定义了对每个 Master子类 访问的行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式。
  2. ConcreteVisitor:具体的访问者,如本例中的MentorVisitor,它需要给出对每一个元素类访问时所产生的具体行为。
  3. Master:元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
  4. AcademicMaster、EngineerMaster:具体的元素类,它提供接受访问的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
    具体代码如下:
    首先是Master及其子类。
// 硕士抽象类,定义基本属性,并定义accept方法,参数为 Vistor 对象。
public abstract class Master {
    private String name;
    private Double gpa;
    private Double socialPractice;
    private Integer paperCount;
    private Integer projectCount;

    public Master(String name, Double gpa, Double socialPractice, Integer paperCount, Integer projectCount) {
        this.name = name;
        this.gpa = gpa;
        this.socialPractice = socialPractice;
        this.paperCount = paperCount;
        this.projectCount = projectCount;
    }

    // 省略getter方法
    public abstract void accept(Visitor visitor);
}

// 学硕类
public class AcademicMaster extends Master {
    public AcademicMaster(String name, Double gpa, Double socialPractice, Integer paperCount, Integer projectCount) {
        super(name, gpa, socialPractice, paperCount, projectCount);
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// 专硕类
public class EngineerMaster extends Master {
    public EngineerMaster(String name, Double gpa, Double socialPractice, Integer paperCount, Integer projectCount) {
        super(name, gpa, socialPractice, paperCount, projectCount);
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

接下来是Visitor接口及其实现类。

public interface Visitor {
    void visit(AcademicMaster academicMaster);

    void visit(EngineerMaster engineerMaster);
}

// 导师报表
public class MentorVisitor implements Visitor {
    @Override
    public void visit(AcademicMaster academicMaster) {
        System.out.println(String.format("name: %s, paper count: %d",
                academicMaster.getName(), academicMaster.getPaperCount()));
    }

    @Override
    public void visit(EngineerMaster engineerMaster) {
        System.out.println(String.format("name: %s, project count: %d",
                engineerMaster.getName(), engineerMaster.getProjectCount()));
    }
}

// 辅导员报表
public class CounselorVisitor implements Visitor {
    @Override
    public void visit(AcademicMaster academicMaster) {
        System.out.println(String.format("name: %s, gpa: %f",
                academicMaster.getName(), academicMaster.getGpa()));
    }

    @Override
    public void visit(EngineerMaster engineerMaster) {
        System.out.println(String.format("name: %s, social practice: %f",
                engineerMaster.getName(), engineerMaster.getSocialPractice()));
    }
}

通过上面两部分代码,大家可以看出,对Master类对象的操作都集中在Visitor实现中,这就实现了对象元素和操作解耦。如果增加访问者,如家长或者院领导都无须修改Master类,只需要增加相应的访问者实现就可以了。

最后,给个测试类。

public class TestMain {
    public static void main(String[] args) {
        Master academicMaster1 = new AcademicMaster("Jeremy", 3.7, 3.0, 1, 3);
        Master academicMaster2 = new AcademicMaster("Bob", 3.2, 2.0, 2, 1);
        Master engineerMaster1 = new EngineerMaster("Tom", 3.3, 4.0, 0, 1);
        Master engineerMaster2 = new EngineerMaster("Amy", 3.0, 3.0, 1, 2);
        List<Master> masters = new ArrayList<>();
        masters.add(academicMaster1);
        masters.add(academicMaster2);
        masters.add(engineerMaster1);
        masters.add(engineerMaster2);

        System.out.println("-----mentor's report-----");
        Visitor mentorVisitor = new MentorVisitor();
        for (Master master : masters) {
            master.accept(mentorVisitor);
        }

        System.out.println("-----counselor's report-----");
        Visitor counselorVisitor = new CounselorVisitor();
        for (Master master : masters) {
            master.accept(counselorVisitor);
        }
    }
}

输出如下:

-----mentor's report-----
name: Jeremy, paper count: 1
name: Bob, paper count: 2
name: Tom, project count: 1
name: Amy, project count: 2
-----counselor's report-----
name: Jeremy, gpa: 3.700000
name: Bob, gpa: 3.200000
name: Tom, social practice: 4.000000
name: Amy, social practice: 3.000000

搞定。

代码地址

i-learning

写在最后

如果你觉得我写的文章帮到了你,欢迎点赞、评论、分享、赞赏哦,你们的鼓励是我不断创作的动力~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339