【设计模式】状态模式

2023-12-30 22:43:04

引例

交通信号灯系统的设计与实现
image.png
方案一
传统设计方案
定义交通灯颜色的枚举```

public enum LightColor {
Green,Red,Yellow
}

交通灯类TrafficLight,处理颜色转换等业务逻辑

public class TrafficLight{
	private LightColor lightColor;
	// 将信号灯初始化为红灯
	public TrafficLight(){lightColor = LightColor.Red;}
	// 信号转换处理
	public void changeSignal(){
		if (lightColor == LightColor.Red){
			System.out.println("红灯停");
			lightColor = LightColor.Green;
		}
		else if (lightColor == LightColor.Green){
			System.out.println("绿灯行");
			lightColor = LightColor.Yellow;
		}
		else if (lightColor == LightColor.Yellow){
			System.out.println("黄灯亮了等一等");
			lightColor = LightColor.Red;
		}
	}
}

客户端类

public class Client{
	public static void main(String[] args){
		TrafficLight light = new TrafficLight();
		light.changeSignal();
		light.changeSignal();
		light.changeSignal();
	}
}

运行结果为:

红灯停
绿灯行
黄灯亮了等一等

说明:

  1. TrafficLight类种的if-else条件分支违背开闭原则

方案二
参考策略模式进行修改
image.png
说明:

  1. 将交通灯的展示即display()做成了策略,因而策略类形成了层次类,满足OCP
  2. 具体类满足单一职责
  3. 环境类引用策略完成展示和交通灯颜色切换

代码说明
交通灯层次类

public interface ITrafficLightStrategy {
	void display();
}

public class RedLightStrategy implements ITrafficLightStrategy {
	@Override
	public void display() {
		System.out.println("红灯停");
	}
}

public class GreenLightStrategy implements ITrafficLightStrategy {
	@Override
	public void display() {
		System.out.println("绿灯行");
	}
}

public class YellowLightStrategy implements ITrafficLightStrategy {
	@Override
	public void display() {
		System.out.println("黄灯请等一等");
	}
}

环境类Context:
在Context类中有一个ITrafficLightStrategy对象,用于控制当前的交通灯颜色,showSignal()方法显示,changeSignalStrategy()方法改变交通灯

public class Context {
	private ITrafficLightStrategy trafficLightStrategy;
	public TrafficLight(ITrafficLightStrategy trafficLightStrategy) {
		this.signalStrategy = signalStrategy;
	}
	public void showSignal() {
		if (signalStrategy != null) {            signalStrategy.displaySignal();
	}}
	public void changeSignalStrategy (ITrafficLightStrategy trafficLightStrategy) {
		this.signalStrategy = signalStrategy;
	}
}

Client类实现:

public class Client {
	public static void main(String[] args) {
		Context context = new Context(new RedLightStrategy());    
		context.showSignal();
		context.changeSignalStrategy(new GreenLightStrategy());
		context.showSignal();
		context.changeSignalStrategy(new YellowLightStrategy());
		context.showSignal();
	}
}

说明:在方案二的设计中交通灯的颜色切换实现是完全暴露给Client的,不符合面向对象的封装特性

方案三
将每种颜色的灯做成一个类,但又不能是像工厂方法模式那样的创建型模式,因为三个灯从始至终都是没有改变的。
这里我们考虑把每种颜色的灯表达为一种状态
image.png
仔细看方案三和方案二的类图差别
在方案三的State.display(Context)方法中有一个Context对象作为参数传递,这表示的是在display()方法中利用Context改变当前交通灯的状态。另外,这也带来了Context类和ITrafficLightState层次类的双向依赖
交通灯的状态切换具体而言是在display()方法中加入以下语句:

//在具体的状态子类中告诉环境对象Context,下一个状态是谁。
context.changeCurrentSignal(new RedLightState());

交通灯接口设计:

public interface ITrafficLightState {
void display(Context context); // 反向关联到Context,取得系统的上下文环境
}

Context类的设计:相比于方案二,changeSignal()方法中具体切换代码从Client类移动到了State类的display()方法中

public class Context {
	private ITrafficLightState currentState;
	public Context() {
		this.currentState = new RedLightState();
	}
	public void showSignal() {
		if (currentState != null) {
			currentState.display(this); // this表示当前context对象
		}
	}
	public void changeCurrentSignal(ITrafficLightState currentState) {
		this.currentState = currentState;
	}
}

Client类的设计:

public class Client {
	public static void main(String[] args) {
		Context context = new Context();
		context.showSignal();
		context.showSignal();
		context.showSignal();
	}
}

状态模式理论

定义:允许状态对象在其内部状态发生改变时改变其行为,通过将抽象有状态的对象,将复杂的状态改变“判断逻辑”提取到不同状态对象中实现
image.png
优点

  1. 解决switch-case、if-else带来的难以维护的问题
  2. 代码结构清晰,提高了扩展性

缺点

  1. 状态扩展导致状态类数量增多
  2. 增加了系统复杂度,使用不当将会导致逻辑的混乱
  3. 不完全满足开闭原则,增加或者删除状态类时,需要修改涉及到的状态转移逻辑和对应的类

应用场景
一个操作的判断逻辑/行为取决于对象的内部状态时

状态模式代码优化

结合享元模式

对象重复创建问题
每次状态切换都需要创建一个新的状态对象,而事实上一个状态对象完全可以只用一个枚举值标识,这带来巨大的额外资源开销。
解决方法
单例模式
享元模式

享元模式代码示例:新增一个Factory创建状态对象的工厂类,在这个Factory类中维护着一个现有的ITrafficLightState状态层次类Map,State类在需要new状态对象时,调用Factory的getTrafficLight方法,如果维护的map中有该类对象,则直接返回;如果没有,则创建一个新的状态对象返回。由此来减少状态模式中的对象重复创建

public class TrafficLightStateFactory {  //享元模式
	// 共享Map
	private static Map<Class, ITrafficLightState> lights = new HashMap();
	public static ITrafficLightState getTrafficLight(Class key) {
		if(!(lights.containsKey(key))) {
			try {
				lights.put(key, (ITrafficLightState) key.getDeclaredConstructor().newInstance());
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		return lights.get(key);
	}
}

ConcreteState类的对应修改

public class GreenLightState implements ITrafficLightState {
	@Override
	public void display(Context context) throws Exception {
		System.out.println("绿灯行");
		context.changeCurrentSignal(TrafficLightStateFactory.getTrafficLight(YellowLightState.class));
	}
}

并发问题解决

由于状态是单例的,可以在多个上下文之间共享。若状态类中持有其他资源就有产生并发问题的可能
于是,我们可以看在前面的方案三设计中,State层次类中对Context类的依赖是来自display()方法的参数,而没有通过属性的方法持有Context对象的引用

策略模式 VS 状态模式

image.png
说明:策略模式持有Context对象一般是需要使用context对象中的数据或方法,如H5所述的使用Context对象的计时方法。

文章来源:https://blog.csdn.net/m0_61819793/article/details/135309996
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。