商场里有许多的店铺,大致分为服装区、饮食区及休闲区,每天都会有络绎不绝的不同角色(打工人、学生、有钱人)的人来逛商场。商场想开发个系统用来模拟这些人的在这些店铺的行为。
public class SuperMarket {
public static void main(String[] args) {
List peopleRoles = new ArrayList<>();
statisticsPeople(peopleRoles);
Random random = new Random();
for (PeopleRole peopleRole : peopleRoles) {
peopleRole.showRole();
int nextInt = random.nextInt();
if (nextInt % 3 == 0) peopleRole.shopping(ShopPlace.food);
else if (nextInt % 2 == 0) peopleRole.shopping(ShopPlace.clothing);
else peopleRole.shopping(ShopPlace.relax);
System.out.println("........................");
}
}
static void statisticsPeople(List peopleRoles) {
Random random = new Random();
for (int i = 0; i < 10; i++) {
int nextInt = random.nextInt();
if (nextInt % 2 == 0) peopleRoles.add(PeopleRole.worker);
else if (nextInt % 3 == 0) peopleRoles.add(PeopleRole.student);
else peopleRoles.add(PeopleRole.rich);
}
}
interface SuperMarketShopping {
void showRole();
void shopping(ShopPlace shopPlace);
}
enum PeopleRole implements SuperMarketShopping{
worker { // 打工人
@Override
public void showRole() {
System.out.print("打工人:");
}
@Override
public void shopping(ShopPlace shopPlace) {
switch (shopPlace) {
case food:
System.out.println("兰州拉面");
break;
case relax:
System.out.println("看场电影");
break;
case clothing:
System.out.println("去优衣库买T恤");
break;
}
}
},
student { // 学生
@Override
public void showRole() {
System.out.print("学生:");
}
@Override
public void shopping(ShopPlace shopPlace) {
switch (shopPlace) {
case food:
System.out.println("吃麻辣烫");
break;
case relax:
System.out.println("去电玩城");
break;
case clothing:
System.out.println("不买衣服,看一下");
break;
}
}
},
rich { // 有钱人
@Override
public void showRole() {
System.out.print("有钱人:");
}
@Override
public void shopping(ShopPlace shopPlace) {
switch (shopPlace) {
case food:
System.out.println("鱼子酱配佛跳墙");
break;
case relax:
System.out.println("私人SPA");
break;
case clothing:
System.out.println("去Prada买衣服");
break;
}
}
}
}
enum ShopPlace {
clothing,
food,
relax
}
}
上面代码存在以下问题:
1)所有业务都集中在一个类中,类功能繁多,违背单一原则。
2)条件判断语句过多。
3)如果新增一个角色或者店铺地区,那么将增加更多的条件判断语句。也违背了开闭原则。
访问者模式提供一个作用于某对象结构中的各元素的操作表示。它包含访问者(打工人、学生及有钱人)和被访问元素(服装区、饮食区及休闲区)两个主要组成部分。这些被访问的元素通常具有不同的类型,且不同的访问者对它们进行不同的访问操作。
图 访问者模式UML
Visitor,抽象访问者。为对象中每个具体元素类ConcreteElement声明一个访问操作,从这个操作的参数类型可以清楚知道访问的具体元素的类型。
ConcreteVisitor,具体访问者。实现了每个由抽象访问者声明的操作,每个操作用于访问对象结构中一种类型的元素。
Element,抽象元素。定义了一个accept方法,该方法通常以一个抽象访问者作为参数。
ConcreteElement,具体元素,实现了accept方法,在accept方法中调用访问者的访问方法以便完成对一个元素的操作。
ObjectStructure,对象结构是一个元素的集合,用于存放元素对象,并且提供了遍历其内部元素的方法。
public interface RoleVisitor {
void visit(ClothingSpaceElement element);
void visit(FoodSpaceElement element);
void visit(RelaxSpaceElement element);
}
public interface SpaceElement {
void accept(RoleVisitor visitor);
}
public class WorkVisitor implements RoleVisitor{
@Override
public void visit(ClothingSpaceElement element) {
System.out.println("打工人:去优衣库买件衣服");
}
@Override
public void visit(FoodSpaceElement element) {
System.out.println("打工人:吃兰州拉面");
}
@Override
public void visit(RelaxSpaceElement element) {
System.out.println("打工人:去看场电影");
}
}
public class StudentVisitor implements RoleVisitor{
@Override
public void visit(ClothingSpaceElement element) {
System.out.println("学生:只逛逛");
}
@Override
public void visit(FoodSpaceElement element) {
System.out.println("学生:吃麻辣烫");
}
@Override
public void visit(RelaxSpaceElement element) {
System.out.println("学生:去游戏厅打游戏");
}
}
public class RicherManVisitor implements RoleVisitor{
@Override
public void visit(ClothingSpaceElement element) {
System.out.println("有钱人:买Prada的衣服");
}
@Override
public void visit(FoodSpaceElement element) {
System.out.println("有钱人:吃鱼子酱配佛跳墙");
}
@Override
public void visit(RelaxSpaceElement element) {
System.out.println("有钱人:私人SPA");
}
}
public class ClothingSpaceElement implements SpaceElement{
@Override
public void accept(RoleVisitor visitor) {
visitor.visit(this);
}
}
public class FoodSpaceElement implements SpaceElement{
@Override
public void accept(RoleVisitor visitor) {
visitor.visit(this);
}
}
public class RelaxSpaceElement implements SpaceElement{
@Override
public void accept(RoleVisitor visitor) {
visitor.visit(this);
}
}
优点:
1)增加新的访问操作很方便。只需要增加一个新的具体访问者类,实现简单,无须修改源代码,符合开闭原则。
2)将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中,类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构可以供多个不同的访问者访问。
缺点:
1)增加新的元素类很困难,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每个具体访问者类中增加相应的具体操作,这违背了开闭原则。
2)破坏封装,访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态。
1)一个对象结构包含多种类型的对象,希望对这些对象实施一些依赖其具体类型的操作。
2)需要对一个对象结构中的对象进行很多不同且不相关的操作,而且需要避免让这些操作“污染”这些对象的类,也不需要在增加新操作时修改这些类。
3)对象结构中元素对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。