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版第八篇的主要内容,如果未能解决你的问题,请参考以下文章