设计模式:(七)解释器、状态、策略、职责链模式

一、解释器模式

1、概述

1、解释器(Interpreter)模式:是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式),其中:“文法”指语言的语法规则,而“句子”是语言集中的元素。比如正则表达式。

2、主要角色

1、抽象表达式(Abstract Expression):声明一个抽象的解释操作,主要包含解释方法interpret(),这个方法为抽象语法树中所有的节点所 共享。
2、终结符表达式(Terminal Expression):是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
3、非终结符表达式(Nonterminal Expression):也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
4、环境(Context):通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。

3、解释器模式实现

/**
 * 抽象表达式,通过HashMap键值对, 可以获取到变量的值
 */
public abstract class Expression {
	/**
	 * 解释公式和数值, key 就是公式(表达式) 参数[a,b,c], value就是就是具体值,HashMap {a=10, b=20}
	 * @param var
	 * @return
	 */
	public abstract int interpreter(HashMap<String, Integer> var);
}

/**
 * 抽象运算符号解析器,这里,每个运算符号,都只和自己左右两个数字有关系,
 * 但左右两个数字有可能也是一个解析的结果,无论何种类型,都是Expression类的实现类
 */
public class SymbolExpression extends Expression {

	protected Expression left;
	protected Expression right;

	public SymbolExpression(Expression left, Expression right) {
		this.left = left;
		this.right = right;
	}

	/**
	 * 因为 SymbolExpression 是让其子类来实现,因此 interpreter 是一个默认实现
	 * @param var
	 * @return
	 */
	@Override
	public int interpreter(HashMap<String, Integer> var) {
		return 0;
	}
}

/**
 * 变量的解释器
 */
public class VarExpression extends Expression {

	// key=a,key=b,key=c
	private String key;

	public VarExpression(String key) {
		this.key = key;
	}

	/**
	 * 根据变量名称,返回对应值
	 * @param var
	 * @return
	 */
	@Override
	public int interpreter(HashMap<String, Integer> var) {
		return var.get(this.key);
	}
}

/**
 * 加法解释器
 */
public class AddExpression extends SymbolExpression  {

	public AddExpression(Expression left, Expression right) {
		super(left, right);
	}

	/**
	 * 处理相加
	 * @param var
	 * @return
	 */
	@Override
	public int interpreter(HashMap<String, Integer> var) {
		//super.left.interpreter(var) : 返回 left 表达式对应的值 a = 10
		//super.right.interpreter(var): 返回right 表达式对应值 b = 20
		return super.left.interpreter(var) + super.right.interpreter(var);
	}
}
/**
 * 减法解释器
 */
public class SubExpression extends SymbolExpression {

	public SubExpression(Expression left, Expression right) {
		super(left, right);
	}

	/**
	 * 求出left 和 right 表达式相减后的结果
	 * @param var
	 * @return
	 */
	@Override
	public int interpreter(HashMap<String, Integer> var) {
		return super.left.interpreter(var) - super.right.interpreter(var);
	}
}
/**
 * 环境
 */
public class Calculator {

	// 定义表达式
	private Expression expression;

	// 构造函数传参,并解析
	public Calculator(String expStr) { // expStr = a+b
		// 安排运算先后顺序
		Stack<Expression> stack = new Stack<>();
		// 表达式拆分成字符数组
		char[] charArray = expStr.toCharArray();// [a, +, b]

		Expression left = null;
		Expression right = null;
		//遍历我们的字符数组, 即遍历  [a, +, b]
		//针对不同的情况,做处理
		for (int i = 0; i < charArray.length; i++) {
			switch (charArray[i]) {
				case '+':
					// 从stack取出left => "a"
					left = stack.pop();
					// 取出右表达式 "b"
					right = new VarExpression(String.valueOf(charArray[++i]));
					// 然后根据得到 left 和 right 构建 AddExpresson 加入 stack 中
					stack.push(new AddExpression(left, right));
					break;
				case '-':
					left = stack.pop();
					right = new VarExpression(String.valueOf(charArray[++i]));
					stack.push(new SubExpression(left, right));
					break;
				default:
					//如果是一个 Var变量 就创建要给 VarExpression 对象,并push到 stack
					stack.push(new VarExpression(String.valueOf(charArray[i])));
					break;
			}
		}
		//当遍历完整个 charArray 数组后,stack 就得到最后Expression
		this.expression = stack.pop();
	}

	public int run(HashMap<String, Integer> var) {
		//最后将表达式a+b和 var = {a=10,b=20} 绑定
		//然后传递给expression的interpreter进行解释执行
		return this.expression.interpreter(var);
	}
}
/**
 * 测试
 */
