23种设计模式Java版第七篇

Posted 小二玩编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了23种设计模式Java版第七篇相关的知识,希望对你有一定的参考价值。

ps:本文系转载文章,阅读原文可获取源码,文章末尾有原文链接

ps:这一篇是写状态模式和访问者模式

1、状态模式

对于有状态的对象,允许不同的状态对象放入复杂的“判断逻辑”,也允许状态对象的状态发生改变时改变它的行为。

状态模式具有以下几种角色:

(1)环境类角色:也称为上下文,内部拥有一个当前状态的接口,负责具体状态的切换。

(2)抽象状态角色:定义一个接口,给环境对象中的特定状态相对应的行为进行调用,它有一个或多个方法。

(3)具体状态角色:实现抽象状态所对应的抽象方法,在符合条件的情况下进行状态切换。

“允许不同的状态对象放入复杂的“判断逻辑” ”这句话就给读者自己去论证了,下面我们论证一下“允许状态对象的状态发生改变时改变它的行为”这句话,用代码举个例子:

(1)抽象状态角色,新建一个 State 接口:

public interface State {

public void handle();

}

(2)具体状态角色,新建一个 CheckedInState 类并实现 State 接口:

public class CheckedInState implements State{

@Override
public void handle() {
  System.out.println("房间已经入住");
}

}

(3)具体状态角色,新建一个 BookedState 类并实现 State 接口:

public class BookedState implements State{

@Override
public void handle() {
  System.out.println("房间已经预定");
}

}

(4)具体状态角色,新建一个 FreeState 类并实现 State 接口:

public class FreeState implements State{

@Override
public void handle() {
  System.out.println("房间空闲,没人住");
}

}

(5)环境类角色,新建一个 HomeContext 类:

public class HomeContext {

private State state;
public void setState(State state) {
  this.state = state;
  state.handle();
}
public State getState() {
    return state;
}

}

(6)客户端进行测试调用:

    HomeContext home = new HomeContext();
    home.setState(new CheckedInState());
    home.setState(new BookedState());
    home.setState(new FreeState());
    State state = home.getState();
    state.handle();

日志打印如下所示:

图片

从日志里可以看出环境类角色 HomeContext 每设置一次不同的状态,它不同状态(CheckedInState、BookedState、FreeState)的行为就会跟着去改变。

状态模式是将状态放到一个环境类中,只需要改变对象状态即可改变对象的行为,多个环境对象可以共享一个状态对象,从而减少系统中对象的个数,同时状态类职责明确,有利于程序的扩展;使用状态模式时,客户端要改变状态,但是状态的改变可能是不简单的;每个状态都要写一个子类,这样会增加类文件的数量。

2、访问者模式

把某一种数据结构中元素的操作分离出来封装成一个类,在不改变数据结构的前提下元素新的操作访问者改变而改变。

访问者模式具有以下几种角色:

(1)抽象访问者角色:声明一个访问具体元素的接口,抽象方法中的参数类型标识了被访问的具体元素。

(2)具体访问者角色:实现抽象访问者角色中声明的所有抽象方法,明确访问者访问元素的具体操作。

(3)抽象元素角色:声明一个具体元素做某些功能的接口。

(4)具体元素角色:实现抽象元素角色提供的抽象方法,同时具体元素也可以有本身业务逻辑的行为。

(5)对象结构角色:一个包含元素角色的容器一般是 List 等聚合类。

下面用代码举个例子:

(1)抽象访问者角色,新建一个 IVisitor 接口:

public interface IVisitor {

public void visit(Element1 element);
public void visit(Element2 element);

}

(2)具体访问者角色,新建一个 Concrete1 类并实现 IVisitor 接口:

public class Concrete1 implements IVisitor{

private static final String TAG = "Concrete1 类的 visit 方法-->";
@Override
public void visit(Element1 element) {
    String msg = TAG + element.operation();
    System.out.println(msg);
}

@Override
public void visit(Element2 element) {
    String msg = TAG + element.operation();
    System.out.println(msg);
}

}

(3)具体访问者角色,新建一个 Concrete2 类并实现 IVisitor 接口:

public class Concrete2 implements IVisitor{

private static final String TAG = "Concrete2 类的 visit 方法-->";
@Override
public void visit(Element1 element) {
    String msg = TAG + element.operation();
    System.out.println(msg);
}

@Override
public void visit(Element2 element) {
    String msg = TAG + element.operation();
    System.out.println(msg);
}

}

(4)抽象元素角色,新建一个 IElement 接口:

public interface IElement {
public void accept(IVisitor visitor);
}
(5)具体元素角色,新建一个 Element1 类并实现 IElement 接口:

public class Element1 implements IElement{

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

public String operation() {
    return "调用 Element1 类的 operation 方法";
}

}

(6)具体元素角色,新建一个 Element2 类并实现 IElement 接口:

public class Element2 implements IElement{

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

public String operation() {
    return "调用 Element2 类的 operation 方法";
}

}

(7)对象结构角色,新建一个 Structure 类:

public class Structure {

private List<IElement> list = new ArrayList<IElement>();

public void accept(IVisitor visitor) {
    IElement element = null;
    for (int i = 0; i < list.size();i++) {
       element = list.get(i);
       element.accept(visitor);
    }
}

public void add(IElement element) {
    list.add(element);
}

public void remove(IElement element) {
    list.remove(element);
}

}

(8)客户端进行测试调用:

    Structure s = new Structure();
    s.add(new Element1());
    s.add(new Element2());
    System.out.println("调用具体访问者 Concrete1------------------------");
    IVisitor visitor = new Concrete1();
    s.accept(visitor);
    System.out.println("调用具体访问者 Concrete2------------------------");
    visitor = new Concrete2();
    s.accept(visitor);

日志打印如下所示:

图片

我们把具体的元素 Element1 和 Element2 存储到对象结构类 Structure 中的容器 List,然后实例化具体访问者 Concrete1 和 Concrete2 并将它们参数丢到 Structure 类中的 accept 方法中;我就拿 Concrete1 作为参数解释一下,Concrete2 也是一样的思路;在 Structure 类中的 accept 方法中先遍历 List 里的元素,然后调用元素 IElement(实现者 Element1、Element2)的 accept 方法并把 IVisitor(实现者 Concrete1)作为参数传过去,Element1 和 Element2 的 accept 方法又调用 Concrete1 的 visit 方法,Concrete1 的 visit 方法又调用 Element1 和 Element2 的 operation 方法,思路就是具体元素 Element1 和 Element2 通过访问者 Concrete1 间接的调用自己的 operation 方法。

访问者模式符合单一职责原则,它的扩展性优秀,又具有灵活性;但是它增加或者删除元素类不容易,违反了依赖倒置原则,依赖了具体类,没有依赖抽象,破坏了对象的封装性,具体元素对访问者公开细节。

以上是关于23种设计模式Java版第七篇的主要内容,如果未能解决你的问题,请参考以下文章

23种设计模式Java版第一篇

23种设计模式Java版第六篇

23种设计模式Java版第八篇

23种设计模式Java版第三篇

第七篇 设计模式之桥接模式

python全栈开发第七篇Python文件操作