23种设计模式Java版第八篇

Posted 小二玩编程

tags:

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

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

ps:这一篇是写备忘录模式、中介者模式和解释器模式

1、备忘录模式

在保持封装性的条件下,获取一个对象的内部状态,在其他地方保存这个对象的状态,当需要用到该对象状态时就恢复到原先保存的状态。

备忘录模式具有以下几种角色:

(1)发起人角色:记录当前的内部状态,具有创建备忘录和恢复备忘录的方法。

(2)备忘录角色:存储发起人的内部状态,在需要用到的时候返回这些内部状态给发起人。

(3)管理者角色:对备忘录进行管理,只提供保存与获取备忘录的方法。

下面用代码举个例子:

(1)发起人角色,新建一个 Emp 类:

public class Emp {
private String ename;
private int age;
private double salary;
public Emp(String ename, int age, double salary) {

super();
this.ename = ename;
this.age = age;
this.salary = salary;

}

public EmpMemento memento() {

return new EmpMemento(this);

}

public void recovery(EmpMemento e) {

this.ename = e.getEname();
this.age = e.getAge();
this.salary = e.getSaraly();

}
public String getEname() {

return ename;

}
public void setEname(String ename) {

this.ename = ename;

}
public int getAge() {

return age;

}
public void setAge(int age) {

this.age = age;

}
public double getSalary() {

return salary;

}
public void setSalary(double salary) {

this.salary = salary;

}

}

(2)备忘录角色,新建一个 EmpMemento 类:

public class EmpMemento {
private String ename;
private int age;
private double saraly;

public EmpMemento(Emp emp) {

ename = emp.getEname();
age = emp.getAge();
saraly = emp.getSalary();

}
public String getEname() {

return ename;

}
public void setEname(String ename) {

this.ename = ename;

}
public int getAge() {

return age;

}
public void setAge(int age) {

this.age = age;

}
public double getSaraly() {

return saraly;

}
public void setSaraly(double saraly) {

this.saraly = saraly;

}

}

(3)管理者角色,新建一个 CareTaker 类:

public class CareTaker {
private EmpMemento memento;

public EmpMemento getMemento() {

return memento;

}

public void setMemento(EmpMemento memento) {

this.memento = memento;

}

}

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

    CareTaker taker = new CareTaker();
    Emp emp = new Emp("小周", 11, 300);
    System.out.println("第一次打印" + emp.getEname() + "---" + emp.getAge() + "---" + emp.getSalary());
    taker.setMemento(emp.memento());
    System.out.println("已经保存了之前的数据,现在对数据进行修改");
    emp.setEname("小二");
    emp.setAge(19);
    emp.setSalary(99);
    System.out.println("第二次打印" + emp.getEname() + "---" + emp.getAge() + "---" + emp.getSalary());
    System.out.println("正在撤销,恢复之前保存的数据");
    emp.recovery(taker.getMemento());
    System.out.println("第三次打印" + emp.getEname() + "---" + emp.getAge() + "---" + emp.getSalary());

日志打印如下所示:

图片

首先一开始就实例化一个发起人角色 Emp,它的内部状态就是 ename、age 和 salary 属性,发起人角色创建备忘录角色 EmpMemento,顺便把自己的状态赋值给 EmpMemento 的状态,然后管理者角色 CareTaker 就把备忘录角色 EmpMemento 存储起来;当我们把发起人角色 Emp 的状态修改后,我们要想恢复之前的状态,就必须通过 CareTaker 的 getMemento 方法获取发起人角色 Emp,并通过 Emp 的 recovery 方法最终实现恢复之前的状态
;这个就像我们用文件编辑文字一样,我们要想恢复上一次编辑的记录,就按下 Ctrl + z 就能撤回到上一次的记录了。

备忘录模式虽然给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态;实现了信息的封装,使得用户不需要关心状态的保存细节,且状态信息都保存到备忘录里由管理者管理;但是如果类的成员变量过多,会占用很大的资源,同时保存也会消耗一定的内存;改动也很麻烦,当发起人角色的状态发生改变时,备忘录角色的状态也会发生改变。

2、中介者模式

用一个中介对象来封装一系列对象的交互过程,中介者使各对象可以隐式的相互引用,使其耦合松散,而且改变它们之间的交互是独立的。

中介者模式具有以下几种角色:

(1)抽象中介者角色:具体中介者要实现的接口,定义了同事对象注册和发送给同事对象信息的抽象方法。

(2)具体中介者角色:实现抽象中介者角色的子类,声明一个容器来管理同事对象,协助同事类角色的交互,它依赖于同事角色。

(3)抽象同事类角色:声明具体同事类角色要实现的接口,定义同事对象交互的抽象方法。

(4)具体同事类角色:抽象同事类角色的子类,实现抽象同事类角色所有的抽象方法,需要与其他同事对象进行交互时,由中介者对象协助。

下面用代码举个例子:

(1)抽象中介者角色,新建一个 Mediator接口:

public interface Mediator {

public void register(String name,Department d);
public void command(String name);

}

(2)抽象同事类角色,新建一个 Department接口:

public interface Department {
public void selfAction();
public void outAction();
}

(3)具体中介者角色,新建一个 President 类并实现 Mediator接口:

public class President implements Mediator{
private Map<String, Department> map = new HashMap<String, Department>();
@Override
public void register(String name, Department d) {

map.put(name, d);

}

@Override
public void command(String name) {

map.get(name).selfAction();

}

}

(4)具体同事类角色,新建一个 Market 类并实现 Department接口:

