原创

JAVA设计模式(篇十)

温馨提示:
本文最后更新于 2022年10月27日,已超过 918 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

十七-状态模式(难)

在软件开发过程中,应用程序中的有些对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而使得其行为也随之发生改变。

对这种有状态的对象编程,传统的解决方案是: 将这些所有可能发生的情况全都考虑到,然后使用if-else语句来做状态判断,再进行不同情况的处理。但当对象的状态很多时,程序会变得很复杂。而且增加新的状态要添加新的 if-else语句,这违背了“开闭原则”,不利于程序的扩展。

以上问题如果采用“状态模式"就能很好地得到解决。状态模式的解决思想是∶当控制一个对象状态转换的条件表达式过于复杂时,把相关“判断逻辑”提取出来,放到一系列的状态类当中,这样可以把原来复杂的逻辑判断简单化。

1.状态模式的定义与特点

1. 状态模式定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

2. 优点

  • 状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
  • 减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
  • 有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。

3. 缺点:

  • 状态模式的使用必然会增加系统的类与对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。

2. 模式的结构和实现

状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。

状态模式包含三种角色,环境角色,抽象状态角色,具体状态角色。

1. 模式的结构

  • 环境角色: 也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态(默认生成一个初始状态),并将与状态相关的操作委托给当前状态对象来处理。
  • 抽象状态角色: 定义一个接口,用以封装环境对象中的特定状态所对应的行为(环境对象的行为的改变,通过该方法实现)。
  • 具体状态角色:实现抽象状态接口,具体状态的实际改变者。
  • 结构图:

image-20221013110359061

2.模式的实现

//环境角色
public class ContextStatus {

    private AbstractStatus status;

    //初始化一个默认状态
    public ContextStatus(){
        this.status = new SpecificStatus();
    }

    public AbstractStatus getStatus() {
        return status;
    }

    public void setStatus(AbstractStatus status) {
        this.status = status;
    }

    public void handel(){
        this.status.handle(this);
    }
}

//抽象状态
public abstract class AbstractStatus {

   public abstract void handle(ContextStatus contextStatus);

}
//具体状态1
public class SpecificStatus extends AbstractStatus{

    @Override
    public void handle(ContextStatus contextStatus) {
        System.out.println("默认状态");
        contextStatus.setStatus(new SpecificStatusTwo());
    }
}

//具体状态类2
public class SpecificStatusTwo extends AbstractStatus{

    @Override
    public void handle(ContextStatus contextStatus)
    {
        System.out.println("改变状态");
        contextStatus.setStatus(new SpecificStatus());
    }
}
//测试
    @Test
    public void status(){
        ContextStatus contextStatus = new ContextStatus();
        contextStatus.handel();
        contextStatus.handel();
        contextStatus.handel();
        contextStatus.handel();
    }

默认状态
改变状态
默认状态
改变状态

3.模式的应用实例

模拟线程运行状态,线程的运行状态分为五种 1.新建状态 2.就绪状态 3.运行状态 4.阻塞状态 5.死亡状态 。

//线程上下文
public class TheadContext {

    private TheadAbstractStatus theadAbstractStatus;

    //初始化时生成一个默认状态(新建状态)
    public TheadContext() {
        theadAbstractStatus = new TheadCreate(this);
    }

    public TheadAbstractStatus getTheadAbstractStatus() {
        return theadAbstractStatus;
    }

    public void setTheadAbstractStatus(TheadAbstractStatus theadAbstractStatus) {
        this.theadAbstractStatus = theadAbstractStatus;
    }

    //改变线程状态
    public void changeStatus(int theadStatus) {
        theadAbstractStatus.changeThreadStatus(theadStatus);
    }
}

//线程抽象状态
public abstract class TheadAbstractStatus {

    //线程上下文
    protected TheadContext theadContext;
    //线程状态
    protected int status;
    //线程状态名称
    protected String threadStatusName;

    //检查线程状态
    protected abstract void checkStatus();

    //修改线程状态
    public void changeThreadStatus(int status) {
        this.status = status;
        if (this.status == 5) {
            System.out.println("线程为死亡状态,运行结束。。。");
            return;
        }
        checkStatus();
        System.out.println("当前线程状态为==" + theadContext.getTheadAbstractStatus().threadStatusName);
    }
}

//线程具体状态==新建
public class TheadCreate extends TheadAbstractStatus {

    //线程上下文初始化时,默认状态
    public TheadCreate(TheadContext context) {
        theadContext = context;
        status = 1;
        threadStatusName = "新建状态";
    }

