Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javascript之状态模式 #6

Open
Jouryjc opened this issue Jul 30, 2018 · 0 comments
Open

Javascript之状态模式 #6

Jouryjc opened this issue Jul 30, 2018 · 0 comments

Comments

@Jouryjc
Copy link
Owner

Jouryjc commented Jul 30, 2018

定义

状态模式是个人觉得所有设计模式中比较难理解的一个。容易与其他模式混淆,特别是策略模式!难归难,掌握了这个设计模式,很多问题都会变得非常简单。

状态模式的关键是要区分事物内部的状态,内部的改变往往会带来事物的行为改变。现实生活中应用状态模式的例子非常多。最常见的就是家里电灯和开关的关系。通过开关,去控制电灯的开与闭。

状态模式即允许一个对象在其内部状态改变时改变它的行为。

举例

上面这个例子用代码实现也比较简单,大部分人会使用if else语句去完成。对于这个例子来说没什么问题。

将上面的例子稍微复杂化,暖白交替的灯显示生活中也很常见。按一下开关是白色,再按一下是暖色,再按一下变成暖白混合色,再按一下就关闭了。脑子里面可以简单的过一下这个逻辑!用代码编写这段逻辑就是四个if else的串联:

Light.prototype.pressButton = () => {
    if (this.state === 'off') {
        this.state = 'white';
    } else if (this.state === 'white') {
        this.state = 'warm';
    } else if (this.state === 'warm') {
        this.state = 'mixedWhiteWarm';
    } else if (this.state === 'mixedWhiteWarm') {
        this.state = 'off';
    }
}

要是再添加一个颜色,就要修改函数内部的逻辑,这显示不是我们想要的。也违反了“开放-封闭”原则。而且pressButton也会越来越庞大。

现在我们用状态模式来修改这个例子。

  • 先定义四个类OffState、Warm、White、MixedWhiteWarm
  • 都有原型方法pressButton,表示按钮按下时的行为
var OffState = (light) => {
    this.light = light;
}

OffState.prototype.pressButton = () => {
    this.light.setState(this.light.Warm);
}

var Warm = (light) => {
    this.light = light;
}

Warm.prototype.pressButton = () => {
    this.light.setState(this.light.White);
}

var White = (light) => {
    this.light = light;
}

White.prototype.pressButton = () => {
    this.light.setState(this.light.MixedWhiteWarm);
}

var MixedWhiteWarm = (light) => {
    this.light = light;
}

MixedWhiteWarm.prototype.pressButton = () => {
    this.light.setState(this.light.offState);
}

接下来写Light类

var Light = () => {
    this.offState = new OffState(this);
    this.Warm = new Warm(this);
    this.White = new White(this);
    this.MixedWhiteWarm = new MixedWhiteWarm(this);
    this.button = null;
}

添加一个初始函数

Light.prototype.init = () => {
    var button = document.createElement('button');

    this.button = document.body.appendChild(button);
    this.button.innerHTML = '按钮';

    this.currentState = this.offState;
    
    this.button.onclick = () => {
        this.currentState.pressButton(); 
    }

}

最后再加一个setState方法:

Light.prototype.setState = (newState) => {
     this.currentState = newState;
}

测试代码:

var light = new Light();
light.init();

这就使用状态模式完成了灯的例子。好处很明显:可以使每一种状态和它对应的行为之间的关系局部化,这些行为被分散和封装在各自对应的状态类之中,便于阅读和管理。

这时如果需要再加一种蓝色灯,只需要添加一个状态类和在Light构造函数中加一个状态。

优点

  • 状态模式定义了状态与行为之间的关系,并将它们封装在一个类里。通过添加新的状态类,很容易增加新的状态转换。
  • 避免函数体无限膨胀,状态切换逻辑被分布在状态类中,避免写很多if else的条件分支。
  • 状态间的切换逻辑非常清晰。
  • 请求动作和封装的行为可以非常容易地独立变化且互不影响。

缺点

  • 定义了非常多的类,编写类的过程比较枯燥。
  • 增加不少对象,消耗内存。
  • 虽然避开了条件分支,却使逻辑分散了。没办法在一个地方看到整个状态机的逻辑。

性能优化

  • 对于管理state对象的创建和销毁,有两种选择。第一种是按需去创建和销毁状态;第二种是一开始就创建好全部状态。对于初始状态比较庞大的对象,建议使用第一种方式,节省内存。如果状态切换的很频繁,建议使用第二种方式,并且无须销毁。

对比策略模式

状态模式和策略模式经常不知怎么区分。它们的相同点是都有一个上下文,一些策略或者状态类,上下文把请求委托给这些类来执行。

状态模式和策略模式的区别是后者中各个策略类之间是平等且平行的,它们之间没有任何的联系,所以客户必须熟知每一个策略的作用细节。而在状态模式中,初始时就封装好了所有的状态以及对应的行为,状态之间的转换也早被规定好;改变状态的行为发生在模式内部,用户不需要了解状细节。

小结

状态模式比较难,但是应用非常广泛。比如说现在火热的前端框架react,对于每一个组件来说,都是一个状态机!state表示react组件内部的一种状态,通过组件内的getInitialState函数,可以为组件的初始状态赋值,当组件的状态发生改变时,组件会重新渲染。

开发中,遇到很多种状态转换时,可以考虑使用状态模式,理清楚状态以及行为的关系,接下来的工作就会变得简单很多!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant