软件设计模式学习笔记(九)
时间:2022-10-07 08:00:00
行为型模式
1. 状态模式
1.1 概述
例通过按钮控制电梯的状态。电梯具有开门、关门、停止和运行状态。每种状态变化都可以根据其他状态进行更新。例如,如果电梯门处于运行状态,则无法开门,如果电梯门处于停止状态,则可以开门。
类图如下:
代码如下:
public interface ILift {
//电梯4个状态 ///开门状态 public final static int OPENING_STATE = 1; ///关门状态 public final static int CLOSING_STATE = 2; //运行状态 public final static int RUNNING_STATE = 3; //停止状态 public final static int STOPPING_STATE = 4; //设置电梯状态 public void setState(int state); ///电梯动作 public void open(); public void close(); public void run(); public void stop(); } public class Lift implements ILift {
private int state; @Override public void setState(int state) {
this.state = state; } //执行关门动作 @Override public void
close
(
)
{
switch
(
this
.state
)
{
case OPENING_STATE
:
System
.out
.
println
(
"电梯关门了。。。"
)
;
//只有开门状态可以关闭电梯门,可以对应电梯状态表来看
this
.
setState
(CLOSING_STATE
)
;
//关门之后电梯就是关闭状态了
break
;
case CLOSING_STATE
:
//do nothing //已经是关门状态,不能关门
break
;
case RUNNING_STATE
:
//do nothing //运行时电梯门是关着的,不能关门
break
;
case STOPPING_STATE
:
//do nothing //停止时电梯也是关着的,不能关门
break
;
}
}
//执行开门动作
@Override
public
void
open
(
)
{
switch
(
this
.state
)
{
case OPENING_STATE
:
/
/门已经开了,不能再开门了
//do nothing
break
;
case CLOSING_STATE
:
/
/关门状态,门打开
:
System
.out
.
println
(
"电梯门打开了。。。"
)
;
this
.
setState
(OPENING_STATE
)
;
break
;
case RUNNING_STATE
:
//do nothing 运行时电梯不能开门
break
;
case STOPPING_STATE
:
System
.out
.
println
(
"电梯门开了。。。"
)
;
//电梯停了,可以开门了
this
.
setState
(OPENING_STATE
)
;
break
;
}
}
//执行运行动作
@Override
public
void
run
(
)
{
switch
(
this
.state
)
{
case OPENING_STATE
:
/
/电梯不能开着门就走
//do nothing
break
;
case CLOSING_STATE
:
/
/门关了,可以运行了
System
.out
.
println
(
"电梯开始运行了。。。"
)
;
this
.
setState
(RUNNING_STATE
)
;
//现在是运行状态
break
;
case RUNNING_STATE
:
//do nothing 已经是运行状态了
break
;
case STOPPING_STATE
:
System
.out
.
println
(
"电梯开始运行了。。。"
)
;
this
.
setState
(RUNNING_STATE
)
;
break
;
}
}
//执行停止动作
@Override
public
void
stop
(
)
{
switch
(
this
.state
)
{
case OPENING_STATE
:
//开门的电梯已经是是停止的了(正常情况下)
//do nothing
break
;
case CLOSING_STATE
:
/
/关门时才可以停止
System
.out
.
println
(
"电梯停止了。。。"
)
;
this
.
setState
(STOPPING_STATE
)
;
break
;
case RUNNING_STATE
:
/
/运行时当然可以停止了
System
.out
.
println
(
"电梯停止了。。。"
)
;
this
.
setState
(STOPPING_STATE
)
;
break
;
case STOPPING_STATE
:
//do nothing
break
;
}
}
}
public
class
Client
{
public
static
void
main
(
String
[
] args
)
{
Lift lift
=
new
Lift
(
)
; lift
.
setState
(
ILift
.STOPPING_STATE
)
;
//电梯是停止的 lift
.
open
(
)
;
//开门 lift
.
close
(
)
;
//关门 lift
.
run
(
)
;
//运行 lift
.
stop
(
)
;
//停止
}
}
问题分析:
- 使用了大量的switch…case这样的判断(if…else也是一样),使程序的可阅读性变差。
- 扩展性很差。如果新加了断电的状态,我们需要修改上面判断逻辑
定义:
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
1.2 结构
状态模式包含以下主要角色。
- 环境(Context)角色:也称为上下文,它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
- 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
- 具体状态(Concrete State)角色:实现抽象状态所对应的行为。
1.3 案例实现
对上述电梯的案例使用状态模式进行改进。类图如下:
代码如下:
//抽象状态类 public abstract class LiftState { //定义一个环境角色,也就是封装状态的变化引起的功能变化 protected Context context; public void setContext(Context context) { this.context = context; } //电梯开门动作 public abstract void open(); //电梯关门动作 public abstract void close(); //电梯运行动作 public abstract void run(); //电梯停止动作 public abstract void stop(); } //开启状态 public class OpenningState extends LiftState { //开启当然可以关闭了,我就想测试一下电梯门
开关功能 @Override public void open() { System.out.println("电梯门开启..."); } @Override public void close() { //状态修改 super.context.setLiftState(Context.closeingState); //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作 super.context.getLiftState().close(); } //电梯门不能开着就跑,这里什么也不做 @Override public void run() { //do nothing } //开门状态已经是停止的了 @Override public void stop() { //do nothing } } //运行状态 public class RunningState extends LiftState { //运行的时候开电梯门?你疯了!电梯不会给你开的 @Override public void open() { //do nothing } //电梯门关闭?这是肯定了 @Override public void close() { //虽然可以关门,但这个动作不归我执行 //do nothing } //这是在运行状态下要实现的方法 @Override public void run() { System.out.println("电梯正在运行..."); } //这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了 @Override public void stop() { super.context.setLiftState(Context.stoppingState); super.context.stop(); } } //停止状态 public class StoppingState extends LiftState { //停止状态,开门,那是要的! @Override public void open() { //状态修改 super.context.setLiftState(Context.openningState); //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作 super.context.getLiftState().open(); } @Override public void close() { //虽然可以关门,但这个动作不归我执行 //状态修改 super.context.setLiftState(Context.closeingState); //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作 super.context.getLiftState().close(); } //停止状态再跑起来,正常的很 @Override public void run() { //状态修改 super.context.setLiftState(Context.runningState); //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作 super.context.getLiftState().run(); } //停止状态是怎么发生的呢?当然是停止方法执行了 @Override public void stop() { System.out.println("电梯停止了..."); } } //关闭状态 public class ClosingState extends LiftState { @Override //电梯门关闭,这是关闭状态要实现的动作 public void close() { System.out.println("电梯门关闭..."); } //电梯门关了再打开,逗你玩呢,那这个允许呀 @Override public void open() { super.context.setLiftState(Context.openningState); super.context.open(); } //电梯门关了就跑,这是再正常不过了 @Override public void run() { super.context.setLiftState(Context.runningState); super.context.run(); } //电梯门关着,我就不按楼层 @Override public void stop() { super.context.setLiftState(Context.stoppingState); super.context.stop(); } } //环境角色 public class Context { //定义出所有的电梯状态 public final static OpenningState openningState = new OpenningState();//开门状态,这时候电梯只能关闭 public final static ClosingState closeingState = new ClosingState();//关闭状态,这时候电梯可以运行、停止和开门 public final static RunningState runningState = new RunningState();//运行状态,这时候电梯只能停止 public final static StoppingState stoppingState = new StoppingState();//停止状态,这时候电梯可以开门、运行 //定义一个当前电梯状态 private LiftState liftState; public LiftState getLiftState() { return this.liftState; } public void setLiftState(LiftState liftState) { //当前环境改变 this.liftState = liftState; //把当前的环境通知到各个实现类中 this.liftState.setContext(this); } public void open() { this.liftState.open(); } public void close() { this.liftState.close(); } public void run() { this.liftState.run(); } public void stop() { this.liftState.stop(); } } //测试类 public class Client { public static void main(String[] args) { Context context = new Context(); context.setLiftState(new ClosingState()); context.open(); context.close(); context.run(); context.stop(); } }
1.4 优缺点
1,优点:
- 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
- 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
2,缺点:
- 状态模式的使用必然会增加系统类和对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
- 状态模式对"开闭原则"的支持并不太好。
1.5 使用场景
- 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
- 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
2. 观察者模式
2.1 概述
定义:
又被称为发布-订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
2.2 结构
在观察者模式中有如下角色:
- Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
- Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
- ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
2.3 案例实现
【例】微信公众号
在使用微信公众号时,大家都会有这样的体验,当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了程序猿这个公众号。
类图如下:
代码如下:
定义抽象观察者类,里面定义一个更新的方法
public interface Observer {
void update(String message);
}
定义具体观察者类,微信用户是观察者,里面实现了更新的方法
public class WeixinUser implements Observer {
// 微信用户名
private String name;
public WeixinUser(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + "-" + message);
}
}
定义抽象主题类,提供了attach、detach、notify三个方法
public interface Subject {
//增加订阅者
public void attach(Observer observer);
//删除订阅者
public void detach(Observer observer);
//通知订阅者更新消息
public void notify(String message);
}
微信公众号是具体主题(具体被观察者),里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法
public class SubscriptionSubject implements Subject {
//储存订阅公众号的微信用户
private List<Observer> weixinUserlist = new ArrayList<Observer>();
@Override
public void attach(Observer observer) {
weixinUserlist.add(observer);
}
@Override
public void detach(Observer observer) {
weixinUserlist.remove(observer);
}
@Override
public void notify(String message) {
for (Observer observer : weixinUserlist) {
observer.update(message);
}
}
}
客户端程序
public class Client {
public static void main(String[] args) {
SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
//创建微信用户
WeixinUser user1=new WeixinUser("孙悟空");
WeixinUser user2=new WeixinUser(