public class Client {

	public static void main(String[] args) throws IOException {
		// a+b
		String expStr = getExpStr();
		HashMap<String, Integer> var = getValue(expStr);// var {a=10, b=20}
		Calculator calculator = new Calculator(expStr);
		System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
	}

	// 获得表达式
	public static String getExpStr() throws IOException {
		System.out.print("请输入表达式:");
		return (new BufferedReader(new InputStreamReader(System.in))).readLine();
	}

	// 获得值映射
	public static HashMap<String, Integer> getValue(String expStr) throws IOException {
		HashMap<String, Integer> map = new HashMap<>();

		for (char ch : expStr.toCharArray()) {
			if (ch != '+' && ch != '-') {
				if (!map.containsKey(String.valueOf(ch))) {
					System.out.print("请输入" + String.valueOf(ch) + "的值:");
					String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
					map.put(String.valueOf(ch), Integer.valueOf(in));
				}
			}
		}
		return map;
	}
}
/**
 * 运行结果:
 * 请输入表达式:a-b
 * 请输入a的值:30
 * 请输入b的值:10
 * 运算结果:a-b=20
 */

4、优缺点说明

1、优点:
  • 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
  • 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
2、缺点:
  • 解释器模式会引起类膨胀、解释器模式采用递归调用 方法,将会导致调试非常复杂、效率可能降低。

5、应用场景

1、当语言的文法较为简单,且执行效率不是关键问题时。
2、当问题重复出现,且可以用一种简单的语言来进行表达时。
3、当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。
4、总结:解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。

二、状态模式

1、概述

1、状态(state)模式:主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换。当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。

2、主要角色

1、环境类(Context):也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
2、抽象状态(State):定义一个接口,用于封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
3、具体状态(Concrete State):实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

3、状态模式实现

/**
 * 环境类
 */
public class Context {
    /**
     * state表示当前的状态,是变化的
     */
    State state = null;

    /**
     * 奖品数量
     */
    int count = 0;

    /**
     * 四个属性,表示四种状态
     */
    State noRafflleState = new NoRaffleState(this);
    State canRaffleState = new CanRaffleState(this);
    State dispenseState =   new DispenseState(this);
    State dispensOutState = new DispenseOutState(this);

    /**
     * 构造器
     * 初始化当前的状态为 NoRaffleState(即不能抽奖的状态)
     * 初始化奖品的数量
     * @param count
     */
    public Context(int count) {
        this.state = getNoRafflleState();
        this.count = count;
    }

    /**
     * 扣分, 调用当前状态的deductMoney
     */
    public void debuctMoney(){
        state.deductMoney();
    }

    /**
     * 抽奖
     */
    public void raffle(){
        //如果当前的状态是抽奖成功
        if(state.raffle()){
            //领取奖品
            state.dispensePrize();
        }
    }

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

    public int getCount() {
        //每领取一次奖品,count--
        int curCount = count;
        count --;
        return curCount;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public State getNoRafflleState() {
        return noRafflleState;
    }

    public void setNoRafflleState(State noRafflleState) {
        this.noRafflleState = noRafflleState;
    }

    public State getCanRaffleState() {
        return canRaffleState;
    }

    public void setCanRaffleState(State canRaffleState) {
        this.canRaffleState = canRaffleState;
    }

    public State getDispenseState() {
        return dispenseState;
    }

    public void setDispenseState(State dispenseState) {
        this.dispenseState = dispenseState;
    }

    public State getDispensOutState() {
        return dispensOutState;
    }

    public void setDispensOutState(State dispensOutState) {
        this.dispensOutState = dispensOutState;
    }
}
/**
 * 抽象状态
 */
public interface State {
    /**
     * 扣除积分操作
     */
    void deductMoney();

    /**
     * 是否抽中奖品
     * @return
     */
    boolean raffle();

    /**
     * 发放奖品
     */
    void dispensePrize();
}
/**
 * 具体状态,不能抽奖状态
 */
public class NoRaffleState implements State {
    private Context context;

    public NoRaffleState(Context context) {
        this.context = context;
    }

    @Override
    public void deductMoney() {
        //当前状态可以扣积分,扣除后,将状态设置成可以抽奖状态
        System.out.println("扣除50积分成功,您可以抽奖了");
        context.setState(context.getCanRaffleState());
    }

    @Override
    public boolean raffle() {
        System.out.println("扣了积分才能抽奖喔!");
        return false;
    }

