行为型模式中<职责链模式、状态模式、观察者模式>

1、职责链模式 (Chain of Responsibility)
 
        现实生活中,经常会遇到一个请求有多个对象进行处理,但是每一个对象的处理条件或者权限不同,比如说公司请假的时候,批准的人有产品线经理、部门负责人、主管副总等,但是每一个领导的权限不同,能批准的假天数也不同,所以员工需要根据自己需要请假的天数去找不同的领导去处理。软件开发中也有这样的例子,比如异常处理中,处理程序根据异常的类型决定自己是否处理该异常,不是的话,向下一级抛出。
 
1.1职责链模式的定义与特点
 
        职责链模式是为了避免请求发送者与多个请求处理者偶合在一起,将所有的请求的处理者通过前一个对象几组其下一个对象的引用而形成的一条链;当有请求发生时,可以将请求沿着这一条链条进行传播,直到有对象处理它为止。
在职责链模式中,客户只需要将请求发送到职责链上即可,无需关心处理的细节和请求的传递过程,所以职责链将请求的发送者和处理者进行了解耦。增强了系统的扩展性。同时简化了对象之间的连接,每一个对象只需要保留一个指向其后继者的引用,不需要保持其他的所有处理者的引用,避免众多的条件分支。最后职责链模式使得每一个类只需要处理他自己的该处理的工作,完成了责任的分担。
但是责任联调模式也有缺陷:
首先不可能每一个请求都被处理,请求可能到职责链的末端都得不到处理;针对比较长的职责链,涉及多个对象,系统的性能会受影响。
 
1.2模式的结构与实现
 
(1)抽象处理者角色:定义一个处理者的接口,包含抽象处理方法和一个后继连接
(2)具体处理者角色:实现抽象处理者的处理方法,判断是否处理本次请求,如果可以处理则处理,否则将该请求转给它的后继者;
(3)客户类角色:创立职责链,并向链头的具体处理对象提交请求,他不关心处理的细节和请求的传递过程
 其类结构图和设置的职责链如下图所示:
 
 
示例代码如下:
复制代码
//抽象处理者角色
abstract class Handler {
    private Handler next;
    public void  setNext(Handler next){
        this.next = next;
    }
    public Handler getNext() {
        return next;
    }
    public abstract void handleRequeset(String quest);
}
//具体处理者角色1
class ConcreteHandler1 extends Handler {
    @Override
    public void handleRequeset(String request) {
        if(request.equals("one")) {
            System.out.println("具体处理者1负责处理该请求");
        } else {
            if(getNext() != null) {
                getNext().handleRequeset(request);
            } else {
                System.out.println("无人处理该请求");
            }
        }
    }
}
//具体处理者角色2
class ConcreteHandler2 extends Handler {
    @Override
    public void handleRequeset(String request) {
        if(request.equals("two")) {
            System.out.println("具体处理者2负责处理该请求");
        } else {
            if(getNext() != null) {
                getNext().handleRequeset(request);
            } else {
                System.out.println("无人处理该请求");
            }
        }
    }
}
//调用客户端
public class TestChainOfResponsibilityPattern {
    public static void main(String[] args) {
        Handler handler = new ConcreteHandler1();
        Handler handler1 = new ConcreteHandler2();
        handler.setNext(handler1);
        handler.handleRequeset("two");
    }
}
复制代码
模式应用的场景:
(1)多个对象处理同一个请求,那个对象处理该请求由运行时刻自动确定
(2)可以动态指定一组对象处理请求,活添加新的处理者
(3)在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
模式的扩展:(1)单纯的职责链模式:一个请求必须被某一个处理者对象所接受,且一个具体处理者对某个请求的处理只能采用两种行为之一,自己承担或者传递给下家处理;(2)不纯的职责链模式,允许出现某一个具体的处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接受端对象所接受。
 
2、状态模式(state)
 
        软件开发过程中,应用程序中某些对象可能会根据不同的情况做出不同的行为,我们把这种对象称作是有状态的对象。而影响对象行为的一个或者多个动态变化的属性称为状态,当有状态的对象和外界时间发生互动的时候,其内部状态也会发生变化,从而使其行为也随之发生变化。比如人的情绪有开心和悲伤,不同的情绪下会导致不同的行为。当然外界也会影响情绪变化。
        针对有状态的对象编程,传统的解决方式是将所有的发生的情况全部考虑到,然后if..else来做状态判断,当对象的状态很多时,程序会变得异常复杂,新增状态的情况下就要增加新的if判断,不利于程序扩展。
        
     上述问题采用状态模式就能很好的解决,状态模式的思想就是:当控制一个状态转换的条件表达式过于复杂,把相关的“判断逻辑”提取出来,放到一些列状态当中,这样可以把原来复杂的逻辑判断简单化。
 
