当前位置: 首页 > news >正文

图解设计模式【3】

State模式

State模式中,用类来表示状态。以类来表示状态就可以通过切换类来方便地改变对象的状态。当需要增加新的状态时,如何修改代码这个问题也会很明确。

示例

设计一个简单的报警系统,每秒会改变一次状态。

金库报警系统
- 有一个金库,金库与报警中心相连,金库里有警铃和正常通话用的电话。金库里有时钟,监视着现在的时间。
- 白天的时间范围时9:00-16:59,晚上的时间范围是17:00-23:59和0:00-8:59。
- 金库只能在白天使用。白天使用金库的话,会在报警中心留下记录;晚上使用金库的话,会向报警中心发送紧急事态通知。
- 任何时候都可以使用警铃。使用警铃的话,会向警报中心发送紧急事态通知。
- 任何时候都可以使用警铃。使用警铃的话,会向警报中心发送紧急事态通知。
- 任何时候都可以使用电话(晚上只有留言电话)。白天使用电话的话,会呼叫报警中心。晚上使用电话的话,会呼叫报警中心的留言电话。

如果不使用State模式,我们可以使用如下的伪码逻辑

警报系统的类{使用金库时被调用的方法(){if (白天) {向警报中心报告使用记录} else if (晚上){向警报中心报告紧急事态}警铃响起时被调用的方法(){向警报中心报告紧急事态}正常通话时被调用的方法(){if (白天){呼叫报警中心} else if (晚上){呼叫警报中心的留言电话}}
}

使用了State模式的伪代码

表示白天的状态的类{使用金库时被调用的方法(){向警报中心报告使用记录}警铃响起时被调用的方法(){向警报中心报告紧急事态}正常通话时被调用的方法(){呼叫警报中心}
}
表示晚上的状态的类{使用金库时被调用的方法(){向警报中心报告紧急事态}警铃响起时被调用的方法(){向警报中心报告紧急事态}正常通话时被调用的方法(){呼叫警报中心的留言电话}
}

两者的区别在于,使用State模式,不需要用if语句判断是白天还是晚上。

«abstract»
Context
setClock()
changeState()
callSecurityCenter()
recordLog()
SafeFrame
-state
setClock()
changeState()
callSecurityCenter()
recordLog()
«interface»
State
doClock()
doUse()
doAlarm()
doPhone()
DayState
-singleton$
-DayState()
getInstance()
doClock()
doUse()
doAlarm()
doPhone()
NightState
-singleton$
-NightState()
getInstance()
doClock()
doUse()
doAlarm()
doPhone()

State接口时表示金库状态的接口。其中包括设置时间、使用金库、按下警铃、正常通话等API

public interface State {public abstract void doClock(Context context, int hour);    // 设置时间public abstract void doUse(Context context);                // 使用金库public abstract void doAlarm(Context context);              // 按下警铃public abstract void doPhone(Context context);              // 正常通话
}

DayState类表示白天的状态。该类实现了State接口,因此还实现了State接口中声明的所有方法。

public class DayState implements State {private static DayState singleton = new DayState();private DayState() {                                // 构造函数的可见性是private}public static State getInstance() {                 // 获取唯一实例return singleton;}public void doClock(Context context, int hour) {    // 设置时间if (hour < 9 || 17 <= hour) {context.changeState(NightState.getInstance());}}public void doUse(Context context) {                // 使用金库context.recordLog("使用金库(白天)");}public void doAlarm(Context context) {              // 按下警铃context.callSecurityCenter("按下警铃(白天)");}public void doPhone(Context context) {              // 正常通话context.callSecurityCenter("正常通话(白天)");}public String toString() {                          // 显示表示类的文字return "[白天]";}
}

NightState类表示晚上的状态。它与DateState类一样,也是用了Singleton模式。

public class NightState implements State {private static NightState singleton = new NightState();private NightState() {                              // 构造函数的可见性是private}public static State getInstance() {                 // 获取唯一实例return singleton;}public void doClock(Context context, int hour) {    // 设置时间if (9 <= hour && hour < 17) {context.changeState(DayState.getInstance());}}public void doUse(Context context) {                // 使用金库context.callSecurityCenter("紧急:晚上使用金库!");}public void doAlarm(Context context) {              // 按下警铃context.callSecurityCenter("按下警铃(晚上)");}public void doPhone(Context context) {              // 正常通话context.recordLog("晚上的通话录音");}public String toString() {                          // 显示表示类的文字return "[晚上]";}
}

Context接口是负责管理状态和联系警报中心的接口。

public interface Context {public abstract void setClock(int hour);                // 设置时间public abstract void changeState(State state);          // 改变状态public abstract void callSecurityCenter(String msg);    // 联系警报中心public abstract void recordLog(String msg);             // 在警报中心留下记录
}

SafeFrame类是使用GUI实现警报系统界面的类,它实现了Context接口。

import java.awt.Frame;
import java.awt.Label;
import java.awt.Color;
import java.awt.Button;
import java.awt.TextField;
import java.awt.TextArea;
import java.awt.Panel;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;public class SafeFrame extends Frame implements ActionListener, Context {private TextField textClock = new TextField(60);        // 显示当前时间private TextArea textScreen = new TextArea(10, 60);     // 显示警报中心的记录private Button buttonUse = new Button("使用金库");      // 金库使用按钮private Button buttonAlarm = new Button("按下警铃");    // 按下警铃按钮private Button buttonPhone = new Button("正常通话");    // 正常通话按钮private Button buttonExit = new Button("结束");         // 结束按钮private State state = DayState.getInstance();           // 当前的状态// 构造函数public SafeFrame(String title) {super(title);setBackground(Color.lightGray);setLayout(new BorderLayout());//  配置textClockadd(textClock, BorderLayout.NORTH);textClock.setEditable(false);// 配置textScreenadd(textScreen, BorderLayout.CENTER);textScreen.setEditable(false);// 为界面添加按钮Panel panel = new Panel();panel.add(buttonUse);panel.add(buttonAlarm);panel.add(buttonPhone);panel.add(buttonExit);// 配置界面add(panel, BorderLayout.SOUTH);// 显示pack();show();// 设置监听器buttonUse.addActionListener(this);buttonAlarm.addActionListener(this);buttonPhone.addActionListener(this);buttonExit.addActionListener(this);}// 按钮被按下后该方法会被调用public void actionPerformed(ActionEvent e) {System.out.println(e.toString());if (e.getSource() == buttonUse) {           // 金库使用按钮state.doUse(this);} else if (e.getSource() == buttonAlarm) {  // 按下警铃按钮state.doAlarm(this);} else if (e.getSource() == buttonPhone) {  // 正常通话按钮state.doPhone(this);} else if (e.getSource() == buttonExit) {   // 结束按钮System.exit(0);} else {System.out.println("?");}}// 设置时间public void setClock(int hour) {String clockstring = "现在时间是";if (hour < 10) {clockstring += "0" + hour + ":00";} else {clockstring += hour + ":00";}System.out.println(clockstring);textClock.setText(clockstring);state.doClock(this, hour);}// 改变状态public void changeState(State state) {System.out.println("从" + this.state + "状態变为了" + state + "状态。");this.state = state; // 给代表状态的字段赋予表示当前状态的类的实例,就相当于进行了状态迁移。}// 联系警报中心public void callSecurityCenter(String msg) {textScreen.append("call! " + msg + "\n");}// 在警报中心留下记录public void recordLog(String msg) {textScreen.append("record ... " + msg + "\n");}
}

Main类生成了一个SafeFrame类的实例,并且每秒钟调用一次setClock方法。

public class Main {public static void main(String[] args) {SafeFrame frame = new SafeFrame("State Sample");while (true) {for (int hour = 0; hour < 24; hour++) {frame.setClock(hour);   // 设置时间try {Thread.sleep(1000);} catch (InterruptedException e) {}}}}
}

解析

  • State

    State表示状态,定义了根据不同状态进行不同处理的API。该API是那些处理内容依赖于状态的方法的集和。

  • ConcreteState

    ConcreteState表示各个具体的状态,实现了State接口。

  • Context

    Context持有表示当前状态的ConcreteState。它还定义了提供外部调用者使用State模式的API。

Context
state
requestX()
requestY()
requestZ()
«abstract»
State
methodA()
methodB()
methodC()
methodD()
ConcreteState1
methodA()
methodB()
methodC()
methodD()
ConcreteState2
methodA()
methodB()
methodC()
methodD()

编程时,我们经常使用分而治之的方针。这种方针非常适用于大规模的复杂处理。当遇到庞大且复杂的问题,不能用一般的方法解决时,我们会将该问题分解为多个小问题。在State模式中,我们用类来表示状态,并为每一种具体的状态都定义一个相应的类。

State模式易于增加新的状态,但是在State模式中增加其他“依赖于状态的处理”是很困难的。

Flyweight模式

Flyweight是轻量级的意思。关于Flyweight模式,一言以蔽之就是“通过尽量共享实例来避免new出实例“。

示例

Uses
creates
Uses
Uses
BigChar
-charname
-fontdate
print()
BigCharFactory
-pool
-singleton$
-BigCharFactory()
getInstacne()
getBigChar()
BigString
-bigchars
print()
Main

BigChar表示大型字符类。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;public class BigChar {// 字符名字private char charname;// 大型字符对应的字符串(由'#' '.' '\n'组成)private String fontdata;// 构造函数public BigChar(char charname) {this.charname = charname;try {BufferedReader reader = new BufferedReader(new FileReader("big" + charname + ".txt"));String line;StringBuffer buf = new StringBuffer();while ((line = reader.readLine()) != null) {buf.append(line);buf.append("\n");}reader.close();this.fontdata = buf.toString();} catch (IOException e) {this.fontdata = charname + "?";}}// 显示大型字符public void print() {System.out.print(fontdata);}
}

BigCharFactory类是生成BigChar类的实例的工厂。它实现了共享实例的功能。

import java.util.HashMap;public class BigCharFactory {// 管理已经生成的BigChar的实例private HashMap pool = new HashMap();// Singleton模式private static BigCharFactory singleton = new BigCharFactory();// 构造函数private BigCharFactory() {}// 获取唯一的实例public static BigCharFactory getInstance() {return singleton;}// 生成(共享)BigChar类的实例public synchronized BigChar getBigChar(char charname) {BigChar bc = (BigChar)pool.get("" + charname);if (bc == null) {bc = new BigChar(charname); // 生成BigChar的实例pool.put("" + charname, bc);}return bc;}
}

BigString类是由BigChar组成的大型字符串的类。

public class BigString {// “大型字符”的数组private BigChar[] bigchars;// 构造函数public BigString(String string) {bigchars = new BigChar[string.length()];BigCharFactory factory = BigCharFactory.getInstance();for (int i = 0; i < bigchars.length; i++) {bigchars[i] = factory.getBigChar(string.charAt(i));}}// 显示public void print() {for (int i = 0; i < bigchars.length; i++) {bigchars[i].print();}}
}

Main类

public class Main {public static void main(String[] args) {if (args.length == 0) {System.out.println("Usage: java Main digits");System.out.println("Example: java Main 1212123");System.exit(0);}BigString bs = new BigString(args[0]);bs.print();}
}

解析

  • Flyweight

    按照通常方式编写程序会导致程序变重,所以如果能够共享实例会比较好,而Flyweight表示的就是那些实例会被共享的类。

  • FlyweightFactory

    FlyweightFactory是生成Flyweight的工厂。在工厂中生成Flyweight可以实现共享实例。

  • Client

    Client使用FlyweightFactory来生成Flyweight。

Uses
Uses
Creates
Flyweight
methodA()
methodB()
FlyweightFactory
pool
getFlyweight()
Client

Flyweight模式的主题是共享。在共享实例时,要想到“如果要改变被共享的对象,就会对多个地方产生影响”。在决定Flyweight中的字段时,需要精挑细选。只将那些真正应该在多个地方共享的字段定义在Flyweight中即可。应当共享的信息叫做Intrinsic信息,不应当共享的信息被称作Extrinsic信息

—————————————未完待续———————————————————

Reference

图解设计模式 【日】结成浩 著 杨文轩 译


文章转载自:

http://jgv7Vxox.fLLfz.cn
http://xbzU5QaF.fLLfz.cn
http://KcV1yDnh.fLLfz.cn
http://Fzy1osET.fLLfz.cn
http://vIcR8cLu.fLLfz.cn
http://vPEfrS5b.fLLfz.cn
http://LcQhYACm.fLLfz.cn
http://PtfSZ8rd.fLLfz.cn
http://ScF7I2Sv.fLLfz.cn
http://HC340qwN.fLLfz.cn
http://7mEls7jX.fLLfz.cn
http://nTzpJLkl.fLLfz.cn
http://iPplfv2K.fLLfz.cn
http://iqtxPPbv.fLLfz.cn
http://F8Z1YnI1.fLLfz.cn
http://vgw1K12T.fLLfz.cn
http://q7BmZOR5.fLLfz.cn
http://cDENb7VN.fLLfz.cn
http://P86ubYbg.fLLfz.cn
http://1Bjx7Mwt.fLLfz.cn
http://18VE2SGE.fLLfz.cn
http://zFmBgNbC.fLLfz.cn
http://91af6MGi.fLLfz.cn
http://EreFJ04o.fLLfz.cn
http://LelxmO2m.fLLfz.cn
http://JkkKnRSJ.fLLfz.cn
http://TsNtkQ5z.fLLfz.cn
http://e2fi1Bz4.fLLfz.cn
http://omrSyDY2.fLLfz.cn
http://JcJc61lR.fLLfz.cn
http://www.dtcms.com/a/376434.html

相关文章:

  • java 将pdf转图片
  • ES(springcloud笔记第五期)
  • Day40 Web服务器原理与C语言实现:从HTTP协议到静态资源服务
  • 利用FFmpeg自动批量处理m4s文件
  • [iOS] ViewController 的生命周期
  • MySQL 核心文件解析:从配置到存储的 “说明书 + 记录仪” 系统
  • 一文了解大模型压缩与部署
  • Jenkins 构建 Node 项目报错解析与解决——pnpm lockfile 问题实战
  • Wazuh 研究记录 | 开源XDR | 安全基线检测
  • 配电网故障诊断与自愈控制工具的智慧能源开源了
  • [邮件服务器core] 安全通信(SSL/TLS) | OpenSSL库管理 | 服务端安全SECURITY.md
  • Workers API 实战教程:45 秒完成 CI/CD 云函数部署
  • MySQL收集processlist记录的shell工具mysql_collect_processlist
  • 计算机毕业设计 基于Hadoop的健康饮食推荐系统的设计与实现 Java 大数据毕业设计 Hadoop毕业设计选题【附源码+文档报告+安装调试】
  • 【nginx基础】Nginx安装指南:CentOS 7.9源码编译安装Nginx 1.28.0完整指南
  • ShardingJDBC实战指南
  • 数据库--MySQL数据管理
  • Java全栈学习笔记33
  • 网络学习笔记
  • GitHub每日最火火火项目(9.10)
  • 基于stm32的环境监测系统/智能家居/空气质量监测系统
  • 基于PyQt5和阿里云TTS的语音合成应用开发实战[附源码】
  • Linux的V4L2视频框架学习笔记
  • Android studio安装教程——超详细(含安装包安装教程)
  • 如何将大型音频文件从 iPhone 发送到不同的设备
  • 使用阿里云容器镜像服务 ACR
  • ffmpeg内存模型
  • Android面试指南(八)
  • 不止是进度条:深入PiXSingleGUI的TpSlideProgressBar组件架构设计​
  • Flutter 视频播放器——flick_video_player 介绍与使用