public class Market implements Department{
private Mediator mediator;
public Market(Mediator mediator) {

this.mediator = mediator;
mediator.register("market", this);

}
@Override
public void selfAction() {

System.out.println("Market---跑市场");

}

@Override
public void outAction() {

System.out.println("Market---汇报工作,项目正在进度");
mediator.command("finacial");
mediator.command("development");

}

}

(5)具体同事类角色,新建一个 Finacial 类并实现 Department接口:

public class Finacial implements Department{
public Finacial(Mediator mediator) {

mediator.register("finacial", this);

}
@Override
public void selfAction() {

System.out.println("Finacial---数钱");

}

@Override
public void outAction() {

System.out.println("Finacial---汇报工作,出账");

}

}

(6)具体同事类角色,新建一个 Development 类并实现 Department接口:

public class Development implements Department{
public Development(Mediator mediator) {

mediator.register("development", this);

}
@Override
public void selfAction() {

System.out.println("Development---专心科研项目");

}

@Override
public void outAction() {

System.out.println("Development---没钱了,需要资金支持");

}

}

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

    Mediator mediator = new President();
    Market market = new Market(mediator);
    new Finacial(mediator);
    new Development(mediator);
    market.selfAction();
    market.outAction();

日志打印如下所示:

图片

首先实例化一个中介者角色对象 mediator (实现类是 President),然后实例化具体同事类角色对象 new Market(mediator)、new Finacial(mediator) 和 new Development(mediator),并顺便把这些实例化具体同事类角色对象注册到中介者角色对象 mediator 中,此时具体同事类角色对象就会被保存到 Mediator 的实现类 President 的 Map 容器中;当 Market 调用 outAction 方法时,在该方法内部顺便通过中介者 mediator 的 command 方法调用 Finacial 的 outAction 方法和 Development 的 outAction 方法,最终实现了中介者模式的效果。

中介者模式虽然使类变得简单,将一对多转化成了一对一的关联,降低了各个类之间的耦合性,遵循迪米特原则;但是中介者会庞大,变得复杂难以维护,当同事类越多时,中介者就会越臃肿,每添加一个同事类,调用同事类的中介者很有可能要修改。

3、解释器模式

定义一个语言,定义它的文法表示,再定义一个解释器,这个解释器用来解释语言中的句子

解释器模式具有以下几种角色:

(1)抽象表达式角色:声明解释器的接口,规范解释器的解释行为。

(2)终结符表达式角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作。

(3)非终结符表达式角色:是抽象表达式的子类,用来实现文法中与非终结符相关的操作,它最终也是调用文法中与终结符相关的操作。

(4)环境角色:一般包含传递被所有解释器共享的数据或是公共的功能。

下面用代码举个例子:

(1)抽象表达式角色,新建一个 Expression 接口:

public interface Expression {
public boolean interpret(String s);
}

(2)终结符表达式角色,新建一个 TerminalExpression 类并实现 Expression 接口:

public class TerminalExpression implements Expression {
private Set<String> set = new HashSet<String>();

public TerminalExpression(String[] data) {

for (int i = 0; i < data.length; i++)
  set.add(data[i]);

}

public boolean interpret(String info) {

if (set.contains(info)) {
  return true;
}
return false;

}
}

(3)非终结符表达式角色,新建一个 AndExpression 类并实现 Expression 接口:

public class AndExpression implements Expression {
private Expression city = null;
private Expression person = null;

public AndExpression(Expression city, Expression person) {

this.city = city;
this.person = person;

}

public boolean interpret(String info) {

String s[] = info.split("的");
return city.interpret(s[0]) && person.interpret(s[1]);

}
}

(4)环境角色,新建一个 Context 类:

public class Context {
private String[] citys = { "深圳", "东莞" };
private String[] persons = { "老人", "学生", "儿童" };
private Expression cityPerson;

public Context() {

Expression city = new TerminalExpression(citys);
Expression person = new TerminalExpression(persons);
cityPerson = new AndExpression(city, person);

}

public void freeRide(String info) {

boolean b = cityPerson.interpret(info);
if (b) {
  System.out.println("这是" + info + ",本次进园旅游打5折!");
} else {
  System.out.println(info + ",不属于享半价优惠人员,按原价收费");

}

}
}

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

Context c = new Context();
c.freeRide("深圳的老人");
c.freeRide("东莞的年轻人");
c.freeRide("北京的妇女");
c.freeRide("上海的儿童");
c.freeRide("东莞的学生");

日志打印如下所示:

图片

首先实例化一个 Context 对象,在其构造方法中实例化2个终结符表达式角色对象 TerminalExpression 并传递字符串数组 citys 和 persons,创建好2个 TerminalExpression 对象之后,然后再创建非终结符表达式角色对象 AndExpression 并把2个 TerminalExpression 对象作为参数传入到它(AndExpression)的构造方法中;最后调用 Context 对象的 freeRide 方法,freeRide 方法的调用过程是这样的 freeRide --> AndExpression.interpret--> TerminalExpression.interpret,最终由终结符表达式角色 TerminalExpression 来解释语言中的句子;在这过程中 AndExpression 的 interpret 方法将“的”字切割出来,然后用 city.interpret 方法判断地方,person.interpret 判断年龄段的人。

虽然解释器模式在解释器模式中使用类来展示语言的文法规则,通过继承可以让拓展性变得更好,语法树中的表达式节点类是相似的,比较好实现;但是它会引起类膨胀,采用递归调用方法层次太多会容易出现栈溢出,可利用场景比较少。

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

23种设计模式Java版第一篇

23种设计模式Java版第六篇

23种设计模式Java版第七篇

23种设计模式Java版第三篇

Python全栈开发之路 第八篇:面向对象编程设计与开发

第八篇.SpringMVC入门学习