    @Override
    public void dispensePrize() {
        System.out.println("不能发放奖品");
    }
}

/**
 * 可以抽奖的状态
 */
public class CanRaffleState implements State {
    private Context context;

    public CanRaffleState(Context context) {
        this.context = context;
    }

    @Override
    public void deductMoney() {
        System.out.println("已经扣取过了积分");
    }

    @Override
    public boolean raffle() {
        System.out.println("正在抽奖,请稍等!");
        Random r = new Random();
        int num = r.nextInt(10);
        // 10%中奖机会
        if(num == 0){
            // 改变活动状态为发放奖品
            context.setState(context.getDispenseState());
            return true;
        }else{
            System.out.println("很遗憾没有抽中奖品!");
            // 改变状态为不能抽奖
            context.setState(context.getNoRafflleState());
            return false;
        }
    }

    @Override
    public void dispensePrize() {
        System.out.println("没中奖,不能发放奖品");
    }
}

/**
 * 发放奖品的状态
 */
public class DispenseState implements State {
    private Context context;

    public DispenseState(Context context) {
        this.context = context;
    }

    @Override
    public void deductMoney() {
        System.out.println("不能扣除积分");
    }

    @Override
    public boolean raffle() {
        System.out.println("不能抽奖");
        return false;
    }

    /**
     * 发放奖品
     */
    @Override
    public void dispensePrize() {
        if(context.getCount() > 0){
            System.out.println("恭喜中奖了");
            // 改变状态为不能抽奖
            context.setState(context.getNoRafflleState());
        }else{
            System.out.println("很遗憾,奖品发送完了");
            // 改变状态为奖品发送完毕, 后面我们就不可以抽奖
            context.setState(context.getDispensOutState());
        }
    }
}

/**
 * 奖品发放完毕状态
 * 说明,当我们context改变成DispenseOutState,抽奖活动结束
 */
public class DispenseOutState implements State {
    private Context context;

    public DispenseOutState(Context context) {
        this.context = context;
    }

    @Override
    public void deductMoney() {
        System.out.println("奖品发送完了,请下次再参加");
    }

    @Override
    public boolean raffle() {
        System.out.println("奖品发送完了,请下次再参加");
        return false;
    }

    @Override
    public void dispensePrize() {
        System.out.println("奖品发送完了,请下次再参加");
    }
}
/**
 * 测试
 */
public class Client {
    public static void main(String[] args) {
        //创建活动对象,奖品有1个奖品
        Context activity = new Context(1);
        // 我们连续抽300次奖
        for (int i = 0; i < 6; i++) {
            System.out.println("--------第" + (i + 1) + "次抽奖----------");
            // 参加抽奖,第一步点击扣除积分
            activity.debuctMoney();
            // 第二步抽奖
            activity.raffle();
        }
    }
}
/**
 * 运行结果:
 * --------第1次抽奖----------
 * 扣除50积分成功,您可以抽奖了
 * 正在抽奖,请稍等!
 * 很遗憾没有抽中奖品!
 * --------第2次抽奖----------
 * 扣除50积分成功,您可以抽奖了
 * 正在抽奖,请稍等!
 * 很遗憾没有抽中奖品!
 * --------第3次抽奖----------
 * 扣除50积分成功,您可以抽奖了
 * 正在抽奖,请稍等!
 * 恭喜中奖了
 * --------第4次抽奖----------
 * 扣除50积分成功,您可以抽奖了
 * 正在抽奖,请稍等!
 * 很遗憾没有抽中奖品!
 * --------第5次抽奖----------
 * 扣除50积分成功,您可以抽奖了
 * 正在抽奖,请稍等!
 * 很遗憾,奖品发送完了
 * --------第6次抽奖----------
 * 奖品发送完了,请下次再参加
 * 奖品发送完了,请下次再参加
 */

4、优缺点说明

1、优点:
  • 结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
  • 将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
  • 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
2、缺点:
  • 状态模式的使用必然会增加系统的类与对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
  • 状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。

5、应用场景

1、当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
2、一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。

三、策略模式

1、概述

1、策略(Strategy)模式:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

2、主要角色

1、抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
2、具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
3、环境(Context)类:持有一个策略类的引用,最终给客户端调用。

3、策略链模式实现

/**
 * 抽象策略
 */
public interface Strategy {
    /**
     * 策略方法
     */
    void fly();
}
/**
 * 具体策略
 */
public class GoodFly implements Strategy {
    @Override
    public void fly() {
        System.out.println("飞翔技术十分高超的!!!");
    }
}

public class BadFly implements Strategy {
    @Override
    public void fly() {
        System.out.println("飞翔技术一般!!!");
    }
}
/**
 * 环境类
 */
public class Context {
    private Strategy strategy;