    //线程状态切换时状态(其他线程状态不会切换到新建状态,此处添加是便于理解)
    public TheadCreate(TheadAbstractStatus theadAbstractStatus) {
        theadContext = theadAbstractStatus.theadContext;
        threadStatusName = "新建状态";
        status = theadAbstractStatus.status;
    }

    @Override
    protected void checkStatus() {
        if (status == 2) {
            theadContext.setTheadAbstractStatus(new TheadReady(this));
        }
    }
}

//线程具体状态==就绪
public class TheadReady extends TheadAbstractStatus {

    //线程状态切换时,状态
    public TheadReady(TheadAbstractStatus theadAbstractStatus) {
        theadContext = theadAbstractStatus.theadContext;
        threadStatusName = "就绪状态";
        status = theadAbstractStatus.status;
    }

    @Override
    protected void checkStatus() {
        if (status == 3) {
            theadContext.setTheadAbstractStatus(new TheadRunning(this));
        } else if (status == 4) {
            theadContext.setTheadAbstractStatus(new TheadLocking(this));
        }
    }
}

//线程具体状态==运行
public class TheadRunning extends TheadAbstractStatus {

    //线程状态切换时,状态
    public TheadRunning(TheadAbstractStatus theadAbstractStatus) {
        theadContext = theadAbstractStatus.theadContext;
        threadStatusName = "运行状态";
        status = theadAbstractStatus.status;
    }

    @Override
    protected void checkStatus() {
        if (status == 4) {
            theadContext.setTheadAbstractStatus(new TheadLocking(this));
        } else if (status == 5) {
            theadContext.setTheadAbstractStatus(new TheadDead(this));
        }
    }
}

//线程具体状态==阻塞状态
public class TheadLocking extends TheadAbstractStatus {

    //线程状态切换时,状态
    public TheadLocking(TheadAbstractStatus theadAbstractStatus) {
        theadContext = theadAbstractStatus.theadContext;
        threadStatusName = "阻塞状态";
        status = theadAbstractStatus.status;
    }

    @Override
    protected void checkStatus() {
        if (status == 2) {
            theadContext.setTheadAbstractStatus(new TheadReady(this));
        } else if (status == 3) {
            theadContext.setTheadAbstractStatus(new TheadRunning(this));
        } else if (status == 5) {
            theadContext.setTheadAbstractStatus(new TheadDead(this));
        }
    }
}

//线程具体状态==死亡
public class TheadDead extends TheadAbstractStatus {

    //线程状态切换时,状态
    public TheadDead(TheadAbstractStatus theadAbstractStatus) {
        theadContext = theadAbstractStatus.theadContext;
        threadStatusName = "死亡状态";
        status = theadAbstractStatus.status;
    }

    @Override
    protected void checkStatus() {
        //线程为死亡状态时,不可再切换其他状态,只能重新新建线程
        theadContext.setTheadAbstractStatus(new TheadCreate(theadContext));
    }
}

//测试
    @Test
    public void status(){
        TheadContext theadContext = new TheadContext();
        //新建状态
        theadContext.changeStatus(1);
        //就绪状态
        theadContext.changeStatus(2);
        //运行状态
        theadContext.changeStatus(3);
        //阻塞状态
        theadContext.changeStatus(4);
        //就绪状态
        theadContext.changeStatus(2);
        //运行状态
        theadContext.changeStatus(3);
        //死亡状态
        theadContext.changeStatus(5);
    }

当前线程状态为==新建状态
当前线程状态为==就绪状态
当前线程状态为==运行状态
当前线程状态为==阻塞状态
当前线程状态为==就绪状态
当前线程状态为==运行状态
线程为死亡状态,运行结束。。。

3.模式的应用场景

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

4.状态模式的扩展

在有些情况下,可能有多个环境对象需要共享一组状态,这时需要引入享元模式,将这些具体状态对象放在集合中供程序共享

image-20221013111738814

//环境类
public class ShareContextStatus {

    private Map<String, ShareAbstractStatus> statusMap = new HashMap<>();

    private ShareAbstractStatus shareAbstractStatus;

    public ShareContextStatus() {
        shareAbstractStatus = new ShareAbstractStatusOne();
    }

    public Map<String, ShareAbstractStatus> getStatusMap() {
        return statusMap;
    }

    public void setStatusMap(Map<String, ShareAbstractStatus> statusMap) {
        this.statusMap = statusMap;
    }

    public ShareAbstractStatus getShareAbstractStatus() {
        return shareAbstractStatus;
    }

    public void setShareAbstractStatus(ShareAbstractStatus shareAbstractStatus) {
        this.shareAbstractStatus = shareAbstractStatus;
    }

    public void addShareAbstractStatus(String statusName, ShareAbstractStatus shareAbstractStatus) {
        statusMap.put(statusName, shareAbstractStatus);
    }

    public ShareAbstractStatus getShareAbstractStatus(String statusName) {
        return statusMap.get(statusName);
    }

    public void handle() {
        shareAbstractStatus.handle(this);
    }
}

//抽象状态
public abstract class ShareAbstractStatus {

   public abstract void handle(ShareContextStatus contextStatus);

}
//抽象状态1
public class ShareAbstractStatusOne extends ShareAbstractStatus{
    @Override
    public void handle(ShareContextStatus contextStatus) {
        System.out.println("这是状态111");
        contextStatus.setShareAbstractStatus(contextStatus.getShareAbstractStatus("status2"));
    }
}

//抽象状态2
public class ShareAbstractStatusTwo extends ShareAbstractStatus{
    @Override
    public void handle(ShareContextStatus contextStatus) {
        System.out.println("这是状态222");
        contextStatus.setShareAbstractStatus(contextStatus.getShareAbstractStatus("status1"));
    }
}
//测试
    @Test
    public void status(){
        ShareContextStatus contextStatus = new ShareContextStatus();
        contextStatus.addShareAbstractStatus("status1",new ShareAbstractStatusOne());
        contextStatus.addShareAbstractStatus("status2",new ShareAbstractStatusTwo());
        contextStatus.handle();
        contextStatus.handle();
        contextStatus.handle();
        contextStatus.handle();
        contextStatus.handle();
        contextStatus.handle();
    }
这是状态111
这是状态222
这是状态111
这是状态222
这是状态111
这是状态222

十八-观察者模式

在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如,国际石油价格上涨时,会影响到各国的运输,民生等行业,股票上升或下跌会影响到股民及公司等。

1.模式的定义与特点

观察者模式:指对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式模型-视图模式,它是对象行为型模式。

优点:

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
  • 目标与观察者之间建立了一套触发机制。

缺点:

  • 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
  • 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

2.模式的结构和实现

观察者模式是包含抽象主题角色具体主题角色抽象观察者角色具体观察者角色

1.观察者模式的结构

  • 抽象主题角色: 也称为抽象目标类,包含一组观察者的实例(抽象观察者角色集合),拥有添加和移除观察者的方法,拥有一个通知 所有观察者的方法。
  • 具体主题角色: 实现抽象目标类中的通知方法,当目标类状态发生改变时,通知所有已注册(被添加进目标类中的)的观察者。
  • 抽象观察者角色: 是一个抽象类或接口,包含一个改变自身状态的方法,当目标类状态发生改变时,改变自身的状态(实现自我更新)。
  • 具体观察者角色:实现抽象观察者中的抽象方法,在接收到通知的时候便于更新自身状态。

tips: 抽象观察者角色和具体观察者角色,两者其实是一种多态的引用,便于在抽象目标类中统一保存和调用。

image-20221014144958591

2.观察者模式的实现

//抽象目标类
public abstract class AbstractSubject {

    public List<AbstractObserver> observerList = new ArrayList<>();

    public void addObserver(AbstractObserver observer) {
        observerList.add(observer);
    }

    public void removeObserver(AbstractObserver observer) {
        observerList.remove(observer);
    }

    //通知方法
    public abstract void notifyAllSubject();

}

//抽象观察者
public abstract class AbstractObserver {
    //默认状态为1
    int status = 1;
    //收到通知时,自己改变自己的状态
    public abstract void changeSelf();
}

//具体观察者
public class SpecificObserver extends AbstractObserver{

    @Override
    public void changeSelf() {
        if (status==1){
            status = 2;
        }else {
            status = 1;
        }
        System.out.println("具体观察者当前状态="+status);
    }
}

//具体目标类
public class SpecificSubject extends AbstractSubject{

    @Override
    public void notifyAllSubject() {
        if (!CollectionUtils.isEmpty(observerList)){
            observerList.forEach(l->l.changeSelf());
        }
    }
}

//测试
    @Test
    public void observer(){
        AbstractSubject subject = new SpecificSubject();
        subject.addObserver(new SpecificObserver());
        subject.notifyAllSubject();
        subject.notifyAllSubject();
    }
