Java访问者设计模式详细讲解

 更新时间:2022年11月23日 16:58:28   作者:lpf_wei  
大多数情况下你不需要访问者模式,但当一旦需要访问者模式时,那就是真的需要它了,这是设计模式创始人的原话。可以看出应用场景比较少,但需要它的时候是不可或缺的,这篇文章就开始学习最后一个设计模式——访问者模式

编程是一门艺术,大批量的改动显然是非常丑陋的做法,用心的琢磨写的代码让它变的更美观。

在生活中,电影或电视剧中的人物角色,不同的观众对他们的评价也不同;还有顾客在商场购物时放在“购物车”中的商品,顾客主要关心所选商品的性价比,而收银员关心的是商品的价格和数量。

这些被处理的数据元素相对稳定而访问方式多种多样的数据结构,如果用“访问者模式”来处理比较方便。访问者模式能把处理方法从数据结构中分离出来,并可以根据需要增加新的处理方法,且不用修改原来的程序代码与数据结构,这提高了程序的扩展性和灵活性。

1.模式的定义

访问者(Visitor)模式:是将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。

访问者设计模式主要解决:java多态方法重载的静态化问题。

2.访问者设计模式的优点与不足

访问者(Visitor)模式是一种对象行为型模式,其主要优点:

  • 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
  • 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
  • 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
  • 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。

访问者(Visitor)模式的不足:

  • 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
  • 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
  • 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。

3.访问者设计模式的实现思路

访问者(Visitor)模式实现的关键是如何将作用于元素的操作分离出来封装成独立的类

访问者模式包含以下主要角色。

  • 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
  • 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
  • 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
  • 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
  • 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

4.访问者设计模式实例

场景介绍:统计不同水果的价格,不同的水果放置到不同的集合,但是由于java多态中方法重载是静态化的不足,导致统计不出来价格,就可以使用访问者设计模式来处理。

public interface Fruit {
    int price();
    void draw();
    int accept(Visit visit);
}
public class Apple implements Fruit {
    private int price = 100;
    public Apple(){
    }
    public Apple(int price){
        this.price = price;
    }
    public void pack(AppleBag bag){
        bag.pack();
    }
    @Override
    public int price() {
        return price;
    }
    @Override
    public void draw() {
        System.out.print("苹果红富士");
    }
    public void setPrice(int price) {
        this.price = price;
    }
    public int accept(Visit visit){
        /*指针可以传递真实类型*/
        return visit.sell(this);
    }
}
public class Banana implements Fruit {
    private int price = 60;
    @Override
    public int price() {
        return price;
    }
    public void pack(BananaBag bag){
        bag.pack();
    }
    @Override
    public void draw() {
        System.out.print("仙人蕉");
    }
    public int accept(Visit visit){
        return visit.sell(this);
    }
    public void setPrice(int price) {
        this.price = price;
    }
}
public class Orange implements Fruit {
    private String name = "";
    private int price = 70;
    public Orange(String name,int price){
        this.price = price;
        this.name = name;
    }
    public void pack(OrangeBag bag){
        bag.pack();
    }
    @Override
    public int price() {
        return price;
    }
    @Override
    public void draw() {
        System.out.print("砂糖桔");
    }
    public int accept(Visit visit){
        return visit.sell(this);
    }
    public void setPrice(int price) {
        this.price = price;
    }
}
public class Visit {
	/*苹果计价*/
	public int sell(Apple apple){
		System.out.println("apple's price: ¥50");
		return 50;
	}
	/*桔子计价*/
	public int sell(Orange orange){
		System.out.println("orange's price: ¥20");
		return 20;
	}
	/*香蕉计价*/
	public int sell(Banana banana){
		System.out.println("banana's price: ¥30");
		return 30;
	}
	//其它水果计价
	public int sell(Fruit fruit){
		System.out.println("other price: ¥10");
		return 10;
	}
}
public class VisitClient {
    private static Visit visit = new Visit();
    /*库存*/
    private static List<Fruit> list = new ArrayList<>();
    static {
        list.add(StaticFactory.getFruitApple());
        list.add(StaticFactory.getFruitOrange());
        list.add(StaticFactory.getFruitBanana());
        list.add(StaticFactory.getFruitApple());
        list.add(StaticFactory.getFruitOrange());
    }
    private static int price() {
        int total = 0;
        for (Fruit fruit : list) {
            total += fruit.accept(visit);
        }
        System.out.println("总价值:" + total);
        return total;
    }
      public static void main(String[] args) {
        price();
    }

访问者设计模式的关键是:不能直接使用visit对象直接调用sell方法将水果对象传递进去,因为直接传递进去由于方法重载的静态化问题,不会执行相应的方法,需要是使用fruit的accept方法才行。

5.访问者设计模式的使用场景

通常在以下情况可以考虑使用访问者(Visitor)模式。

  • 对象结构相对稳定,但其操作算法经常变化的程序。
  • 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
  • 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。

到此这篇关于Java访问者设计模式详细讲解的文章就介绍到这了,更多相关Java访问者模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java延迟队列原理与用法实例详解

    Java延迟队列原理与用法实例详解

    这篇文章主要介绍了Java延迟队列原理与用法,结合实例形式详细分析了延迟队列的概念、原理、功能及具体使用方法,需要的朋友可以参考下
    2018-09-09
  • SpringBoot微信消息接口配置详解

    SpringBoot微信消息接口配置详解

    这篇文章主要介绍了SpringBoot 微信消息接口配置详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-06-06
  • java实现微信H5支付方法详解

    java实现微信H5支付方法详解

    本篇文章主要介绍了java实现微信H5支付方法详解,非常具有实用价值,需要的朋友可以参考下
    2017-04-04
  • 详解Spring Security如何在权限中使用通配符

    详解Spring Security如何在权限中使用通配符

    小伙伴们知道,在Shiro中,默认是支持权限通配符的。现在给用户授权的时候,可以一个权限一个权限的配置,也可以直接用通配符。本文将介绍Spring Security如何在权限中使用通配符,需要的可以参考一下
    2022-06-06
  • Spring Boot 结合 aop 实现读写分离

    Spring Boot 结合 aop 实现读写分离

    这篇文章主要介绍了Spring Boot 结合 aop 实现读写分离的示例,帮助大家更好的理解和使用Spring Boot框架,感兴趣的朋友可以了解下
    2020-11-11
  • SpringBoot集成screw实现数据库文档生成的代码示例

    SpringBoot集成screw实现数据库文档生成的代码示例

    数据库设计文档是项目技术文档的重要组成部分,Screw 是一款开源的数据库文档生成工具,它支持多种数据库类型,并能生成丰富格式的文档,本文将通过一个实际的例子,展示如何使用 Spring Boot 集成 Screw 生成数据库设计文档
    2024-07-07
  • SpringBoot使用@Value实现给静态变量注入值

    SpringBoot使用@Value实现给静态变量注入值

    这篇文章主要介绍了SpringBoot使用@Value实现给静态变量注入值的方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • IDEA+Maven创建Spring项目的实现步骤

    IDEA+Maven创建Spring项目的实现步骤

    这篇文章主要介绍了IDEA+Maven创建Spring项目的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • Java多线程和并发基础面试题(问答形式)

    Java多线程和并发基础面试题(问答形式)

    多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • JavaSE文件操作工具类FileUtil详解

    JavaSE文件操作工具类FileUtil详解

    这篇文章主要为大家详细介绍了JavaSE系列之文件操作工具类FileUtil,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08

最新评论