设计模式之----状态模式(State-pattern)的理解

1.前言

在现实生活中,人都有高兴和伤心的时候,不同的时候有不同的行为,有状态的对象编程中高兴,伤心可以看成一种状态,传统的解决方案是:人的不同时候的行为都要考虑到的话,然后使用 if-else 或 switch-case 语句来做状态判断,再进行不同情况的处理。这样代码会会过于臃肿,可读性差,且不具备扩展性,维护难度也大。且增加新的状态时要添加新的 if-else 语句,这违背了“开闭原则”,不利于程序的扩展。


2.概念

状态模式的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。状态模式的思想是:当控制一个对象状态转换的条件表达式过于复杂时,把相关“判断逻辑”提取出来,用各个不同的类进行表示,系统处于哪种情况,直接使用相应的状态类对象进行处理,这样能把原来复杂的逻辑判断简单化,消除了 if-else、switch-case 等冗余语句,代码更有层次性,并且具备良好的扩展力。


3.模式的结构和实现

状态模式把受环境改变的对象行为包装在不同的状态对象里,其目的是让一个对象在其内部状态改变的时候,其行为也随之改变。现在我们来分析其基本结构和实现方法。

3.1 模式的结构

状态模式包含以下主要角色。

  1. 环境类(Context)角色:也称为上下文,相当是所有状态的管理者(manage)并负责具体状态的切换。
  2. 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
  3. 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

3.2模式的实现

场景:这个场景大家都很熟悉,鼠标进去某个元素,会有个高亮的样式,当鼠标点击这个元素的时候,会有个选中的样式,再次点击选中状态样式消失,当鼠标离开元素,元素没有样式

在这里插入图片描述
简单分析下,这里的话会有4个状态,正常状态(无样式),高亮状态(悬浮样式),选中状态(选中样式),高亮选中状态(悬浮加选中样式),并且一个状态进入另一个状态是有条件的,看下图

在这里插入图片描述
按需求画出类图

在这里插入图片描述
1.首先创建一个抽象类ElementStatus

export abstract class ElementStatus {
    public statusName: string;
    public abstract calculateState(eventName: string):ElementStatus;
}

2.分别创建ElementNormalState类(正常状态),ElementHightLightState类(高亮状态),ElementHightLightSelectState类(高亮选中状态),ElementSelectState类(选中状态)和构建这些状态的StateFactory类(工厂)

//ElementNormalState.ts     正常状态
import { ElementStatus } from "./ElementStatus";
import { StateFactory } from "./StateFactory";
export class ElementNormalState extends ElementStatus {
    public statusName: string;
    constructor() {
        super();
        this.statusName = "正常状态";
    }

    public calculateState(eventName: string):ElementStatus {
        if (eventName == "mouseEnter") {
            return StateFactory.getElementStatus("ElementHightLightState");
        }
        throw new Error("现在是正常状态,切换不了其他状态");
    }
}

//ElementHightLightState.ts  高亮状态
import { ElementStatus } from "./ElementStatus";
import { StateFactory } from "./StateFactory";
export class ElementHightLightState extends ElementStatus {
    public statusName: string;
    constructor() {
        super();
        this.statusName = "高亮状态";
    }
    public calculateState(eventName: string): ElementStatus {
        if (eventName == "mouseLeaver") {
            return StateFactory.getElementStatus("ElementNormalState");
        } else if (eventName == "mouseClick") {
            return StateFactory.getElementStatus("ElementHightLightSelectState");
        }
        throw new Error("现在是高亮状态,切换不了其他状态");
    }
}


//ElementHightLightSelectState.ts  高亮选中状态
import { ElementStatus } from "./ElementStatus";
import { StateFactory } from "./StateFactory";
export class ElementHightLightSelectState extends ElementStatus {
    public statusName: string;
    constructor() {
        super();
        this.statusName = "高亮选中状态";
    }
    public calculateState(eventName: string): ElementStatus {
        if (eventName == "mouseLeaver") {
            return StateFactory.getElementStatus("ElementSelectState");
        } else if (eventName == "mouseClick") {
            return StateFactory.getElementStatus("ElementHightLightState");
        }
        throw new Error("现在是高亮选中状态,切换不了其他状态")
    }
}


//ElementSelectState.ts 选中状态
import { ElementStatus } from "./ElementStatus";
import { StateFactory } from "./StateFactory";

