访问者模式

访问者模式是一种集中规整模式,适合用于重构项目时(重构时需求已经清晰,原有的功能点也明确)
通过访问者模式可以很容易达到功能集中化。
还可以与其他模式混编建立一套自己的过滤器和拦截器。

定义

封装一些用于某种数据结构中的各个元素的操作,可以在不改变数据结构的前提下定义作用于这些元素的新操作。

访问者模式通用类图

角色

Visitor 抽象访问者

抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。

1
2
3
4
public interface IVisitor{
public void visit(ConcreteElement1 el1);
public void visit(ConcreteElement2 el2);
}

ConcreteVisitor 具体访问者

影响访问者访问到一个类后该做什么事情。

1
2
3
4
5
6
7
8
public class Visitor implements IVisitor{
public void visit(ConcreteElement1 el1){
el1.doSomething();
}
public void visit(ConcreteElement2 el2){
el2.doSomething();
}
}

Element 抽象元素

接口或者抽象类,声明接受哪一类访问者访问。
抽象元素有两类方法, 一类是本身的业务逻辑。另一类是允许访问者来访问的方法。

1
2
3
4
5
6
public abstract class Element{
//业务逻辑
public abstract void doSomething();
//接受访问者
public abstract void accept(IVisitor visitor);
}

ConcreteElement 具体元素

实现accept方法, 通常是 visitor.visit(this)模式。

1
2
3
4
5
6
7
8
9
10
11
12
public class ConcreteElement1 extends Element{
public void doSomething(){}
public void accept(IVisitor visitor){
visitor.visit(this);
}
}
public class ConcreteElement2 extends Element{
public void doSomething(){}
public void accept(IVisitor visitor){
visitor.visit(this);
}
}

ObjectStructure结构对象

元素生产者,一般容纳在多个不同类、不同接口的容器,如List/Set/Map。

1
2
3
4
5
public class ObjectStructure{
public static Element createElement(){
return new Random().nextInt(10) > 5 ? new ConcreteElement1() : new ConcreteElement2();
}
}

使用

1
2
3
for(int i = 0; i < 10 ; i++){
ObjectStructure.createElement().accept(new Visitor());
}

优点

  • 符合单一职责原则
  • 优秀的扩展性
  • 灵活

缺点

  • 违背了迪米特法则,Element具体的细节需要公布给Visitor
  • 具体元素变更比较困难,元素增加/减少字段Visitor都需要修改
  • 违背了依赖倒置原则,访问者依赖的是具体的元素,而不是抽象元素。

使用场景

当一个对象结构包含很多类对象,它们有不同的接口,需要对这些对象实施一些依赖于其具体类的操作。

双分派

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public abstract class AbsActor{
public void act(Role role){
System.out.println("Actor can act any role.");
}
public void act(KungFuRole role){
System.out.println("Actor can act Kungfu Role.");
}
}

public class YongActor extends AbsActor{
public void act(KungFuRole role){
System.out.println("Yong Man Lov Act KungFu Role.");
}
}

public class OldActor extends AbsActor{
public void act(KungFuRole role){
System.out.println("Old Man Can Not Act KungFu Role.")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Role{
public void accept(AbsActor actor);
}
public class KungFuRole extends Role{
public void accept(AbsActor actor){
actor.act(this);
}
}
public class IdiotRole extends Role{
public void accept(AbsActor actor){
actor.act(this);
}
}
1
2
3
4
AbsActor actor = new OldActor();
Role role = new KungFuRole();
actor.act(role); //oldActor.act(Role)
actor.act(new KungFuRole()); //oldActor.act(KungFuRole);
1
2
3
4
AbsActor actor = new OldActor();
Role role = new KungFuRole();
role.accept(actor); //KungFuRole.accept(OldActor);
//OldActor.act(KungFuRole)

状态模式

在状态模式中, 类的行为时给予它的状态改变的。折中类型的设计模式属于行为型模式。

定义

当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。

状态模式通用类图

实现

抽象状态角色State

接口或者抽象类,负责对象状态的定义,并且封装环境角色以实现状态切换。

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface IState {
void open();
void close();
void run();
void stop();
}

public abstract class BaseState implements IState {
protected Car context;
public void setContext(Car context) {
this.context = context;
}
}

具体状态角色ConcreteState

每个具体状态必须完成两个职责:本状态下要做的事情以及本状态如何过渡到其他状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class OpenState extends BaseState {
public void open() {
super.context.setCurrentState(Car.OPEN_STATE);
System.out.println("车门已开启");
}
public void close() {
super.context.setCurrentState(Car.CLOSE_STATE);
System.out.println("关闭车门。。。");
}
public void run() {
System.out.println("开门状态下不允许飙车start");
}
public void stop() {
System.out.println("开门状态下不允许飙车Stop");
}
}


public class StopState extends BaseState {
public void open() {
super.context.setCurrentState(Car.OPEN_STATE);
System.out.println("停车后开启车门");
}
public void close() {
super.context.setCurrentState(Car.CLOSE_STATE);
System.out.println("停车后关闭车门");
}
public void run() {
super.context.setCurrentState(Car.RUN_STATE);
System.out.println("停车后再次跑起来");
}
public void stop() {
super.context.setCurrentState(Car.STOP_STATE);
System.out.println("停车后。。停止不同");
}
}

public class RunState extends BaseState {
public void open() {
System.out.println("飙车时不能开门");
}
public void close() {
System.out.println("飙车时车门已关闭");
}
public void run() {
super.context.setCurrentState(Car.RUN_STATE);
System.out.println("飙车Run");
}
public void stop() {
super.context.setCurrentState(Car.STOP_STATE);
System.out.println("飙车 stop");
}
}

public class CloseState extends BaseState {
public void open() {
super.context.setCurrentState(Car.OPEN_STATE);
System.out.println("开启车门。。。");
}
public void close() {
System.out.println("车门已经关闭!");
}
public void run() {
this.context.setCurrentState(Car.RUN_STATE);
System.out.println("汽车开始运行。。。");
}
public void stop() {
this.context.setCurrentState(Car.STOP_STATE);
System.out.println("汽车停止。");
}
}

环境角色Context

定义客户端需要的接口,并且负责具体状态的切换。

把状态对象声明为静态常量,有几个状态对象就声明几个静态常量。
环境角色具有状态抽象角色定义的所有行为,具体执行使用委托方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Car implements IState {

public static final BaseState OPEN_STATE = new OpenState();
public static final BaseState CLOSE_STATE = new CloseState();
public static final BaseState RUN_STATE = new RunState();
public static final BaseState STOP_STATE = new StopState();
private BaseState currentState;
public Car() {
this.setCurrentState(CLOSE_STATE);
}
public BaseState getCurrentState() {
return currentState;
}
public Car setCurrentState(BaseState currentState) {
this.currentState = currentState;
this.currentState.setContext(this);
return this;
}
public void open() {
this.currentState.open();
}
public void close() {
this.currentState.close();
}
public void run() {
this.currentState.run();
}
public void stop() {
this.currentState.stop();
}
}

使用

结合建造者模式将已有的状态按照一定的顺序再重新组装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Car car = new Car();

car.setCurrentState(Car.CLOSE_STATE);

car.run(); //汽车开始运行。。。
car.stop(); //飙车 stop

car.run(); //停车后再次跑起来
car.open(); //飙车时不能开门

car.stop(); //飙车 stop

car.open(); //停车后开启车门
car.close(); //关闭车门。。。

应用

优点

  • 结构清晰
    避免过多的switch case和if else,降低了程序的复杂性,提高系统的可维护性。
  • 遵循设计原则
    遵循了开闭原则以及单一职责原则,每个状态都是一个子类,要增加状态就要增加子类,修改状态时只修改一个子类即可。
  • 封装性良好
    状态变换防止到类的内部来实现,外部调用不需要知道类内部如何实现状态和行为的变换。

缺点

  • 状态过多时子类膨胀

使用场景

  • 行为跟随状态改变而改变的场景
  • 条件、分支判断语句的替代