2.1模式的定义和结构
 
        对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。主要优点:将与特定状态相关的行为局部化到一个状态中,并且分割开不同的状态,满足单一职责原则;同时减少对象间的相互依赖,有利于程序的扩展。缺点是:状态模式的使用必然会增加系统与独享的个数。且结构与状态的实现较为复杂。
 
 状态模式包含以下角色:
(1)环境角色context、也成为上下文,定义了客户感兴趣的接口,维护一个当前的状态,并将与状态相关的操作委托给当前状态对象来处理,
(2)抽象状态(state)角色,定义一个接口,用来封装环境对象中的特定状态对应的行为。
(3)具体状态实现抽象状态的行为。
 
状态模式的实现代码如下:
复制代码
//环境类:上下文
class ContextEnvir {
    private State state;
    //定义环境类的初始状态
    public ContextEnvir() {
        this.state = new ConcreteStateA();
    }
    //设置新的状态
    public State getState() {
        return state;
    }
    //读取状态
    public void setState(State state) {
        this.state = state;
    }
    //对请求做处理
    public void handle(){
        state.handle(this);
    }
}
//抽象状态类
abstract class State {
    public abstract  void handle(ContextEnvir context);
}
//具体状态类A
class ConcreteStateA extends State {
    @Override
    public void handle(ContextEnvir context) {
        System.out.println("当前状态是A");
        context.setState(new ConcreteStateB());
    }
}
//具体状态类B
class ConcreteStateB extends State {
    @Override
    public void handle(ContextEnvir context) {
        System.out.println("当前状态是B");
        context.setState(new ConcreteStateA());
    }
}
public class TestStatePattern {
    public static void main(String[] args) {
        ContextEnvir context = new ContextEnvir();//创建环境
        context.handle();
        context.handle();
        context.handle();
        context.handle();
        context.handle();
        context.handle();
    }
} 
复制代码
实例应用:
 
实例应用1:用状态模式实现多线程的状态转换程序。
 
线程存在五种状态:新建、就绪、运行、挂起、死亡。各个状态之间遇到相关方法调用或者时间触发的时候就会转移到其他的状态上,规律如下图所示:
定义一个抽象状态类ThreadState,然后为上图每一个状态设计一个具体状态类,每一个状态都有触发他们转变状态的方法,环境类(ThreadContext)中先生成一个初始状态(New新建状态),并且提供触发方法,下图为线程状态转换程序的结构图:
 
具体实现代码如下:
复制代码
//抽象状态类:线程状态
abstract class ThreadState
{
    protected String stateName; //状态名
}
//状态环境类, 线程状态管理类
class ThreadContext{
    private ThreadState state;
    ThreadContext(){
        state = new New();//初始状态
    }
 
    public void setState(ThreadState state) {
        this.state = state;
    }
 
    public ThreadState getState() {
        return state;
    }
 
