3.行为型模式
3.1观察者模式
- 意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
- 主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
- 何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
- 如何解决:使用面向对象技术,可以将这种依赖关系弱化。
- 关键代码:在抽象类里有一个 ArrayList 存放观察者们。
- 优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
- 缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
- 注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
package observerPattern;
import java.util.ArrayList;
import java.util.List;
public class ObserverPattern {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
Observer obs1 = new ConcreteObserver1();
Observer obs2 = new ConcreteObserver2();
subject.add(obs1);
subject.add(obs2);
subject.notifyObserver();
}
}
// 抽象目标
abstract class Subject {
protected List<Observer> observers = new ArrayList<>();
// 增加观察者方法
public void add(Observer observer) {
observers.add(observer);
}
// 删除观察者方法
public void remove(Observer observer) {
observers.remove(observer);
}
public abstract void notifyObserver(); //通知观察者方法
}
// 具体目标
class ConcreteSubject extends Subject {
public void notifyObserver() {
System.out.println("具体目标发生改变...");
System.out.println("--------------");
for (Observer obs : observers) {
obs.response();
}
}
}
// 抽象观察者
interface Observer {
void response(); //反应
}
// 具体观察者1
class ConcreteObserver1 implements Observer {
public void response() {
System.out.println("具体观察者1作出反应!");
}
}
// 具体观察者1
class ConcreteObserver2 implements Observer {
public void response() {
System.out.println("具体观察者2作出反应!");
}
}
import java.util.ArrayList;
import java.util.List;
public class ObserverPattern {
public static void main(String[] args) {
Debit zhangSan = new ZhangSan();
zhangSan.borrowMoney(new LiSi());
zhangSan.borrowMoney(new WangWu());
zhangSan.notifyCredits();
}
}
// 借款方
interface Debit{
void borrowMoney(Credit credit); // 借钱 添加观察者
void notifyCredits(); // 提醒贷款方收钱 通知观察者
}
// 贷款方
interface Credit{
void takeMoney();
}
class ZhangSan implements Debit{
private List<Credit> allCredits = new ArrayList<>();
private Boolean hasMoney = false; // 是否有钱
@Override
public void borrowMoney(Credit credit) {
// 主题对象添加观察者
allCredits.add(credit);
}
@Override
public void notifyCredits() {
allCredits.forEach(credit -> credit.takeMoney());
}
}
class LiSi implements Credit{
@Override
public void takeMoney() {
System.out.println("lisi");
}
}
class WangWu implements Credit{
@Override
public void takeMoney() {
System.out.println("wangwu");
}
}
3.2策略模式
-
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
-
主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
-
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
-
如何解决:将这些算法封装成一个一个的类,任意地替换。
-
关键代码:实现同一个接口。
-
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
-
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
-
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
package strategypattern;
public class StrategyPattern {
public static void main(String[] args) {
Context c = new Context();
Strategy s = new ConcreteStrategyA();
c.setStrategy(s);
c.strategyMethod();
s = new ConcreteStrategyB();
c.setStrategy(s);
c.strategyMethod();
}
}
// 抽象策略类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
interface Strategy {
void strategyMethod(); //策略方法
}
// 具体策略类A
class ConcreteStrategyA implements Strategy {
public void strategyMethod() {
System.out.println("具体策略A的策略方法被访问!");
}
}
// 具体策略类B
class ConcreteStrategyB implements Strategy {
public void strategyMethod() {
System.out.println("具体策略B的策略方法被访问!");
}
}
// 环境类:持有一个策略类的引用,最终给客户端调用。
class Context {
private Strategy strategy;
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void strategyMethod() {
strategy.strategyMethod();
}
}
3.3命令模式
-
意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
-
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
-
何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
-
如何解决:通过调用者调用接受者执行命令,顺序:调用者→命令→接受者。
-
关键代码:定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
-
优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。
-
缺点:使用命令模式可能会导致某些系统有过多的具体命令类。
-
注意事项:系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。
package commandpattern;
public class CommandPattern {
public static void main(String[] args) {
Command cmd = new ConcreteCommand();
Invoker ir = new Invoker(cmd);
System.out.println("客户访问调用者的call()方法...");
ir.call();
}
}
// 调用者:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
class Invoker {
private Command command;
public Invoker(Command command) {
this.command = command;
}
public void setCommand(Command command) {
this.command = command;
}
public void call() {
System.out.println("调用者执行命令command...");
command.execute();
}
}
// 抽象命令:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
interface Command {
void execute();
}
// 具体命令:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
class ConcreteCommand implements Command {
private Receiver receiver;
ConcreteCommand() {
receiver = new Receiver();
}
@Override
public void execute() {
receiver.action();
}
}
// 接收者:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
class Receiver {
public void action() {
System.out.println("接收者的action()方法被调用...");
}
}
3.4职责链模式
- 意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
- 主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
-
何时使用:
- 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
- 可动态指定一组对象处理请求,或添加新的处理者。
- 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
- 如何解决:拦截的类都实现统一接口。
- 关键代码:Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。
- 优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
- 缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。
- 注意事项:在 JAVA WEB 中遇到很多应用。
package chainOfResponsibilityPattern;
public class ChainOfResponsibilityPattern {
public static void main(String[] args) {
// 创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
// 组装责任链
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setNext(handler2);
// 提交请求
handler1.handleRequest("two");
}
}
// 抽象处理者角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
abstract class Handler {
private Handler next;
public void setNext(Handler next) {
this.next = next;
}
public Handler getNext() {
return next;
}
//处理请求的方法
public abstract void handleRequest(String request);
}
// 具体处理者角色1:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
class ConcreteHandler1 extends Handler {
public void handleRequest(String request) {
if (request.equals("one")) {
System.out.println("具体处理者1负责处理该请求!");
} else {
if (getNext() != null) {
getNext().handleRequest(request);
} else {
System.out.println("没有人处理该请求!");
}
}
}
}
// 具体处理者角色2
class ConcreteHandler2 extends Handler {
public void handleRequest(String request) {
if (request.equals("two")) {
System.out.println("具体处理者2负责处理该请求!");
} else {
if (getNext() != null) {
getNext().handleRequest(request);
} else {
System.out.println("没有人处理该请求!");
}
}
}
}
3.5状态模式
-
意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
-
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
-
何时使用:代码中包含大量与对象状态有关的条件语句。
-
如何解决:将各种具体的状态类抽象出来。
-
关键代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句。
-
优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
-
缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
-
注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。
package statePattern;
public class StatePattern {
public static void main(String[] args) {
Context context = new Context();
context.Handle();
context.Handle();
context.Handle();
context.Handle();
}
}
// 环境类:它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
class Context {
private State state;
// 定义环境类的初始状态
public Context() {
this.state = new ConcreteStateA();
}
// 设置新状态
public void setState(State state) {
this.state = state;
}
// 读取状态
public State getState() {
return (state);
}
// 对请求做处理
public void Handle() {
state.Handle(this);
}
}
// 抽象状态类:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
abstract class State {
public abstract void Handle(Context context);
}
// 具体状态A类:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
class ConcreteStateA extends State {
public void Handle(Context context) {
System.out.println("当前状态是 A.");
context.setState(new ConcreteStateB());
}
}
// 具体状态B类
class ConcreteStateB extends State {
public void Handle(Context context) {
System.out.println("当前状态是 B.");
context.setState(new ConcreteStateA());
}
}
3.6中介者模式
-
意图:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
-
主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
-
何时使用:多个类相互耦合,形成了网状结构。
-
如何解决:将上述网状结构分离为星型结构。
-
关键代码:对象 Colleague 之间的通信封装到一个类中单独处理。
-
优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。
-
缺点:中介者会庞大,变得复杂难以维护。
-
注意事项:不应当在职责混乱的时候使用。
package mediatorPattern;
import java.util.ArrayList;
import java.util.List;
public class MediatorPattern {
public static void main(String[] args) {
Mediator md = new ConcreteMediator();
Colleague c1, c2;
c1 = new ConcreteColleague1();
c2 = new ConcreteColleague2();
md.register(c1);
md.register(c2);
c1.send();
System.out.println("-------------");
c2.send();
}
}
// 抽象中介者:是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
abstract class Mediator {
public abstract void register(Colleague colleague);
public abstract void relay(Colleague cl); // 转发
}
// 具体中介者:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
class ConcreteMediator extends Mediator {
private List<Colleague> colleagues = new ArrayList<>();
@Override
public void register(Colleague colleague) {
if (!colleagues.contains(colleague)) {
colleagues.add(colleague);
colleague.setMedium(this); // 设置中介是自己
}
}
@Override
public void relay(Colleague cl) {
for (Colleague ob : colleagues) {
if (!ob.equals(cl)) {
ob.receive();
}
}
}
}
// 抽象同事类:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
abstract class Colleague {
protected Mediator mediator;
public void setMedium(Mediator mediator) {
this.mediator = mediator;
}
public abstract void receive();
public abstract void send();
}
// 具体同事类:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。
class ConcreteColleague1 extends Colleague {
public void receive() {
System.out.println("具体同事类1收到请求。");
}
public void send() {
System.out.println("具体同事类1发出请求。");
mediator.relay(this); // 请中介者转发
}
}
// 具体同事类
class ConcreteColleague2 extends Colleague {
public void receive() {
System.out.println("具体同事类2收到请求。");
}
public void send() {
System.out.println("具体同事类2发出请求。");
mediator.relay(this); // 请中介者转发
}
}
3.7迭代器模式
-
意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
-
主要解决:不同的方式来遍历整个整合对象。
-
何时使用:遍历一个聚合对象。
-
如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。
-
优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
-
缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
-
注意事项:迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
package iteratorPattern;
import java.util.ArrayList;
import java.util.List;
public class IteratorPattern {
public static void main(String[] args) {
Aggregate ag = new ConcreteAggregate();
ag.add("中山大学");
ag.add("华南理工");
ag.add("韶关学院");
System.out.print("聚合的内容有:");
Iterator it = ag.getIterator();
while (it.hasNext()) {
Object ob = it.next();
System.out.print(ob.toString() + "\t");
}
Object ob = it.first();
System.out.println("\nFirst:" + ob.toString());
}
}
// 抽象聚合:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
interface Aggregate {
void add(Object obj);
void remove(Object obj);
Iterator getIterator();
}
// 具体聚合:实现抽象聚合类,返回一个具体迭代器的实例。
class ConcreteAggregate implements Aggregate {
private List<Object> list = new ArrayList<>();
@Override
public void add(Object obj) {
list.add(obj);
}
@Override
public void remove(Object obj) {
list.remove(obj);
}
@Override
public Iterator getIterator() {
return (new ConcreteIterator(list));
}
}
// 抽象迭代器:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
interface Iterator {
Object first();
Object next();
boolean hasNext();
}
// 具体迭代器:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
class ConcreteIterator implements Iterator {
private List<Object> list = null;
private int index = -1;
public ConcreteIterator(List<Object> list) {
this.list = list;
}
@Override
public boolean hasNext() {
if (index < list.size() - 1) {
return true;
} else {
return false;
}
}
public Object first() {
index = 0;
Object obj = list.get(index);
;
return obj;
}
public Object next() {
Object obj = null;
if (this.hasNext()) {
obj = list.get(++index);
}
return obj;
}
}
3.8访问者模式
-
意图:主要将数据结构与数据操作分离。
-
主要解决:稳定的数据结构和易变的操作耦合问题。
-
何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
-
如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
-
关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
-
优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
-
缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
-
注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
package visitorPattern;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class VisitorPattern {
public static void main(String[] args) {
ObjectStructure os = new ObjectStructure();
os.add(new ConcreteElementA());
os.add(new ConcreteElementB());
Visitor visitor = new ConcreteVisitorA();
os.accept(visitor);
System.out.println("------------------------");
visitor = new ConcreteVisitorB();
os.accept(visitor);
}
}
// 抽象访问者:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,
// 该操作中的参数类型标识了被访问的具体元素。
interface Visitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
// 具体访问者A类:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
class ConcreteVisitorA implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("具体访问者A访问-->" + element.operationA());
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("具体访问者A访问-->" + element.operationB());
}
}
// 具体访问者B类
class ConcreteVisitorB implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("具体访问者B访问-->" + element.operationA());
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("具体访问者B访问-->" + element.operationB());
}
}
// 抽象元素类:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
interface Element {
void accept(Visitor visitor);
}
// 具体元素A类:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,
// 另外具体元素中可能还包含本身业务逻辑的相关操作。
class ConcreteElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationA() {
return "具体元素A的操作。";
}
}
// 具体元素B类
class ConcreteElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationB() {
return "具体元素B的操作。";
}
}
// 对象结构角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,
// 通常由 List、Set、Map 等聚合类实现。
class ObjectStructure {
private List<Element> list = new ArrayList<Element>();
public void accept(Visitor visitor) {
Iterator<Element> i = list.iterator();
while (i.hasNext()) {
i.next().accept(visitor);
}
}
public void add(Element element) {
list.add(element);
}
public void remove(Element element) {
list.remove(element);
}
}
3.9备忘录模式
-
意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
-
主要解决:所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
-
何时使用:很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有"后悔药"可吃。
-
如何解决:通过一个备忘录类专门存储对象状态。
-
关键代码:客户不与备忘录类耦合,与备忘录管理类耦合。
-
优点: 1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装,使得用户不需要关心状态的保存细节。
-
缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
-
注意事项: 1、为了符合迪米特原则,还要增加一个管理备忘录的类。 2、为了节约内存,可使用原型模式+备忘录模式。
package mementoPattern;
public class MementoPattern {
public static void main(String[] args) {
Originator or = new Originator();
Caretaker cr = new Caretaker();
or.setState("S0");
System.out.println("初始状态:" + or.getState());
cr.setMemento(or.createMemento()); //保存状态
or.setState("S1");
System.out.println("新的状态:" + or.getState());
or.restoreMemento(cr.getMemento()); //恢复状态
System.out.println("恢复状态:" + or.getState());
}
}
// 备忘录:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 发起人:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento createMemento() {
return new Memento(state);
}
public void restoreMemento(Memento m) {
this.setState(m.getState());
}
}
// 管理者:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
class Caretaker {
private Memento memento;
public void setMemento(Memento m) {
memento = m;
}
public Memento getMemento() {
return memento;
}
}
3.10模板方法模式
-
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
-
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
-
何时使用:有一些通用的方法。
-
如何解决:将这些通用算法抽象出来。
-
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
-
注意事项:为防止恶意操作,一般模板方法都加上 final 关键词。
package templatemethodpattern;
public class TemplateMethodPattern {
public static void main(String[] args) {
AbstractClass tm = new ConcreteClass();
tm.TemplateMethod();
}
}
//抽象类
abstract class AbstractClass {
//模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
public void TemplateMethod() {
SpecificMethod();
abstractMethod1();
abstractMethod2();
}
//具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
public void SpecificMethod() {
System.out.println("抽象类中的具体方法被调用...");
}
//抽象方法1:在抽象类中声明,由具体子类实现。
public abstract void abstractMethod1();
//抽象方法2
public abstract void abstractMethod2();
}
//具体子类
class ConcreteClass extends AbstractClass {
public void abstractMethod1() {
System.out.println("抽象方法1的实现被调用...");
}
public void abstractMethod2() {
System.out.println("抽象方法2的实现被调用...");
}
}
3.11解释器模式
-
意图:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
-
主要解决:对于一些固定文法构建一个解释句子的解释器。
-
何时使用:如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
-
如何解决:构建语法树,定义终结符与非终结符。
-
关键代码:构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。
-
优点: 1、可扩展性比较好,灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。
-
缺点: 1、可利用场景比较少。 2、对于复杂的文法比较难维护。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法。
-
注意事项:可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。
package interpreterPattern;
public class InterpreterPattern {
// 主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,
// 然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
}
// 抽象表达式类:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
interface AbstractExpression {
void interpret(String info); // 解释方法
}
// 终结符表达式类:是抽象表达式的子类,用来实现文法中与终结符相关的操作,
// 文法中的每一个终结符都有一个具体终结表达式与之相对应。
class TerminalExpression implements AbstractExpression {
public void interpret(String info) {
// 对终结符表达式的处理
}
}
// 非终结符表达式类:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,
// 文法中的每条规则都对应于一个非终结符表达式。
class NonterminalExpression implements AbstractExpression {
private AbstractExpression exp1;
private AbstractExpression exp2;
public void interpret(String info) {
// 非对终结符表达式的处理
}
}
// 环境类:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,
// 后面的解释器可以从这里获取这些值。
class Context {
private AbstractExpression exp;
public Context() {
// 数据初始化
}
public void operation(String info) {
// 调用相关表达式类的解释方法
}
}