行为型模式用于控制对象的行为。
- 状态模式:将对象的状态抽象为一个类,是对if switch等状态判断的代替,Context类、State接口及其具体类。
- 备忘录模式:获取对象状态没在对象之外保存状态,状态恢复,白箱和黑箱,宽、窄接口,私有内部类。
- 策略模式:把算法的实现单独抽出一个类,对if switch等分支语句的替换,封装算法,并且使他们可以相互特换。
- 调停者模式:一组对象关系错综复杂,加入调停者,各个对象只要跟调停者打交道。
- 模板方法模式:父类定义模板方法和固定顺序,子类继承并重写模板方法。
- 解释器模式:语言解释执行,可以将语言中的句子表示为一个抽象语法树,循环递归。
1.状态模式State
状态模式允许一个对象在其内部状态改变时,更改它的行为,这使得对象看起来就像是修改了它的类一样。
状态模式的结构要点是将状态抽象为单独的类层次,用环境类聚合状态类,当调用与状态相关的方法时,环境对象会将其委托给状态对象。
1.Context(环境):上下文类,这个实例可以定义当前包含的状态。
2.State(状态):定义一个接口以封装与Context的一个特定状态相关的行为。
3.ConcreteState(具体状态):每一子类实现一个与Context的一个状态相关的行为。
实现
class Context { //环境
private State _state; //维护一个当前的状态实例
public Context(State state) {
_state = state;
}
public void request() { //状态执行请求操作,由客户调用
if (_state != null) {
_state.handle(this);
}
}
public void changeState(State s) { //切换状态
if (_state != null) {
_state = null;
}
_state = s;
}
}
interface State { //状态接口
public void handle(Context ctx);
}
class ConcreteStateA implements State { //具体状态类
@Override
public void handle(Context ctx) { //完成本状态的行为,然后切换到下一状态
System.out.println("handle by ConcreteStateA");
if (ctx != null) { //切换到下一状态:切换工作转发给上下文Context来完成
ctx.changeState(new ConcreteStateB());
}
}
}
class ConcreteStateB implements State {
@Override
public void handle(Context ctx) {
System.out.println("handle by ConcreteStateB");
if (ctx != null) {
ctx.changeState(new ConcreteStateA());
}
}
}
public class StateClient {
public static void main(String[] args) {
State state = new ConcreteStateA();
Context context = new Context(state);
context.request(); //内部会把状态转发给具体状态类,它完成状态的行为后,
//又会返回到Context,以进行状态切换
context.request();
context.request();
context.request();
}
}
2.备忘录模式Memento
备忘录模式可以在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后再需要时就将该对象恢复到原先保存的状态。
备忘录模式可以将一个对象的状态外部化,并可以根据需要在适当的时候恢复状态。有两种实现方式:白箱实现和黑箱实现。
白箱实现:
1.Memento备忘录角色:(1)将原发器对象的内部状态存储起来,备忘录可以根据原发器对象的判断来决定存储多少原发器对象的内部状态。(2)备忘录可以保护其内容不被原发器对象之外的任何对象所读取。
2.Originator原发器角色:(1)创建一个含有当前内部状态的备忘录对象;(2)使用备忘录对象存储其内部状态;
3.Caretaker负责人角色:(1)负责保存备忘录对象;(2)不检查备忘录对象的内容;
实现:
//Memento备忘录角色
public class Memento{
private String state;
public String getState(){ return state;}
public void setState(String state){this.state = state;}
public Memento(String state){
super();
this.state = state;
}
}
//原发器类
public class Originator{
private String state;
public String getState(){return state;}
public void setState(String state){this.state= state;}
//创建备忘录对象
public Memento createMemento(){
return new Memento(this.state);
}
//恢复状态
public void setMemento(Memento memento){
this.state = memento.getState();
}
}
//负责人类
public class Caretaker{
private Memento memento;
//保存备忘录对象
public void saveMemento(Memento memento){
this.memento = memento; }
//取回备忘录对象
public Memento retrieveMemento(){ return memento;}
}
//客户端代码
public class Client{
//创建原发器、负责人
public static void main(String[] args){
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("初始状态");
System.out.print("当前状态"+originator.getState());
System.out.print("保存当前状态");
caretaker.saveMemento(originator.createMemento());
System.out.print("设置新状态");
originator.setState("新状态");
System.out.print("当前状态"+originator.getState());
System.out.print("恢复原状态");
originator.setMemento(caretaker.retriveMemento());
System.out.print("当前状态"+originator.getState());
}
}
白箱备忘录中,备忘录类可以被任何对象访问,宽接口和窄接口的问题,由黑箱模式解决。
黑箱模式可以将备忘录类设置为对原生器类开放宽接口,对负责人类提供窄接口。
定义私有内部类,外部无法访问,实现窄接口。将备忘录类定义为原发器类的内部类,可以保证他们之间能够互相访问各自的私有成员,实现宽接口。
实现
//备忘录接口,声明式接口,无需定义任何方法
public interface IMenmento{}
//原发器类
public class Originator{
private String state;
public String getState(){ return state;}
public void setState(Stirng state){this.state = state;}
//创建备忘录对象
public IMemento createMemento(){
return this.new Memento(this.state);
}
//恢复状态
public void setMemento(IMemento memento){
this.state = ((IMemento)memento).getState();
}
//备忘录,内部类
private class Memento implements IMemento{
private String state;
public String getState(){ return state;}
public void setState(String state){this.state = state;}
public Memento(String state){super(); this.state = state;}
}
}
//负责人类
public class Caretaker{
private IMemento memento;
//保存备忘录对象
public void saveMemento(IMemento memento){
this.memento = memento; }
//取回备忘录对象
public IMemento retrieveMemento(){ return memento;}
}
//客户端代码
public class Client{
//创建原发器、负责人
public static void main(String[] args){
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("初始状态");
System.out.print("当前状态"+originator.getState());
System.out.print("保存当前状态");
caretaker.saveMemento(originator.createMemento());
System.out.print("设置新状态");
originator.setState("新状态");
System.out.print("当前状态"+originator.getState());
System.out.print("恢复原状态");
originator.setMemento(caretaker.retriveMemento());
System.out.print("当前状态"+originator.getState());
}
}
可以与命令模式相组合,实现undo,redo等命令。
在web应用中,Session和Cookie实现备忘录模式。
3.策略模式
策略模式目的是定义一系列算法,把他们一个个封装起来,并且使他们可以相互替换,从而使得算法可以独立于使用它的客户而发生变化。
1.环境角色Context:拥有一个策略角色的引用;也叫上下文;
2.抽象策略角色Strategy:这是一个抽象角色,通常由一个接口或者抽象类来实现。
3.具体策略角色: 包装了相应的算法和行为。
4.客户端:使用Context和具体的策略对象进行操作。
实现
interface DatabaseStrategy { //Strategy:抽象策略
public void process();
}
class MysqlDBStrategy implements DatabaseStrategy { //具体策略
@Override
public void process() {
System.out.println("处理Mysql数据库连接");
}
}
class OracleDBStrategy implements DatabaseStrategy {
@Override
public void process() {
System.out.println("处理Oracle数据库连接");
}
}
class DataBaseManager { //Context角色
public void process(DatabaseStrategy dbStrategy) {
dbStrategy.process();
}
}
public class StrategyClient {
public static void main(String[] args) {
MysqlDBStrategy mysql = new MysqlDBStrategy();
DataBaseManager manager = new DataBaseManager();
manager.process(mysql);
OracleDBStrategy oracle = new OracleDBStrategy();
manager.process(oracle);
}
}
4.调停者模式Mediator
调停者模式定义了一个可以封装一组对象之间的相互影响的行为的对象,这样就可以使用松耦合的方式联系一组对象,避免对象之间相互显示地直接引用,从而当改变了某些对象之间的关系时,就可以不影响其他对象了。
相当于在个团队之间加上一个经理,负责管理所有队员的沟通,使同事关系变得简洁。
1.IMediator调停者接口:用于与各个同事IColleague对象通信;
2.MediatorImpl具体调停者:负责封装各个同事对象之间的协调关系并维护各个同事对象的实例;
3.IColleague同时接口:此接口供调停者使用以实现协调;
4.XColleagueImpl(具体同事类):维护调停者对象并在需要时调用调停者的协作方法。
实现
import java.util.ArrayList;
//抽象调停者
abstract class AbstractMediator {
//注册同事
public abstract void register(AbstractColleague ac);
//协调各同事对象实现协作行为
public abstract void ColleagueChanged(AbstractColleague ac);
}
//抽象同事
abstract class AbstractColleague {
private AbstractMediator med; //每个同事都知道中介者,故要引入中介者
public AbstractColleague(AbstractMediator mediator) { //通过构造函数传入
this.med = mediator;
}
public abstract void action(); //对象的操作行为
public void changed() { //把交互行为发送给中介者
med.ColleagueChanged(this);
}
}
//具体调停者
class ConcreteMediator extends AbstractMediator {
//中介者要维护一个同事列表
private ArrayList<AbstractColleague> colleagueList = new ArrayList<>();
@Override
public void register(AbstractColleague ac) {
colleagueList.add(ac);
}
@Override
public void ColleagueChanged(AbstractColleague ac) {
for (int i = 0; i < colleagueList.size(); i++) {
if (colleagueList.get(i) == ac) {
colleagueList.get(i).action();
}
}
}
}
//具体同事
class ConcreteColleagueA extends AbstractColleague {
public ConcreteColleagueA(AbstractMediator mediator) { //同事要知道中介者
super(mediator);
mediator.register(this); //把自己注册给中介者
}
@Override
public void action() { //对象的操作行为,由中介者来调用
System.out.println("AAAAAAAAAAAAAAA");
}
}
class ConcreteColleagueC extends AbstractColleague {
public ConcreteColleagueC(AbstractMediator mediator) {
super(mediator);
mediator.register(this);
}
@Override
public void action() {
System.out.println("CCCCCCCCCCCCCCC");
}
}
class ConcreteColleagueB extends AbstractColleague {
public ConcreteColleagueB(AbstractMediator mediator) {
super(mediator);
mediator.register(this);
}
@Override
public void action() {
System.out.println("BBBBBBBBBBBBBBB");
}
}
class MediatorTest {
public static void main(String[] args) {
AbstractMediator mediator = new ConcreteMediator();
AbstractColleague colleagueA = new ConcreteColleagueA(mediator);
AbstractColleague colleagueB = new ConcreteColleagueB(mediator);
AbstractColleague colleagueC = new ConcreteColleagueC(mediator);
colleagueA.changed();
colleagueB.changed();
colleagueC.changed();
}
}
5.模板方法模式
模板方法模式的设计意图是由抽象父类控制顶级逻辑,并把基本操作的实现推迟到子类中去实现,这是通过集成的手段来达到对对象的复用。
通过继承的方式实现步骤的重用,子类只需要重写特定的方法即可实现一个特定的流程。
1、AbstractClass(抽象类):定义一到多个抽象方法(也可以不是抽象方法,但至少应该是virtual方法。视你的应用需要,如果你的AbstractClass负责实现一个通用版本的算法,各子类对该方法进行进一步细化,则只需定义成virtual方法即可),具体的子类将重定义它们以实现一个算法;而且还实现一个模板方法,来定义一个算法的骨架。该模板方法不仅调用前面的抽象方法,也可以调用其他的操作。各抽象方法往往被定义成protected(保护)成员,以保证它们只被模板方法调用,而TemplateMethod往往被定义成public非虚成员函数。
2、ConcreteClass(具体类):实现父类中的抽象方法以完成算法中与特定子类相关的步骤
实现
abstract class QueryTemplate { //抽象类:算法的模板
//Template Method:模板方法,一般为public的,用来执行算法的各步
public void doQuery() {
formatConnect();
formatSelect();
}
//算法的骨架,一般为protected的,由子类来实现它们
//可见,子类并不能改变算法的框架结构,但可以改变算法的实现步骤
protected abstract void formatConnect();
protected abstract void formatSelect();
}
class OracleQT extends QueryTemplate { //实现算法的具体子类
@Override
public void formatConnect() {
System.out.println("格式化Qracle数据库连接");
}
@Override
public void formatSelect() {
System.out.println("格式化Oracle数据库查询");
}
}
class MysqlQT extends QueryTemplate {
@Override
public void formatConnect() {
System.out.println("格式化Mysql数据库连接");
}
@Override
public void formatSelect() {
System.out.println("格式化Mysql数据库查询");
}
}
public class TemplateTestClient {
public static void main(String[] args) {
QueryTemplate oracleQT = new OracleQT();
oracleQT.doQuery(); //调用抽象模板的模板方法,以执行算法
QueryTemplate mysqlQT = new MysqlQT();
mysqlQT.doQuery();
}
}
解释器模式
解释器模式给定一种语言,定义它的文法标识,并定义一个解释器,然后这个解释器将使用该文法标识来解释语言中的句子。
Expression表达式接口:声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。
终结符表达式:实现与文法中的终结符相关联的解释操作,一个句子中的每个终结符都需要使用该类的一个实例。
非终结符表达式,对位文法中的每一条规则都需要一个非终结表达式类,来为文法中的非终结符实现解释操作,解释操作一般要递归地调用表示之前那些对象的解释操作。
运算器:表示该文法定义的语言中一个特定的句子的抽象语法树。
客户端:驶入需要解释的表达式,调用运算器进行解释动作。
实现
import java.util.HashMap;
import java.util.Map;
class Context { //上下文环境
private Map valueMap = new HashMap();
//一个变量对应一个值,相当于文法的一个推导规则
public void addValue(Variable x, int y) {
Integer yi = new Integer(y);
valueMap.put(x, yi);
}
//从左边的变量(即非终结符),返回右边的终结符
public int LookupValue(Variable x) {
int i = ((Integer) valueMap.get(x)).intValue();
return i;
}
}
//AbstractExpression:抽象表达式,也可以用接口来实现
abstract class Expression {
public abstract int interpret(Context con); //声明一个抽象的解释操作
}
//TerminalExpression:终结符表达式
//实现与文法中的终结符相关联的解释操作。句子中的每个
//终结符需要该类的一个实例与之对应
class Constant extends Expression {
private int i;
public Constant(int i) {
this.i = i;
}
@Override
public int interpret(Context con) { //用文法解释终结符:直接返回此终结符
return i;
}
}
//单个非终结符(变量)的表达式,文法每条规则左边的非终结符需要该类的一个实例
class Variable extends Expression {
@Override
public int interpret(Context con) {
//this为调用interpret方法的Variable对象
return con.LookupValue(this);
}
}
//NonterminalExpression:非终结符表达式R1+R2,有多个非终结符
class Add extends Expression {
private Expression left, right;
public Add(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context con) { //用文法来解释非终结符表达R1+R2
//一般要递归地调用表示从R1到Rn的那些对象的解释操作
return left.interpret(con) + right.interpret(con);
}
}
class Subtract extends Expression {
private Expression left, right;
public Subtract(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context con) {
return left.interpret(con) - right.interpret(con);
}
}
class Multiply extends Expression {
private Expression left, right;
public Multiply(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context con) {
return left.interpret(con) * right.interpret(con);
}
}
class Division extends Expression {
private Expression left, right;
public Division(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context con) {
try {
return left.interpret(con) / right.interpret(con);
} catch (ArithmeticException ae) {
System.out.println("被除数为0!");
return -11111;
}
}
}
//测试程序,计算 (a*b)/(a-b+2)
public class InterpreterTest {
private static Expression ex;
private static Context con;
public static void main(String[] args) {
con = new Context();
//设置变量、常量
Variable a = new Variable();
Variable b = new Variable();
Constant c = new Constant(2);
//为变量赋值
con.addValue(a, 8);
con.addValue(b, 7); //这些工作相当于完成文法的存储
//运算,对句子的结构(即抽象语法树)由我们自己来分析和构造
//可见解释器模式并没有说明如何创建一个抽象语法树
ex = new Division(new Multiply(a, b), new Add(new Subtract(a, b), c));
System.out.println("运算结果为:" + ex.interpret(con));
}
}