设计模式(10) 状态模式
状态模式
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
介绍
意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
何时使用:代码中包含大量与对象状态有关的条件语句。
如何解决:将各种具体的状态类抽象出来。
关键代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句。
应用实例: 1、打篮球的时候运动员可以有正常状态、不正常状态和超常状态。 2、曾侯乙编钟中,'钟是抽象接口','钟A'等是具体状态,'曾侯乙编钟'是具体环境(Context)。
优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景: 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者。
注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。
实现
1,电池状态
class SuccessState {
show() {
console.log('绿色');
}
}
class WarningState {
show() {
console.log('黄色');
}
}
class ErrorState {
show() {
console.log('红色')
}
}
class WorstErrorState {
show() {
console.log('深红色')
}
}
class Battery {
amount:string = 'high'
state:any = new SuccessState()
constructor() {
this.state.show();
}
show() {
if(this.amount == 'high') {
this.amount = 'middle';
this.state = new WarningState();
} else if(this.amount == 'middle') {
this.amount = 'low';
this.state = new ErrorState();
} else if(this.amount == 'low') {
this.state = new WorstErrorState();
}
}
}
let t1 = new Battery();
t1.show();
t1.show();
t1.show();
t1.show();
2,实现点赞功能
<!DOCTYPE html>
<html lang="en">
<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>
<body>
<div id="root">
</div>
</body>
<script>
let likeState = {
render(element) {
element.innerHTML = '赞';
}
}
let likedState = {
render(element) {
element.innerHTML = '取消';
}
}
class Button {
constructor(container) {
this.liked = false;
this.state = likeState;
this.element = document.createElement('button');
container.appendChild(this.element);
this.render();
}
setState(state) {
this.state = state;
this.render();
}
render() {
this.state.render(this.element);
}
}
let button = new Button(document.getElementById('root'));
button.element.addEventListener('click',()=>{
button.setState(button.liked?likedState:likeState);
button.liked = !button.liked;
})
</script>
</html>
3,切换显示banner
const States = {
'show': function() {
console.log("banner显示,点击可以关闭");
this.setState({
currentState: "hide"
})
},
'hide': function() {
console.log("banner隐藏,点击可以打开");
this.setState({
currentState: "show"
})
}
}
class Banner extends React.Component {
state = {currentState: 'show'}
toggle = () => {
let s = this.state.currentState;
States[s]&&States[s].apply(this);
}
redner() {
return (
<div>
{this.state.currentState == 'show' && <nav>导航</nav>}
<button onClick = {this.toggle}>toggle</button>
</div>
)
}
}
4,简易Promise实现
class promise {
state: string = 'initial'
successes: Array<Function> = []
errors: Array<Function> = []
constructor(fn:Function) {
fn(this.resolve,this.reject);
}
resolve(data:any) {
this.state = 'fulfilled';
this.successes.forEach((fn: Function) => fn(data));
}
reject(err:Error) {
this.state = 'rejected';
this.errors.forEach((fn: Function) => fn(err));
}
then(success:Function,error:Function) {
this.successes.push(success);
this.errors.push(error);
}
}