23种设计模式Java版第七篇

诸葛亮
• 阅读 757

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 方法。

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

点赞
收藏
评论区
推荐文章
观察者模式在spring中的应用
作者:王子源1观察者模式简介1.1定义指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布订阅模式、模型视图模式,它是对象行为型模式。
可莉 可莉
4年前
了解Vuex状态管理模式
1Vuex是什么呢?它是Vue的状态管理模式,在使用vue的时候,需要在vue中各个组件之间传递值是很痛苦的,在vue中我们可以使用vuex来保存我们需要管理的状态值,值一旦被改变,所有引用该值的地方就会自动更新。是不是很方便,很好用呢?vuex是专门为vue.js设计的状态管理模式,集中式存储和管理应用程序中所有组件的状态,vuex也集成了vue的
徐小夕 徐小夕
5年前
vue高级进阶系列——用typescript玩转vue和vuex
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用过vue的朋友大概对vuex也不陌生,vuex的官方解释是专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。说的简单点就是对vue的状态进行统一管理,如下图介绍了其管理模式:im
Wesley13 Wesley13
4年前
C#游戏开发快速入门2.2改变游戏对象的状态
C游戏开发快速入门2.2改变游戏对象的状态改变游戏对象的状态,就是要改变游戏对象的位置、朝向和大小。那么,为什么要改变游戏对象的状态呢?当然是因为游戏对象的状态不合适了。在具体说明之前,读者应该先了解下MainCamera对象的作用。本文选自《C游戏开发快速入门(大学霸)》2.2.1  Ma
Wesley13 Wesley13
4年前
EA&UML日拱一卒
点击上方【面向对象思考】可快速关注本公众号!行为状态机概念以下内容摘自UML2,今天的内容是说明状态机具有上下文类目的情况。行为状态机可以用于定义下面的场景(不限于)主动类的类目行为一个行为化类目的除了类目行为以外的拥有行为。如果状态机有某种行为类目
Wesley13 Wesley13
4年前
Java设计模式之状态模式详解
(本文由言念小文原创,转载请注明出处)在实际工作中经常遇到某个对象,处于不同的状态有不同行为逻辑、且状态之间可以相互迁移的业务场景,特别是在开发通信协议栈类软件中尤为多见。《设计模式之禅》这本书中对状态模式有着非常详尽的讲解(目前为止我认为讲解得最好的书),但总觉得自己没能够理解透彻、灵活运用。直到今年完成了一个通信协议软件的开发,重新研究了“状态机”,
Wesley13 Wesley13
4年前
PHP状态模式
状态设计模式状态模式的作用是允许对象在状态改变时改变其行为对象中频繁的状态非常依赖于条件语句,就自身来说条件语句并没有什么问题,不过,如果选项太多,以至于程序出现混乱,或者增加或改变选项需要太多的是时间。<?php/CreatedbyPhpStorm.User:ge
Wesley13 Wesley13
4年前
C++设计模式——备忘录模式
备忘录模式在GOF的《设计模式:可复用面向对象软件的基础》一书中对备忘录模式是这样说的:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。有时有必要记录一个对象的内部状态。为了允许用户取消不确定的操作或从错误中恢复过来,需要实现检查点和取消机制,而要实现这些机制,你必须事先
Wesley13 Wesley13
4年前
22. 状态模式
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的context对象。介绍意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。何时使用:代码中包含大量与对象状态有关的条件语句
玩转Spring状态机 | 京东云技术团队
说起Spring状态机,大家很容易联想到这个状态机和设计模式中状态模式的区别是啥呢?没错,Spring状态机就是状态模式的一种实现,在介绍Spring状态机之前,让我们来看看设计模式中的状态模式。1\.状态模式状态模式的定义如下:状态模式(StatePat
玩转Spring状态机
说起Spring状态机,大家很容易联想到这个状态机和设计模式中状态模式的区别是啥呢?没错,Spring状态机就是状态模式的一种实现,在介绍Spring状态机之前,让我们来看看设计模式中的状态模式。1.状态模式状态模式的定义如下:状态模式(StatePatt