export class ElementSelectState extends ElementStatus {
    public statusName: string;
    constructor() {
        super();
        this.statusName = "选中状态";
    }
    public calculateState(eventName: string): ElementStatus {
        if (eventName == "mouseEnter") {
            return StateFactory.getElementStatus("ElementHightLightSelectState");
        }
        throw new Error("现在是选中状态,切换不了其他状态");
    }
}


//StateFactory.ts    工厂类
import { ElementHightLightSelectState } from "./ElementHightLightSelectState";
import { ElementHightLightState } from "./ElementHightLightState";
import { ElementNormalState } from "./ElementNormalState";
import { ElementSelectState } from "./ElementSelectState";
export class StateFactory {
    public static getElementStatus(statusName: string): Status.ElementStatus {
        switch (statusName) {
            case "ElementNormalState":
                return new ElementNormalState();
            case "ElementHightLightState":
                return new ElementHightLightState();
            case "ElementHightLightSelectState":
                return new ElementHightLightSelectState();
            case "ElementSelectState":
                return new ElementSelectState();
            default:
                return new ElementNormalState();
        }
    }
}


3.创建ElementStatusContext类 管理这些状态的类,设置默认状态是正常状态

//ElementStatusContext.ts
import { ElementNormalState } from "./ElementNormalState";
export class ElementStatusContext{
    public currentStatus: Status.ElementStatus;

    constructor() {
        this.currentStatus = new ElementNormalState();
    }

    public getStatus() {
        return this.currentStatus
    }

    public setStatus(status: Status.ElementStatus) {
        this.currentStatus = status;
    }

    public changeStatus(eventName: string): Status.ElementStatus {
        return this.currentStatus.calculateState(eventName);
    }

}

//Status.d.ts   ts声明文件
declare namespace Status {
    export type ElementStatus =import ("./ElementStatus").ElementStatus;
    export type ElementStatusContext =import ("./ElementStatusContext").ElementStatusContext;

}

4.最后IndexController 通过事件绑定好这些状态并显示在UI界面上

//Index.controller.ts
import angular from "angular";
import { ElementStatusContext } from "./ElementStatusContext";
export class IndexController implements angular.IController {
    public static ElementStatusContext: Status.ElementStatusContext;
    public statusName: string;
    constructor() {
        IndexController.ElementStatusContext = new ElementStatusContext();
        this.statusName = IndexController.ElementStatusContext.getStatus().statusName;
    }

    //鼠标进入事件
    public mouseEnterEvent(): void {
        let status = this.calAndSetStatus(IndexController.ElementStatusContext, "mouseEnter");
        this.statusName = status.statusName

    }

    //鼠标离开事件
    public mouseLeaveEvent(): void {
        let status = this.calAndSetStatus(IndexController.ElementStatusContext, "mouseLeaver");
        this.statusName = status.statusName
    }

    //colorElement点击事件
    public colorElementClickEvent(): void {
        let status = this.calAndSetStatus(IndexController.ElementStatusContext, "mouseClick");
        this.statusName = status.statusName
    }


    //计算并设置状态
    private calAndSetStatus(StatusContext: Status.ElementStatusContext, eventName: string) {
        let status = StatusContext.changeStatus(eventName);
        StatusContext.setStatus(status);
        return status;
    }
}

下面是 index.html ui界面

<!DOCTYPE html>
<html lang="en" ng-app="myApp">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    .element {
        width: 200px;
        height: 200px;
        border: 5px solid  #ccc;
        margin: 0 auto;
        line-height: 200px;
        text-align: center;
        font-size: 20px;
    }

    .hight-light {
        background-color: greenyellow;
    }

    .select {
       border-color: green;
    }

    .hight-light-select {
        background-color: greenyellow;
        border-color: green;
    }
</style>

<body ng-controller="IndexController as vm">
    <div class="element" ng-mouseenter="vm.mouseEnterEvent()" ng-mouseleave="vm.mouseLeaveEvent()"
        ng-click="vm.colorElementClickEvent()" ng-class="{'hight-light':vm.statusName=='高亮状态','select':vm.statusName=='选中状态','hight-light-select':vm.statusName=='高亮选中状态'}">{{vm.statusName}}</div>
</body>
</html>

最后看下效果图:

在这里插入图片描述

总结优缺点

优点:

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

缺点:

  1. 状态模式的使用必然会增加系统的类与对象的个数。
  2. 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
  3. 状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值