具体观察者当前状态=2
具体观察者当前状态=1

3. 实例

以人民币汇率对进出口公司的影响为例。人民币汇率升值时,进口公司的进口成本和利润率会变高,出口公司的出口成本变高和利润率变低。反之,同理

//抽象目标类==人民币汇率
public abstract class RmbRate {

    public List<InOutCompany> companyList = new ArrayList<>();

    public void addCompany(InOutCompany company){
        companyList.add(company);
    }
    public void removeCompany(InOutCompany company){
        companyList.remove(company);
    }
    //汇率发生变化1升值0贬值
    public abstract void rateChange(int flag);
}

//具体目标类==人民币汇率状态改变
public class SpecificRmbRate extends RmbRate{

    @Override
    public void rateChange(int flag) {
        if (!CollectionUtils.isEmpty(companyList)){
            companyList.forEach(c->c.rateChange(flag));
        }
    }
}
//抽象观察者  公司
public interface InOutCompany {
    void rateChange(int flag);
}

//具体观察者=进口公司
public class InCompany implements InOutCompany{

    @Override
    public void rateChange(int flag) {
        if (flag==1){
            System.out.println("进口公司成本变低,利润率提升");
        }else {
            System.out.println("进口公司成本变高,利润率降低");
        }
    }
}

//具体观察者=出口公司
public class OutCompany implements InOutCompany{

    @Override
    public void rateChange(int flag) {
        if (flag==1){
            System.out.println("出口公司成本变高,利润率降低");
        }else {
            System.out.println("出公司成本变低,利润率提升");
        }
    }
}

//测试
    @Test
    public void observer(){
        RmbRate rmbRate = new SpecificRmbRate();
        rmbRate.addCompany(new InCompany());
        rmbRate.addCompany(new OutCompany());
        rmbRate.rateChange(1);
        System.out.println("=============");
        rmbRate.rateChange(0);
    }

进口公司成本变低,利润率提升
出口公司成本变高,利润率降低
=============
进口公司成本变高,利润率降低
出公司成本变低,利润率提升

3. 观察者模式的应用场景

  • 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
  • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

4.观察者模式的扩展

在Java中,通过java.util.Observable类和java.util.Observer接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。

1.Observable

抽象主题类(目标类),以下为三个主要目标方法

  1. addObserver(obj) :添加观察者方法,obj为观察者对象
  2. notifyObservers(args): 状态改变提醒方法,args为传参,可选可不选
  3. setChanged(): 状态改变方法,只有调用了该状态改变方法,才会通知所有观察者

2.Observer接口

抽象观察者,主要方法为void update(Observable o,Object arg) 。目标类状态发生改变通知观察者时,通过该方法进行观察者状态更新。

3.实例

使用该工具类实现国际原油价格上下浮动时,加油站和油车司机的状态变化

//具体目标类==石油价格变化
public class SpecificObservable extends Observable {

    public void priceNotify(int price){
        System.out.println("国际原油今日价格走势:"+price+"元/桶");
        super.setChanged();
        super.notifyObservers(price);
        System.out.println("---------------------------------------------");
    }
}

//具体观察者===加油站
public class SpecificObserverGasStation implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        int price = (Integer) arg;
        if (price>0){
            System.out.println("石油价格上涨:"+price+",加油站开心了。。。。");
        }else {
            System.out.println("石油价格下跌:"+price+",加油站难过了。。。。");
        }
    }
}

//具体观察者===油车司机
public class SpecificObserverUser implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        int price = (Integer) arg;
        if (price>0){
            System.out.println("石油价格上涨:"+price+",驾驶员难过了。。。。");
        }else {
            System.out.println("石油价格下跌:"+price+",驾驶员开心了。。。。");
        }
    }
}
//测试
    @Test
    public void observer(){
        SpecificObservable observable = new SpecificObservable();
        observable.addObserver(new SpecificObserverGasStation());
        observable.addObserver(new SpecificObserverUser());
        observable.priceNotify(3);
        observable.priceNotify(-2);
    }
国际原油今日价格走势:3元/桶
石油价格上涨:3,驾驶员难过了。。。。
石油价格上涨:3,加油站开心了。。。。
---------------------------------------------
国际原油今日价格走势:-2元/桶
石油价格下跌:-2,驾驶员开心了。。。。
石油价格下跌:-2,加油站难过了。。。。
---------------------------------------------
正文到此结束