`
jgsj
  • 浏览: 1003778 次
文章分类
社区版块
存档分类
最新评论

设计模式笔记10-状态模式

阅读更多

设计模式笔记10-状态模式


1 引言


基本常识:策略模式和状态模式是双胞胎,在出生时才分开。你已经知道了,策略模式是围绕可以互换的算法来创建成功业务的。然而,状态走的是更崇高的路,他通过改变对象内部的状态来帮助对象控制自己的行为。

2 正文


2.1 本章业务背景


本章的业务背景是一个糖果机,它有四种状态:没有25分钱、有25分钱、售出糖果、糖果售罄,可以对糖果机做出的操作有三种:投入25分钱、退回25分钱、转动曲柄、发放糖果,前三种是客户对机器做出的操作,最后一种是糖果机内部的操作。

2.2 以操作为对象的设计


现在我们以操作为对象,根据当前的状态命令糖果机作出下一步动作。
下面给出投入25分钱的方法代码:

	public void insertQuarter() {
		if (state == HAS_QUARTER) {
			System.out.println("You can't insert another quarter");
		} else if (state == NO_QUARTER) {
			state = HAS_QUARTER;
			System.out.println("You inserted a quarter");
		} else if (state == SOLD_OUT) {
			System.out.println("You can't insert a quarter, the machine is sold out");
		} else if (state == SOLD) {
        	System.out.println("Please wait, we're already giving you a gumball");
		}
	}

该来的躲不掉,变更请求。
增加需求:当曲柄被转动时,有10%的几率掉下来两颗糖果。

需求变更导致我们上述的设计暴露出一些弊端:
1、这份代码没有遵守开放-关闭的原则。
2、状态转换被埋藏在条件语句中,所以并不明显。
3、我们还没有把会改变的那部分包装起来。
4、未来加入的代码很有可能导致Bug。

2.3 新的设计


我们的计划是这样的:不要维护我们现有的代码,我们重写它以便于将状态对象封装在各自的类中,然后在动作发生时委托给当前状态。

我们在这里遵照我们的设计原则,所以最后应该得到一个容易维护的设计。我们要做的事情是:
1、首先,我们定义一个state接口。在这个接口内,糖果机的每个动作都有一个对象的方法。
2、然后为机器中的每个状态实现状态类。这些类将负责在对应的状态下进行机器的行为。
3、最后,我们要摆脱旧的条件代码,取而代之的方式是,将动作委托到状态类。

你将会看到,我们不仅遵守了设计原则,实际上我们还实现了状态模式。在重新完成代码之后我们再来了解状态模式的正式定义。

State接口

public interface State {
 
	public void insertQuarter();
	public void ejectQuarter();
	public void turnCrank();
	public void dispense();
}

实现我们的状态类

public class NoQuarterState implements State {
    GumballMachine gumballMachine;
 
    /*
     * 1 首先我们要实现State接口
     * 2 我们通过构造器得到糖果机的引用,然后记录在实例变量中
     * 3 以下分别是四种操作对应的处理方法
     */
    public NoQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
 
	public void insertQuarter() {
		System.out.println("You inserted a quarter");
		gumballMachine.setState(gumballMachine.getHasQuarterState());
	}
 
	public void ejectQuarter() {
		System.out.println("You haven't inserted a quarter");
	}
 
	public void turnCrank() {
		System.out.println("You turned, but there's no quarter");
	 }
 
	public void dispense() {
		System.out.println("You need to pay first");
	} 
 
	public String toString() {
		return "waiting for quarter";
	}
}

完整的糖果机类

public class GumballMachine {
	
	//所有的状态都在这里
	//以及实例变量
	State soldOutState;
	State noQuarterState;
	State hasQuarterState;
	State soldState;
	State winnerState;
 
	State state = soldOutState;
	int count = 0;
 
	//构造器取得糖果的初始数目并把它存放在一个实例变量中
	//每一种状态也都创建一个状态实例
	public GumballMachine(int numberGumballs) {
		soldOutState = new SoldOutState(this);
		noQuarterState = new NoQuarterState(this);
		hasQuarterState = new HasQuarterState(this);
		soldState = new SoldState(this);
		winnerState = new WinnerState(this);

		//如果超过0颗糖果,我们就把状态设为NoQuaterState
		this.count = numberGumballs;
 		if (numberGumballs > 0) {
			state = noQuarterState;
		} 
	}
 
	/*
	 * 1 现在这些动作变得容易实现了。我们只是委托到当前状态
	 * 2 请注意,我们不需要再GumballMachine中转杯一个dispense()的动作方法,
	 *   因为这是一个内部动作;用户不可以直接要求机器发放糖果。但我们再状态对象的
	 *   turnCrank()方法中调用dispense()方法的
	 */
	public void insertQuarter() {
		state.insertQuarter();
	}
 
	public void ejectQuarter() {
		state.ejectQuarter();
	}
 
	public void turnCrank() {
		state.turnCrank();
		state.dispense();
	}

	//这个方法 允许其他的对象(像我们的状态对象)将极其的状态转换到不同的状态
	void setState(State state) {
		this.state = state;
	}
 
	//这个极其提供了一个releaseBall()的辅助方法来释放出糖果,并将count实例变量的值减1
	void releaseBall() {
		System.out.println("A gumball comes rolling out the slot...");
		if (count != 0) {
			count = count - 1;
		}
	}
 
	//下面是一系列的get方法
	int getCount() {
		return count;
	}
 
	void refill(int count) {
		this.count = count;
		state = noQuarterState;
	}

    public State getState() {
        return state;
    }

    public State getSoldOutState() {
        return soldOutState;
    }

    public State getNoQuarterState() {
        return noQuarterState;
    }

    public State getHasQuarterState() {
        return hasQuarterState;
    }

    public State getSoldState() {
        return soldState;
    }

    public State getWinnerState() {
        return winnerState;
    }
 
	public String toString() {
		StringBuffer result = new StringBuffer();
		result.append("\nMighty Gumball, Inc.");
		result.append("\nJava-enabled Standing Gumball Model #2004");
		result.append("\nInventory: " + count + " gumball");
		if (count != 1) {
			result.append("s");
		}
		result.append("\n");
		result.append("Machine is " + state + "\n");
		return result.toString();
	}
}

2.4 定义状态模式


状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
这个描述中的第一部分附有相当多的涵义,是吧?因为这个模式将状态封装成为独立的类,并将动作委托到代表当前状态的对象,我们知道行为会随着内部状态而改变。糖果机提供了一个很好的例子:当糖果机是在不同状态时,你投入25分钱,就会得到不同的行为。
而这个定义的第二部分呢?一个对象“看起来好像修改了它的类”是什么意思呢?从客户的视角来看:如果说你使用的对象能够完全改变它的行为,那么你会觉得,这个对象实际上是从别的类实例化而来的。然后,实际上,你知道我们是在使用组合通过简单引用不同的状态对象来造成类改变的假象。

一般来说,我们把策略模式想成是除了继承之外的一种弹性替代方案。如果你使用继承定义了一个类的行为,你将被这个行为困住,甚至修改它很难。有了策略模式,你可以通过组合不同的对象改变行为。
我们把状态模式想成是不用在context中放置许多条件判断的替代方案。通过将行为包装进状态对象中,你可以通过在context内简单地改变状态对象来改变context的行为。

2.5 解决幸运糖果的问题


首先,我们增加一个状态,并且在转动曲柄是做一个随机数判断,根据结果决定是否额外多发放一个糖果。

我们为什么要增加一个新的状态?为什么不直接在SoldState中发放两颗糖果?
这是一个好问题。这两个状态几乎一样,唯一的差别在于,WinnerState状态会发放两颗糖果。你当然可以将发放两颗糖果的代码放在SoldState中,当然这么做也有缺点,因为你等于时将两个状态用一个状态类来代表。这样做你牺牲了状态类的清晰易懂来减少一些代码冗余。你也应该考虑到在前面的章节所学到的原则:一个类,一个责任。将WinnerState状态的责任放进SoldState中,你等于是让SoldState状态具有两个责任。那么促销方案结束后或者赢家的几率改变以后,你又该怎么办呢?所以,这必须用的只会来做折中。

3 本章总结

本章要点:
1、状态模式允许一个对象基于内部状态而拥有不同的行为
2、和程序状态机(PSM)不同,状态模式用类代表状态
3、Context会将行为委托给当前状态对象
4、通过将每个状态封装进一个类,我们把以后需要做的任何改变局部化了
5、状态模式和策略模式有相同的类图,但是它们意图不同
6、策略模式通常会用行为或算法来配置Context类
7、状态模式允许Context随着状态的改变而改变行为
8、状态转换可以由State类或Context类控制
9、使用状态模式通常会导致设计类的数目大量增加
10、状态类可以被多个Context实例共享

状态模式,根据字面意思其实很好理解。我们要处理的业务是一个流程,流程是由两部分组成:状态和动作,动作可以根据当前的状态(或者其他上下文)判断从而命令实例改变行为。缺陷管理流程大家应该都很熟悉,测试人员new一个bug report给测试经理,再由测试经理转给开发经理,开发经理根据模块分配给相关的开发人员,开发人员修改完成后转给组内人员进行校验,校验成功后转给开发经理,开发经理转给测试经理,测试经理转给测试人员,回归通过,问题单关闭。上述只列出了正常流程,每个状态都可以因为一些上下文原因将流程回退,比如测试经理发现bug report是个非问题,则将bug report走回,等等,这个流程相比本章的糖果机可能要复杂一点,但确实是一个很好的锻炼机会,有兴趣地朋友可以实现以下噢。







分享到:
评论

相关推荐

    设计模式学习笔记--Flyweight享元模式.docx

    享元模式是一种设计模式,属于构造型模式,其主要目的是减少对象的数量,通过共享大量相似对象的内部状态来节省内存。这种模式在处理大量细粒度对象时特别有用,能够有效地提高系统的性能。 享元模式的核心是...

    HeadFirst 设计模式学习笔记2--观察者模式 demo

    HeadFirst的设计模式系列书籍以其直观易懂的方式深受程序员喜爱,本笔记将深入探讨观察者模式的概念及其应用。 观察者模式的核心思想是定义对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的...

    Java 版设计模式学习笔记-java-design-patterns.zip

    这个“Java版设计模式学习笔记”涵盖了多种设计模式,旨在帮助开发者更好地理解和应用这些模式。让我们深入探讨一下其中可能包含的关键知识点。 一、单例模式 单例模式确保一个类只有一个实例,并提供一个全局访问...

    韩顺平_Java设计模式笔记.docx

    ### 韩顺平_Java设计模式笔记知识点详解 #### 1. Java设计模式内容介绍 ##### 1.1.1 先看几个经典的面试题 - **原型设计模式问题** - **UML类图**:原型模式的核心在于定义了一个`Prototype`接口,该接口声明了...

    23个设计模式图解--学习笔记

    在《23个设计模式图解--学习笔记》中,我们探讨了这些模式,以便于理解和应用到实际开发中。以下是这23个设计模式的详细说明: 1. **工厂方法**(Factory Method):定义一个用于创建对象的接口,让子类决定实例化...

    设计模式笔记

    设计模式是软件工程中的一种重要概念,它代表了在特定情境下解决问题的...设计模式笔记中的内容应该涵盖了以上所述的各种模式,通过深入学习和实践,你可以将这些模式应用到实际项目中,提升自己的编程技能和设计能力。

    Head First设计模式读书笔记-DesignPatterns.zip

    8. **状态模式**:行为型设计模式,允许对象在其内部状态改变时改变其行为。对象看起来似乎修改了它的类。常见于游戏中的角色状态切换或者设备的不同工作模式。 9. **职责链模式**:行为型设计模式,将请求的发送者...

    尚硅谷设计模式源码笔记课件.zip

    行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式) 2) 学习目标:通过学习,学员...

    设计模式整理代码-pattern.zip

    - 状态模式(State):允许对象在其内部状态改变时改变它的行为,看起来像改变了它的类。 - 策略模式(Strategy):定义一系列算法,并将每个算法封装起来,使它们可以相互替换,策略对象改变算法的选用。 - 模板...

    设计模式学习笔记总结

    这里我们聚焦于C#语言中的设计模式学习笔记,涵盖了多种经典的设计模式,如合成模式、桥梁模式、装饰模式、享元模式、门面模式、命令模式、工厂方法、策略模式、代理模式以及状态模式。下面将对这些模式逐一进行详细...

    根据《JAVA与设计模式》整理的笔记及示例代码

    这份"根据《JAVA与设计模式》整理的笔记及示例代码"涵盖了Java语言和设计模式的核心概念,旨在帮助开发者理解和应用这些模式。 一、设计模式的基本概念 设计模式是对在特定情境下软件设计问题的解决方案的一种描述...

    设计模式学笔记

    ### 设计模式学习笔记 #### 一、设计模式概述 设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。设计...

    设计模式读书笔记

    在本文档中,我们主要探讨了设计模式的基本概念和分类,以及“状态机模式”这一特定的设计模式。 首先,设计模式可以分为三大类:创建型、结构型和行为型。创建型设计模式关注的是对象的创建,如工厂方法、抽象工厂...

    23种设计模式---大牛笔记[汇编].pdf

    以及行为型中的策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式和解释器模式。 设计模式的实施遵循六项核心原则: 1. **开闭原则**:软件...

    新版设计模式手册-笔记

    《新版设计模式手册》中的笔记通过21个设计模式的概念图,直观地展示了这些模式的结构和交互方式,帮助读者形成清晰的认识。同时,生活类比将复杂的编程概念与日常经验相结合,使学习更加生动有趣。例如,单例模式...

    设计模式之美—学习笔记

    在这个“设计模式之美”的学习笔记中,我们将探讨一些主要的设计模式,以及它们在实际开发中的应用。 首先,我们从创建型模式开始。这类模式主要用于对象的创建,如单例模式(Singleton)、工厂模式(Factory ...

    JS设计模式笔记和代码

    本笔记和代码集合涵盖了多种重要的设计模式,旨在帮助开发者编写更可维护、可扩展和可重用的JavaScript代码。 1. **工厂模式**:这是一种创建型设计模式,它提供了一个创建对象的接口,但允许子类决定实例化哪一个...

    台湾人写的设计模式笔记

    这份由台湾作者编写的笔记,结合Java语言,为读者提供了一种深入理解并应用设计模式的方式。以下是对这些设计模式的详细解释: 1. **创建型模式(Creational Patterns)** 创建型模式关注于对象的创建过程,它们帮助...

    面向对象与设计模式基础笔记

    ### 面向对象与设计模式基础知识点梳理 #### 一、面向对象的基本概念 **面向对象编程(Object-Oriented Programming, OOP)** 是一种编程范式,其核心思想是将现实世界中的事物抽象成类(Class),并通过类创建...

Global site tag (gtag.js) - Google Analytics