    public void start(){
        ((New)state).start(this);
    }
    public void getCPU()
    {
        ((Runnable) state).getCPU(this);
    }
    public void suspend()
    {
        ((Running) state).suspend(this);
    }
    public void stop()
    {
        ((Running) state).stop(this);
    }
    public void resume()
    {
        ((Blocked) state).resume(this);
    }
}
//具体状态类:新建状态
class New extends ThreadState
{
    public New()
    {
        stateName="新建状态";
        System.out.println("当前线程处于:新建状态.");
    }
    public void start(ThreadContext threadContext)
    {
        System.out.print("调用start()方法-->");
        if(stateName.equals("新建状态"))
        {
            threadContext.setState(new Runnable());
        }
        else
        {
            System.out.println("当前线程不是新建状态,不能调用start()方法.");
        }
    }
}
//具体状态类:就绪状态
class Runnable extends ThreadState
{
    public Runnable()
    {
        stateName="就绪状态";
        System.out.println("当前线程处于:就绪状态.");
    }
    public void getCPU(ThreadContext threadContext)
    {
        System.out.print("获得CPU时间-->");
        if(stateName.equals("就绪状态"))
        {
            threadContext.setState(new Running());
        }
        else
        {
            System.out.println("当前线程不是就绪状态,不能获取CPU.");
        }
    }
}
//具体状态类:运行状态
class Running extends ThreadState
{
    public Running()
    {
        stateName="运行状态";
        System.out.println("当前线程处于:运行状态.");
    }
    public void suspend(ThreadContext threadContext)
    {
        System.out.print("调用suspend()方法-->");
        if(stateName.equals("运行状态"))
        {
            threadContext.setState(new Blocked());
        }
        else
        {
            System.out.println("当前线程不是运行状态,不能调用suspend()方法.");
        }
    }
    public void stop(ThreadContext threadContext)
    {
        System.out.print("调用stop()方法-->");
        if(stateName.equals("运行状态"))
        {
            threadContext.setState(new Dead());
        }
        else
        {
            System.out.println("当前线程不是运行状态,不能调用stop()方法.");
        }
    }
}
//具体状态类:阻塞状态
class Blocked extends ThreadState
{
    public Blocked()
    {
        stateName="阻塞状态";
        System.out.println("当前线程处于:阻塞状态.");
    }
    public void resume(ThreadContext threadContext)
    {
        System.out.print("调用resume()方法-->");
        if(stateName.equals("阻塞状态"))
        {
            threadContext.setState(new Runnable());
        }
        else
        {
            System.out.println("当前线程不是阻塞状态,不能调用resume()方法.");
        }
    }
}
//具体状态类:死亡状态
class Dead extends ThreadState
{
    public Dead()
    {
        stateName="死亡状态";
        System.out.println("当前线程处于:死亡状态.");
    }
}
public class TestThreadStatePattern {
    public static void main(String[] args) {
        ThreadContext context=new ThreadContext();
        //线程依次进行的状态是:新建,就绪,运行,阻塞,就绪,运行,停止。
        context.start();
        context.getCPU();
        context.suspend();
        context.resume();
        context.getCPU();
        context.stop();
    }
}
View Code 
复制代码
实例应用2,假设泡杯茶的步骤是:1. 拿一个空茶杯 2. 往杯子中添加茶叶 3. 往杯子中加开水,
 这个场景下杯子就有三个状态: 1. 空杯子 2. 装热茶的杯子 3. 装水的杯子
1
2
3
4
final static int NO_CUP = 0                    // 还没有杯子的状态
final static int EMPTY_CUP = 1                 // 空杯
final static int FULL_TEA_CUP = 2        // 装有茶叶的杯子
final static int ENJOY_TEA_CUP = 3      // 泡好茶的杯子
实例代码如下:
复制代码
//抽象状态接口
interface  TeaState {
    //1. 拿杯子
    void takeCup();
    // 2. 往杯子中添加茶叶
    void addTea();
    //3. 往杯子中加开水
    void addHotWater();
    //4. 热茶备就
    void enjoyTea();
}
//状态管理类,维护茶的状态
class DrinkTeaState {
    //这杯茶的状态
    public TeaState state;
    //没有杯子状态
    public NoCupState noCupState;
    //有一个空杯子状态
    public EmptyCupState emptyCupState;
    //杯子里有速溶咖啡粉状态
    public FullTeaCupState fullTeaCupState;
    // 冲好咖啡状态
    public EnjoyTeaState enjoyTeaState;
    public DrinkTeaState() {
        this.noCupState = new NoCupState(this);
        this.emptyCupState = new EmptyCupState(this);
        this.fullTeaCupState = new FullTeaCupState(this);
        this.enjoyTeaState = new EnjoyTeaState(this);
        // 设置默认状态
        this.state = this.noCupState;
    }
    // 拿杯子
    public void takeCup() {
        state.takeCup();
    }
    // 加入茶叶
    public void addInstantCoffe() {
        state.addTea();
    }
    // 加开水
    public void addWater() {
        state.addHotWater();
    }
    //完成
    public void enjoyTea() {
        state.enjoyTea();
    }
}
//没有杯子的状态
class NoCupState implements TeaState {
    private  DrinkTeaState drinkTeaState;
    public NoCupState (DrinkTeaState drinkTeaState) {
        this.drinkTeaState = drinkTeaState;
    }
    @Override
    public void takeCup() {
        drinkTeaState.state = drinkTeaState.emptyCupState;
        System.out.println("拿起一个空杯子");
    }
    @Override
    public void addTea() {
        System.out.println("还没有一个杯子, 需要拿个杯子");//没杯子
    }
    @Override
    public void addHotWater() {
        System.out.println("还没有一个杯子, 需要拿个杯子");//没杯子
    }
    @Override
    public void enjoyTea() {
        System.out.println("还没有一个杯子, 需要拿个杯子");//没杯子
    }
}
//空杯子状态
class EmptyCupState implements TeaState {
    private  DrinkTeaState drinkTeaState;
    public EmptyCupState(DrinkTeaState drinkTeaState) {
        this.drinkTeaState = drinkTeaState;
    }
    @Override
    public void takeCup() {
        System.out.println("已经有个空杯子了, 不需要再拿杯子了");
    }
    @Override
    public void addTea() {
        this.drinkTeaState.state = drinkTeaState.emptyCupState;
        System.out.println("空杯子中加入茶叶");
    }
    @Override
    public void addHotWater() {
        System.out.println("仍需要加入茶叶");
    }
    @Override
    public void enjoyTea() {
        System.out.println("现在还是空杯子");
    }
}
//装有茶叶的茶杯
class FullTeaCupState implements TeaState {
    private DrinkTeaState drinkTeaState;
    public FullTeaCupState(DrinkTeaState drinkTeaState) {
        this.drinkTeaState = drinkTeaState;
    }
    @Override
    public void takeCup() {
        System.out.println("已经有茶叶了,不需要拿空杯子了");
    }
    @Override
    public void addTea() {
        System.out.println("已经有茶叶了,不需要拿茶叶了");
    }
    @Override
    public void addHotWater() {
        this.drinkTeaState.state = drinkTeaState.fullTeaCupState;
    }
    @Override
    public void enjoyTea() {
        System.out.println("没加热水了");
    }
}
//享受热茶的状态
class EnjoyTeaState implements TeaState {
    private DrinkTeaState drinkTeaState;
    public EnjoyTeaState(DrinkTeaState drinkTeaState) {
        this.drinkTeaState = drinkTeaState;
    }
    @Override
    public void takeCup() {
        System.out.println("茶泡好了,不需要拿空杯子了");
    }
    @Override
    public void addTea() {
        System.out.println("茶泡好,别加茶叶了");
    }
    @Override
    public void addHotWater() {
        System.out.println("热茶备就,不需要添加茶叶了");
    }
    @Override
    public void enjoyTea() {
        this.drinkTeaState.state = drinkTeaState.enjoyTeaState;
        System.out.println("享受热茶");
    }
}
public class TestStatePattern4DrinkingTea {
    public static void main(String[] args) {
        DrinkTeaState drinkTeaState = new DrinkTeaState();
        drinkTeaState.takeCup();
        drinkTeaState.addInstantCoffe();
        drinkTeaState.addWater();
        drinkTeaState.enjoyTea();
    }
}
View Code
复制代码
上述代码写的不太好,各种具体的状态都实现了四个步骤,太冗余,尝试进行重构一下,让每一个状态类只做自己应该做的操作,比如空杯子状态下只有一个操作就是去拿被子takeCup(),整个泡茶的操作流程让状态的环境类DrinkState去管理,抽象的状态类只有一个茶杯的状态即可。 
实现代码如下:
复制代码
//抽象状态接口
enum CUP_STATE {NO_CUP, EMPTY_CUP, FULL_TEA_CUP, ENJOY_TEA_CUP};
abstract class TeaState {
    protected CUP_STATE cupState;
}
//状态环境类
class DrinkTeaState {
    private TeaState state;
    DrinkTeaState() {
        state = new NoCupState();//没有杯子的状态
    }
    public void setState(TeaState teaState){
        this.state = teaState;
    }
    public TeaState getState() {
        return state;
    }
    public void  takeCup(){
        ((NoCupState)state).takeCup(this);
    }
    public void addTea() {
        ((EmptyCupState)state).addTea(this);
    }
    public void addHotWater() {
        ((FullTeaCupState)state).addHotWater(this);
    }
    public void enjoyTea() {
        ((EnjoyTeaState)state).enjoyTea(this);
    }
}
//空杯子状态
class NoCupState extends TeaState {
    public NoCupState(){
        cupState = CUP_STATE.NO_CUP;
    }
    public void takeCup(DrinkTeaState drinkTeaState) {
        System.out.println("拿起一个空杯子吧");
        if(cupState == CUP_STATE.NO_CUP) {
            drinkTeaState.setState(new EmptyCupState());
        } else {
            System.out.println("泡茶你得先拿杯子");
        }
    }
}
class EmptyCupState extends TeaState {
    public EmptyCupState() {
        cupState = CUP_STATE.EMPTY_CUP;
    }
    public void addTea(DrinkTeaState drinkTeaState) {
        System.out.println("杯子中加入茶叶");
        if (cupState == CUP_STATE.EMPTY_CUP) {
            drinkTeaState.setState(new FullTeaCupState());
        } else {
            System.out.println("空杯子放茶叶");
        }
    }
}
class FullTeaCupState extends TeaState {
    public FullTeaCupState() {
        cupState = CUP_STATE.FULL_TEA_CUP;
    }
    public void addHotWater(DrinkTeaState drinkTeaState) {
        System.out.println("泡茶得加热水");
        if (cupState == CUP_STATE.FULL_TEA_CUP) {
            drinkTeaState.setState(new EnjoyTeaState());
        } else {
            System.out.println("热水");
        }
    }
}
class EnjoyTeaState extends TeaState {
        public EnjoyTeaState() {
            cupState = CUP_STATE.ENJOY_TEA_CUP;
        }
        public void enjoyTea(DrinkTeaState drinkTeaState) {
            System.out.println("热茶备就");
        }
}
public class TestStatePattern4DrinkingTea {
    public static void main(String[] args) {
        DrinkTeaState drinkTeaState = new DrinkTeaState();
        drinkTeaState.takeCup();
        drinkTeaState.addTea();
        drinkTeaState.addHotWater();
        drinkTeaState.enjoyTea();
    }
}
View Code
复制代码
 
