设计模式01:一篇文章精通访问模式

访问者模式是什么

Vistor Pattern-----访问者模式是一种将数据结构与数据操作分离的设计模式。是指封装一些作用于某种数据结构中的各种元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新操作,访问者模式属于行为型模式。

访问者模式的基本思想是针对系统中拥有的某些固定类型的对象结构(元素),在其内提供一个 accept 方法用来接受访问者对象的访问。不同的访问者对同一元素的访问内容不同,使得相同的元素集合可以产生不同的元素结果。accept 方法可以接受不同的访问者对象,然后在内部将自己转发到访问者对象 visit 方法内。

访问者模式的核心思想是解耦数据结构与数据操作,使得对元素的操作具备优秀的扩展性,我们可以通过扩展不同的数据操作类型(访问者)实现对相同元素的不同操作。
(名词解释百度百科,博客园借鉴 )

栗子

我们以点菜时、顾客需要通过菜单查看点菜为例。在这个例子里面顾客就是访问者,而食谱就是访问者需要访问的信息。(这里我们需要新建一个 visitor 目录,相关类创建在 visitor 目录下)。

首先建立一个食谱接口 IRecipe.java,接口中只定义一个接受访问者来访问的方法。

public interface IRecipe {
void accept(ICustomer customer);//食谱接受访问者顾客的访问,参数就是顾客
}
新建一道工具菜红烧肉类 Meat.java,实现食谱接口 IRecipe。

public class Meat implements IRecipe {

@Override
public void accept(ICustomer customer) {
    customer.visit(this);
}

public String getPrice(){//获取价格
    return "889元/份";
}

}
接下来,我们还需要一个类 来管理历史信息。

public class Cabbage implements IRecipe {
@Override
public void accept(ICustomer customer) {
customer.visit(this);
}

public String getPrice(){//获取价格
    return "998元/份";
}

}
这时候我们需要建立一个抽象的访问者,即:顾客接口 ICustome。这个接口定义两个方法,注意,这个菜就相当于是访问者需要访问数据结构中的元素,而因为我们示例中只有两道菜,相当于食谱中只有两道菜,所以需要为每道菜都提供一个方法,也就是 2 个方法,总之访问者中的方法个数应该要和访问数据结构中的数据种类数相等。

public interface ICustomer {
void visit(Meat meat);//访问肉类菜

void visit(Cabbage cabbage);//访问蔬菜类菜

}
新建一个具体访问者类顾客 A CustomerA.java,需要实现抽象访问者类 ICustomer。
package visitor;

public class CustomerA implements ICustomer{
@Override
public void visit(Meat meat) {
System.out.println(“肉类:” + meat.getPrice());
}

@Override
public void visit(Cabbage cabbage) {
    System.out.println("时蔬:" + cabbage.getPrice());
}

}

现在顾客有了,菜也有了,那么现在就还缺少一个菜单,毕竟顾客是通过菜单去点菜的,所以菜单在这里就起到了一个中间的作用,而菜单所对应的角色在访问者模式中称之为结构对象(ObjectStruture)。

新建一个结构对象,即菜单类 。

public class RestaurantMenu {
private List recipeList = new ArrayList<>();//存储菜单中的菜,也就是数据结构中的元素

public RestaurantMenu(IRecipe recipe) {//初始化菜单,即初始化数据结构
    recipeList.add(recipe);
}

public void addRecipe(IRecipe recipe){//添加一道菜到菜单中,即添加一种数据类型到数据结构中
    recipeList.add(recipe);
}

public void display(ICustomer customer){//展示所有菜单
    for (IRecipe recipe : recipeList){
        recipe.accept(customer);
    }
}

}
最后新建一个测试类 进行测试。

public class TestVistor {
public static void main(String[] args) {
IRecipe recipe = new Meat();//创建一道肉类菜
RestaurantMenu menu = new RestaurantMenu(recipe);//将肉类菜初始化到菜单中
menu.addRecipe(new Cabbage());//再添加一道蔬菜
menu.display(new CustomerA());//顾客A开始访问菜单中的元素(菜)
}
}

总结

好了 ,点菜成功,想吃什么都有了

上面我们看到,如果要扩展访问者,即顾客时非常简单,新建一个类就行了,但是一旦要新增一道菜,那么所有的访问者都必须要改源码了,因为每一道菜在访问者中都要对应一个方法,所以访问者模式的前提就是数据结构非常稳定。

访问者模式角色
从上面示例中,我们可以得出访问者模式主要有 5 个角色:

抽象访问者(Vistor):接口或者抽象类都可以(如示例中的 ICustomer)。这个角色主要是定义对具体元素的 visit 方法,参数就是具体元素,理论上来说方法数等于元素个数。所以如果元素不稳定经常变化的话,那么访问者是要一直修改的,并不适合使用访问者模式。
具体访问者(ConcreteVistor):实现对具体元素的访问(如示例中的 CustomerA)。
抽象元素(Element):接口或者抽象类。定义了一个接受访问者访问的方法 accept(如示例中的 IRecipe)。
具体元素(ConcreteElement):提供接受访问者访问的具体实现,通常都是采用 visitor.visit() 来实现(如示例中的 Cabbage 和 Meat)。
结构对象(ObjectStruture):用来维护元素,并提供一个方法来接受访问者访问所有的元素(如示例中的 RestaurantMenu)。
访问者模式适用场景
数据结构要稳定,但是作用于数据结构上的操作经常变化(如上面示例中菜的种类要稳定,但是顾客可以不断变化)。
需要对不同数据类型(元素)进行操作,而不使用分支判断具体类型的场景。
访问者模式优点
解耦了数据结构和数据操作,使得操作集合可以独立变化,非常容易扩展。
每种角色各司其职,符合单一职责原则。
访问者模式缺点
增加元素类型困难,一旦增加元素就必须要修改访问者源码,违反了开闭原则,也不利于维护。
违背了依赖倒置原则,比如我们示例中的访问者接口,定义的 visit 方法依赖的是具体元素而不是抽象元素。

Guess you like

Origin blog.csdn.net/weixin_45530192/article/details/117785997