    public Strategy getStrategy() {
        return strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void strategyMethod() {
        strategy.fly();
    }
}
/**
 * 测试
 */
public class Client {
    public static void main(String[] args) {
        Context c = new Context();
        Strategy s = new GoodFly();
        c.setStrategy(s);
        c.strategyMethod();
        System.out.println("-----------------");
        s = new BadFly();
        c.setStrategy(s);
        c.strategyMethod();
    }
}

/**
 * 运行结果:
 * 飞翔技术十分高超的!!!
 * -----------------
 * 飞翔技术一般!!!
 */

4、优缺点说明

1、优点:
  • 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如if…else 语句、switch…case语句。
  • 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
  • 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
  • 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
  • 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
2、缺点:
  • 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
  • 策略模式造成很多的策略类,增加维护难度。

5、应用场景

1、一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
2、一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
3、系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
4、系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
5、多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。

四、职责链模式

1、概述

1、职责链(Chain of Responsibility)模式:又叫责任链模式,为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

2、主要角色

1、抽象处理者(Handler):定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
2、具体处理者(Concrete Handler):实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
3、请求(request):含有很多属性,表示一个请求。

3、职责链模式实现

/**
 * 请求类
 */
@Getter
@AllArgsConstructor
public class PurchaseRequest {
    /**
     * 金额
     */
    private float price = 0.0f;
}
/**
 * 抽象处理者
 */
@Getter
public abstract class Handler {
    /**
     * 下一个处理者
     */
    private Handler next;

    /**
     * 名字
     */
    private String name;

    public Handler(String name) {
        this.name = name;
    }

    public void setNext(Handler next) {
        this.next = next;
    }

    /**
     * 处理请求的方法
     * @param purchaseRequest
     */
    public abstract void handleRequest(PurchaseRequest purchaseRequest);
}
/**
 * 具体处理者1
 */
public class ConcreteHandler1 extends Handler {
    public ConcreteHandler1(String name) {
        super(name);
    }

    @Override
    public void handleRequest(PurchaseRequest purchaseRequest) {
        if (purchaseRequest.getPrice() <= 5000) {
            System.out.println(this.getName() + "能处理5000元以下的审批!!!");
        } else {
            this.getNext().handleRequest(purchaseRequest);
        }
    }
}

/**
 * 具体处理者2
 */
public class ConcreteHandler2 extends Handler {
    public ConcreteHandler2(String name) {
        super(name);
    }

    @Override
    public void handleRequest(PurchaseRequest purchaseRequest) {
        if (5000 < purchaseRequest.getPrice() && purchaseRequest.getPrice() <= 10000) {
            System.out.println(this.getName() + "能处理5000 至 10000元的审批!!!");
        } else {
            this.getNext().handleRequest(purchaseRequest);
        }
    }
}

/**
 * 具体处理者3
 */
public class ConcreteHandler3 extends Handler {
    public ConcreteHandler3(String name) {
        super(name);
    }

    @Override
    public void handleRequest(PurchaseRequest purchaseRequest) {
        if (10000 < purchaseRequest.getPrice()) {
            System.out.println(this.getName() + "能处理10000元以上的审批!!!");
        } else {
            this.getNext().handleRequest(purchaseRequest);
        }
    }
}
/**
 * 测试
 */
public class Client {
    public static void main(String[] args) {
        //创建一个请求对象
        PurchaseRequest purchaseRequest = new PurchaseRequest(19000);
        //创建审批人
        ConcreteHandler1 c1 = new ConcreteHandler1("年级主任");
        ConcreteHandler2 c2 = new ConcreteHandler2("教导处主任");
        ConcreteHandler3 c3 = new ConcreteHandler3("副校长");
        //将各个审批人的下一处理人设置,并且形成一个闭环
        c1.setNext(c2);
        c2.setNext(c3);
        c3.setNext(c1);
        //执行审批
        c1.handleRequest(purchaseRequest);
    }
}

/**
 * 运行结果:
 * 副校长能处理10000元以上的审批!!!
 */

4、优缺点说明

1、优点:
  • 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
  • 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
  • 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
  • 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
  • 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
2、缺点:
  • 不能保证每个请求一定被处理。假如一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

5、应用场景

1、多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
2、可动态指定一组对象处理请求,或添加新的处理者。
3、需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值