状态模式可以将不同的变化以状态类的形式分开封装,以 要是某个状态中的操作改了,找到对应类修改即可。或者以后添加某个状态的话,直接添加状态,修改部分状态中的代码,不需要增加if--else,循环判断。但是缺点也很明显,这样做增加了很多的状态类,造成了代码膨胀。
 
2.2 状态模式的应用
 
(1)当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式
(2)一个操作中含有庞大的分支结构的时候,并且这些分支决定对象的状态时。
状态模式模式的扩展:
有些状态下,可能有多个环境对象需要共享一组状态,这时候需要引用享元模式,将这些具体的状态对象放到集合中供程序分享,其结构如下图所示:
分析:共享状态模式下的不同之处是在环境内种增加了一个HashMap来保存相关的状态,当需要某种状态的时候,就可以从中获取,其代码程序如下:
 
复制代码
//环境类
class ShareContext
{
    private ShareState state;
    private HashMap<String, ShareState> stateSet=new HashMap<String, ShareState>();
    public ShareContext()
    {
        state=new ConcreteState1();
        stateSet.put("1", state);
        state=new ConcreteState2();
        stateSet.put("2", state);
        state=getState("1");
    }
    //设置新状态
    public void setState(ShareState state)
    {
        this.state=state;
    }
    //读取状态
    public ShareState getState(String key)
    {
        ShareState s=(ShareState)stateSet.get(key);
        return s;
    }
    //对请求做处理
    public void Handle()
    {
        state.Handle(this);
    }
}
//抽象状态类
abstract class ShareState
{
    public abstract void Handle(ShareContext context);
}
//具体状态1类
class ConcreteState1 extends ShareState
{
    public void Handle(ShareContext context)
    {
        System.out.println("当前状态是: 状态1");
        context.setState(context.getState("2"));
    }
}
//具体状态2类
class ConcreteState2 extends ShareState
{
    public void Handle(ShareContext context)
    {
        System.out.println("当前状态是: 状态2");
        context.setState(context.getState("1"));
    }
}
public class FlyweightStatePattern
{
    public static void main(String[] args)
    {
        ShareContext context=new ShareContext(); //创建环境       
        context.Handle(); //处理请求
        context.Handle();
        context.Handle();
        context.Handle();
    }
}
复制代码
 
3、观察者模式(Observer)
 
        现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个对象行为也发生改变,比如某些商品价格的上涨会使得一部分人高兴,使另外一部分人伤心。软件开发也是如此,比如Excel中数据与折线图,饼状图,柱状图的关系、MVC模式中的模型与视图的关系。
模式的定义与特点:
        观察者模式指的是多个对象建存在的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。这种模式又称为发布订阅模式,它是对象行为型模式。主要优点:(1)降低目标与观察者之间的耦合,建立一种出发的方式。但是这种耦合并没有完全解除,同时观察者很多的情况下,通知的发布会花费很多时间,影响效率。
       
3.1 模式的结构如下:
 
        (1)抽象主题角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法;
        (2)具体主题角色Concrete Subject:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生变化时候,通知所有注册过的观察者对象;
        (3)抽象观察者Observer角色:抽象类或者接口,包含一个更新自己的抽象方法,当接到具体主题的更改通知时候被调用;
        (4) 具体观察者角色Consrete Observer角色,实现抽象观察者中定义的抽象方法,以便在得到目标的更改时候更新自身的状态。
 
观察者模式的结构图如下:
实现的代码如下:
 
复制代码
//抽象目标
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();
}
//具体目标1
class ConcreteSubject extends Subject{
    @Override
    public void notifyObserver() {
        System.out.println("具体目标发生改变,请注意!");
        System.out.println("---------------");
        for (Object obj:observers){
            ((Observer)obj).response();
        }
    }
}
//抽象观察者
interface Observer{
    public void response();//反应
}
//具体观察着1
class ConcrteObserver1 implements Observer{
    @Override
    public void response() {
        System.out.println("观察者1号做出应答");
    }
}
//具体观察着2
class ConcrteObserver2 implements Observer{
    @Override
    public void response() {
        System.out.println("观察者2号做出应答");
    }
}
public class TestObserverPattern {
    public static void main(String[] args) {
        Subject subject = new ConcreteSubject();
        Observer observer1 = new ConcrteObserver1();
        Observer observer2 = new ConcrteObserver2();
        subject.add(observer1);
        subject.add(observer2);
        subject.notifyObserver();
    }
} 
复制代码
 
3.2 模式的应用实例
 
利用观察者模式设计一个程序,分析人民币汇率的升值或者贬值对进口公司的产品成本或者出口公司的出口产品的收入以及公司利润率的影响。分析:当“人民币汇率”升值的时候,进口公司的进口产品成本降低,利润率高, 反之相反。
这里面的汇率就是抽象目标类,包含保存观察者的List的增加/删除观察者的方法。以及有关汇率改变的抽象方法change(int number);而人民币的汇率则是具体的目标类,实现了父类的change方法,公司类是抽象观察者,定义了一个有关汇率反映的抽象方法response;进口公司和出口公司是具体的观察者,实现父类的response方法。接到汇率变动的时候作出反映。
结构类图如下:
实例代码如下:
复制代码
 
//抽象观察者:公司
interface Company {
    public void response(int number);
}
//具体的观察者,进口公司
class ImportCompany implements  Company{
    @Override
    public void response(int number) {
        if(number > 0){
            System.out.println("人民币升值" + number + "个基点,降低了进口的成本");
        } else {
            System.out.println("人民币贬值" + number + "个基点,增加进口的成本");
        }
    }
}
//具体的观察者2,出口公司
class ExportCompany implements  Company{
    @Override
    public void response(int number) {
        if(number > 0){
            System.out.println("人民币升值" + number + "个基点,降低出口收入");
        } else {
            System.out.println("人民币贬值" + number + "个基点,增加出口收入");
        }
    }
}
//抽象目标,汇率
abstract class Rate {
    protected List<Company> companyList = new ArrayList<>();
    //增加观察者
    public void add(Company company) {
        companyList.add(company);
    }
    public void remove(Company company) {
        companyList.remove(company);
    }
    public abstract void change(int number);
}
//具体目标。人民币汇率
class RMBrate extends Rate {
    @Override
    public void change(int number) {
        for(Company company : companyList){
            company.response(number);
        }
    }
}
public class ObserverRMBrateTest {
    public static void main(String[] args) {
        Rate rate = new RMBrate();
        Company importcompany = new ImportCompany();
        Company exportcompany = new ExportCompany();
        rate.add(importcompany);
        rate.add(exportcompany);
        rate.change(-9);
        rate.change(10);
    }
}
 
View Code
复制代码
    观察者模式在软件开发中用的最多的就是窗口程序设计中的时间处理,窗口中的所有组件都是“事件源”,也就是目标对象,而事件处理程序类的对象是具体观察者对象。下面以一个学校铃声的事件处理程序为例,介绍Windows中的“事件处理模型”工作原理。
 
实例2:使用观察者模式设计一个校园铃声的时间处理程序:
分析:本实例中,铃声是事件源和目标,老师和学生是时间的监听器和具体观察者,学生和老师来到学校的教学区,都会注意学校的铃声,这叫事件绑定;当上课时间或者下课时间到,铃声响起来。这时候造成铃声时间;学生和老师听到铃声会开始上课或者下课。这叫时间处理。这个实例非常适用观察者模式实现,下图为学校铃声的事件模型。
现在用“观察者模式”来实现事件处理模型。首先,定义一个铃声事件RingEvent类,记录铃声的类型,在定义一个学校的铃声类BellEventSource,它是事件源,是观察者目标类,该类里面包含了铃声事件处理方法heardBell(RingEvent);最后定义老师类和学生类,他们是事件监听器,是具体的观察者。下图是学校铃声事件处理程序的结构。
示例代码如下:
复制代码
 
//铃声事件类,用于封装事件源及一些与时间相关的参数
class RingEvent extends EventObject{
    private static final long serialVersionUID = -1L;
    private boolean sound;//true 上课,false 下课
    public RingEvent (Object source , boolean sound) {
        super(source);
        this.sound = sound;
    }
    public void setSound(boolean sound) {
        this.sound = sound;
    }
    public boolean getSound() {
        return sound;
    }
}
//目标类:事件源、铃
class BellEventSource {
    private List<BellEventListener> listeners;//监听器容器
    public BellEventSource() {
        listeners = new ArrayList<>();
    }
    //给事件绑定监听器
    public void addLisenter(BellEventListener bellEventListener) {
        listeners.add(bellEventListener);
    }
    //事件触发器:打铃,当铃声sound的值发生变化的时候,触发时间
    public void ring(boolean sound) {
        String type = sound?"上课铃声":"下课铃声";
        System.out.println(type + "响起");
        RingEvent event = new RingEvent(this, sound);
        notifies(event);
    }
    //当事件发生时,通知绑定在该事件源上的所有监听器做出反应(调用事件处理方法)
    protected void notifies(RingEvent event){
        BellEventListener bellEventListener = null;
        Iterator<BellEventListener> iterator = listeners.iterator();
        while(iterator.hasNext()) {
            bellEventListener = iterator.next();
            bellEventListener.heardBell(event);
        }
    }
}
//抽象观察类:铃声时间监听器
interface BellEventListener extends EventListener {
    public void heardBell(RingEvent ringEvent);
}
//具体的观察者类:老师事件监听器
class TeacherEventListener implements BellEventListener {
    @Override
    public void heardBell(RingEvent ringEvent) {
        if(ringEvent.getSound()) {
            System.out.println("老师上课了");
        }
        else {
            System.out.println("老师下课了");
        }
    }
}
//具体的观察类:学生事件监听器
class StudentEventListener implements BellEventListener {
    @Override
    public void heardBell(RingEvent ringEvent) {
        if(ringEvent.getSound()) {
            System.out.println("学生们上课了");
        } else {
            System.out.println("学生们下课了");
        }
    }
}
public class BellEventRing {
    public static void main(String[] args) {
        BellEventSource bellEventSource = new BellEventSource();//铃声,事件源
        bellEventSource.addLisenter(new TeacherEventListener());
        bellEventSource.addLisenter(new StudentEventListener());
        bellEventSource.ring(true);//上课铃声响起
        System.out.println("--------------");
        bellEventSource.ring(false);//下课铃声响起
    }
}
 
View Code
复制代码
实例3:使用观察者模式设计一个交通信号灯的事件处理程序
 
类似上个实例中的校园响铃是事件的分析,进行交通信号灯的事件处理程序的分析如下:
本实例中,交通信号灯颜色是事件源和目标,车辆是是事件的监听器和具体观察者,车辆来到信号灯前面,观察信号灯的状态,这叫事件绑定;红灯停,绿灯行,黄灯暂停,这叫时间处理。首先,定义一个信号灯颜色SignalColor类,记录信号灯的颜色,再定义一个交通信号灯类SignalLight作为事件源,是观察者目标类,该类里面包含了事件处理方法heardBell(RingEvent);最后定义车辆类,他们是事件监听器,是具体的观察者。
这个实例非常适用观察者模式实现。示例图如下:
实现代码如下:
 
复制代码
//信号灯颜色
class SignalColor extends EventObject {
    private String color;
    public SignalColor(Object source, String color){
        super(source);
        this.color  = color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public String getColor() {
        return color;
    }
}
//目标类:事件源、交通信号灯
class SignalLight {
    private List<Vehicle> listeners;//监听器容器
    public SignalLight(){
        listeners = new ArrayList<>();
    }
    //给事件源绑定监听器
    public void addVehicle(Vehicle vehicle) {
        listeners.add(vehicle);
    }
    //事件触发器:信号灯改变颜色
    public void changeColor(String color) {
        System.out.println(color + "信号灯亮...");
        SignalColor event = new SignalColor(this, color);
        notifies(event);
    }
    //事件通知方法
    protected void notifies(SignalColor signalColor) {
        Vehicle vehicle = null;
        Iterator<Vehicle> iterator = listeners.iterator();
        while(iterator.hasNext()) {
            vehicle = iterator.next();
            vehicle.see(signalColor);
        }
    }
}
//抽象观察类
interface Vehicle extends EventListener {
    //事件处理方法,看见
    public void see(SignalColor signalColor);
}
//具体观察者类:轿车
class Car implements  Vehicle {
    @Override
    public void see(SignalColor signalColor) {
        if("Red".equals(signalColor.getColor())) {
            System.out.println("红灯亮,轿车亮");
        } else {
            System.out.println("绿灯亮,轿车行");
        }
    }
}
//具体观察者类:公交车
class Bus implements   Vehicle {
    @Override
    public void see(SignalColor signalColor) {
        if("Red".equals(signalColor.getColor())) {
            System.out.println("红灯亮,轿车亮");
        } else {
            System.out.println("绿灯亮,轿车行");
        }
    }
}
public class SignalColorPattern {
    public static void main(String[] args) {
        SignalLight light = new SignalLight();//交通信号灯(事件源)
        light.addVehicle(new Car());
        light.addVehicle(new Bus());
        light.changeColor("Green");
        System.out.println("--------------");
        light.changeColor("Red");
    }
}
 
View Code 
复制代码
注意上述两个实例中都出现了EventObjec类和EventListener
(1)EventObject并不是一个特殊的类,它为开发者提供了一个source属性,一个getSource方法,和一个带有事件源对象的构造器,也就是说它只管理了一个单独的变量。
(2)EventObject是事件状态对象的基类,它封装了事件源对象以及和事件相关的信息。所有java的事件类都需要继承该类;
 
(3) java.util.EventListener是一个标记接口,就是说该接口内是没有任何方法的。所有事件监听器都需要实现该接口。事件监听器注册在事件源上,当事件源的属性或状态改变的时候,调用相应监听器内的回调方法。
详细见Java事件机制
 
3.3 状态模式的应用场景与扩展
 
(1)对象间存在一对多的关系,一个对象的状态发生改变会影响其他对象。、(2)当一个抽象模型有两个方面,其中一个方面依赖于另一个,可以将这二者封装在独立的对象中使它们可以独立地改变和复用。
 
模式的扩展:
在Java中,通过java.util.Observable类和java.util.Observer接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。
 
(1)Observable类,抽象目标类,它有一个vector,保存所有要通知的观察者对象,该类重要的三个方法是:
1、void addObserver(Observer o)
2、void notifyObservers(Object obj)方法:调用vector中所有的观察者对象中的update()方法,通知它们数据发生变化,一般是越晚加入的观察者越先得到通知。
3、void setChange()方法:设置一个boolean标志位,注明目标对象发生了变化。当它为真的时候,notifyObservers才会通知。
 
(2)java.util.Observer接口 抽象观察者,监视目标对象的变化,当目标对象发生变化时候,观察者得到通知,调用void update(Observable o,Object arg)方法,进行相应的工作。
 
实例:利用Observable类和Observer接口实现原油期货的观察者模式实例。
抽象目标Observable在Java中已经定义,原油期货类是具体的目标类,该类中定义setPrice(float price)方法,当原始数据发生变化时候调用其父类的notifyObserver(Object arg)方法来通知所有的观察者。另外抽象观察者接口(Observer)在Java中已经定义,只要定义其子类,即具体的观察者类(多方Bull和空头Bear)并且实现update方法即可,下图是结构图:
示例代码如下:
 
复制代码
//具体目标类:原油期货类
class CrudeOil extends Observable {
    private float price;
    public float getPrice() {
        return price;
    }
    public void setPrice(float price) {
        super.setChanged();//设置内部标志位,注册数据发生变化
        super.notifyObservers(price);//通知观察者价格改变了
        this.price = price;
    }
}
//具体观察者:多方
class Bull implements Observer {
    public void update(Observable observable, Object object) {
        Float price  = ((Float)object).floatValue();
        if(price > 0) {
            System.out.println("原油价格上涨" + price + "元,多头做多成功");
        } else {
            System.out.println("原油价格下降" + price + "元,多头被暴击");
        }
    }
}
//具体观察者:空头
class Bear implements Observer {
    public void update(Observable observable, Object object) {
        Float price  = ((Float)object).floatValue();
        if(price > 0) {
            System.out.println("原油价格上涨" + price + "元,空头被暴击");
        } else {
            System.out.println("原油价格下降" + price + "元,做空成功");
        }
    }
}
public class CrudeOilFutures {
    public static void main(String[] args) {
        CrudeOil crudeOil = new CrudeOil();
        Observer bull = new Bull();
        Observer bear = new Bear();
        crudeOil.addObserver(bull);
        crudeOil.addObserver(bear);
        crudeOil.setPrice(10);
        crudeOil.setPrice(-1);
    }
}
 
 
View Code
复制代码

 

 
 
 
posted @   jrliu  阅读(330)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示