MLDN
课程咨询[孔老师]QQ 1031143579孔老师QQ课程咨询留言 课程咨询[徐老师 ]QQ 945313230
徐老师QQ课程咨询留言
课程咨询[刘老师 ]QQ 514543793
刘老师QQ课程咨询留言
java培训
北京java培训
IT电子教育门户 高端JAVA培训 -=> 资源中心 -=> JAVA SE技术专区 -=> JAVA设计模式 -=> 正文

State模式在客户端软件中的应用

 发布日期:2009-3-9 9:11:00 发布者:[IT电子教育门户]   评论:[]  浏览:

在对一个J2EE项目的重构、增加新功能的过程中,对客户端GUI程序,我们使用了State模式。结果显示,该模式的使用,不但减少了客户端GUI程序的程序规模(LOC),而且,该部分的开发及单元测试时间大大减少,同时,在集成测试中发现的缺陷数量比使用该模式前平均减少了3倍。本文就该项目中使用State模式的方式进行介绍。

  引言

  在分层软件体系结构中,服务端程序关注于实现业务逻辑,客户端程序则包含用户界面。服务端程序由客户端程序调用,其请求、响应模式在设计时已经确定,运行时出现问题的概率较小。相反,客户端程序与用户直接交互,虽然有正确规定的操作顺序或模式,但是用户的操作是不可预知的,程序必须处理各种操作错误、加上数据输入有效验证等要求,使得客户端程序的开发成本上升。

  因而,一旦有经过充分测试的、甚至是通过验收的用户交互程序GUI,应该尽可能的重用该GUI,以提高软件的可靠性、可维护性。

  在对一个J2EE项目的重构、增加新功能的过程中,对客户端GUI程序,我们使用了State模式。结果显示,该模式的使用,不但减少了客户端GUI程序的程序规模(LOC),而且,该部分的开发及单元测试时间大大减少,同时,在集成测试中发现的缺陷数量比使用该模式前平均减少了3倍。本文就该项目中使用State模式的方式进行介绍。

  1. State模式

  首先,先简单介绍一下State模式。

  该模式是指在对象的内部状态改变时改变对象的行为【1】。
  模式中各个参与者职责简介如下:

  Context:用户对象,拥有一个State类型的成员,以标识对象的当前状态;

  State:接口或基类,封装与Context的特定状态相关的行为;

  ConcreteState:接口实现类或子类,实现了一个与Context某个状态相关的行为。

  运行时,Context将与状态相关的请求委托给当前的ConcreteState对象处理。关于State模式更详尽的介绍,请参阅参考文献1。

  2. 客户端应用

  本模式的目标是分离客户端软件中的变化部分与不变部分,以使得变化的部分可独立于不变的部分,有利于扩充新的功能,也有利于维护。

  在项目中,对于客户端GUI的重用有两种方式。

  方式1适用于:相同数据集合,不同操作模式;此时,在GUI中定义客户端数据处理验证逻辑,不同的状态对象封装了不同的操作模式;

  方式2适用于:不同数据集合,相同操作模式;此时,在状态对象中定义客户端数据处理验证逻辑,不同的状态对象封装了不同的数据集合操作。
2.1 类型1: Read-Only & Normal

  2.1.1 动机

  客户端GUI接受用户输入,经过数据有效性验证,然后将数据传输到服务端,服务端检查业务逻辑有效性,保存数据;但是在特定情况下(比如数据已经存在、且只能经历一次输入),依据客户端GUI所操作数据的状态,业务逻辑要求数据为只读,即客户端只具有显示数据的功能,并不能改变服务器上的数据。

  一般地,编程实现时,会在GUI程序中加入判断数据是否应该为只读的逻辑判断语句。如果针对相同的数据集合(Model),有多个客户端GUI(View),就需要在每个程序中加入该判断。如此降低了程序的可维护性,决定数据是否为只读的这样一个业务逻辑将分散在程序中多处,不易维护,在业务逻辑发生改变时,易造成不一致。

  我们可以将变化部分(在Normal和Read-Only状态下不同的部分)从GUI中抽取出来,分别用不同的类来表示,这样,当GUI的状态发生改变时,只需要改变其对状态对象的引用即可,状态相关操作委托给状态对象去完成。

  2.1.2 适用性

  本类型适用环境:相同数据集合,不同操作模式。即特定的GUI根据操作上下文环境,改变其响应模式,根据数据是否可编辑,分为两种状态Read-Only State、 Normal State。
  

  2.1.4 参与者

  ClientStateChangeable:客户端GUI提供给State对象的操作接口,使得State对象可以访问GUI的成员,以完成该状态相关操作;

  getChangeableComponents():返回在状态转换为Read-Only时,不可编辑的控件Collection;

  saveChangeToServer():在可编辑状态时,将客户端数据保存到服务端。

  AClientGUI:完成图1中的Context功能,即其状态要进行改变的客户端GUI;维护ClientState的一个实例(state);

  init():将this引用作为参数,调用state.setComponents(this) 以设置可变控件的初始状态;

  okButtonActionPerformed(e:ActionEvent):用户完成操作后,本方法可作为 "OK"按钮控件的ActionListener方法,调用state.action(this) 以完成本操作。

  ClientState:State接口,封装与客户端GUI某个特定状态相关的行为;

  setComponents(gui:ClientStateChangeable):设置参数gui指定的客户端GUI的控件状态;

  action(gui:ClientStateChangeable):完成数据保存功能。

  ClientNormalState:可编辑状态子类,实现ClientState接口;

  setComponents(gui:ClientStateChangeable):调用参数gui指定的客户端GUI的getChangeableComponents()获取控件Collection,然后遍历设置各控件状态为可编辑;

  action(gui:ClientStateChangeable):调用参数gui指定的客户端GUI的saveChangeToServer()完成数据保存功能。

  ClientReadOnlyState:只读状态子类,实现ClientState接口;

  setComponents(gui:ClientStateChangeable):调用参数gui指定的客户端GUI的getChangeableComponents()获取控件Collection,然后遍历设置各控件状态为ReadOnly;

  action(gui:ClientStateChangeable):空方法,本状态下无数据变化,无须保存。

  2.1.5 代码示例

1  ClientStateChangeable接口:
2
3   public interface ClientStateChangeable {
4
5   /**
6
7   * To get all changeable components in the GUI object.
8
9   * @return Collection contains Component objects.
10
11   */
12
13   Collection getChageableComponents();
14
15   /**
16
17   * To save data to server.
18
19   */
20
21   void saveChangeToServer();
22
23   }
24
25
  AClientGUI类:

1 public class AClientGUI extends JPanel implements ClientStateChangeable{
2
3   //…
4
5   private ClientState state = null;
6
7   public DesignStep1View(ClientState state){
8
9   //…
10
11   this.state = state;
12
13   this.state.setComponents(this);
14
15   }
16
17   private void okButton_actionPerformed(ActionEvent e){
18
19   state.action(this); //save data
20
21   }
22
23   public Collection getChageableComponents() {
24
25   Collection dataComponents = new ArrayList();
26
27   dataComponents.add(jComboBoxESEPattern);
28
29   //…
30
31   return dataComponents;
32
33   }
34
35   public void saveChangeToServer() {
36
37   //…
38
39   }
40
41   }
42
43
  ClientState接口:

1 public interface ClientState {
2 /**
3 * To set components' state in the GUI. * @param gui a GUI object which implements StateChangeable interface.
4 */
5 void setComponents(ClientStateChangeable gui);
6 /**
7 * when user click OK-Button in a GUI, the GUI will call this method.
8 * @param gui a GUI object which implements StateChangeable interface.
9 */
10 void action(ClientStateChangeable gui);
11 }
12
13
  ClientNormalState类:

1 public class ClientNormalState implements ClientState {
2
3   /**
4
5   * 正常状态下, 各个控件默认为可编辑的, 所以不用做任何更改
6
7   */
8
9   public void setComponents(ClientStateChangeable gui) {}
10
11   /**
12
13   * 正常状态下, 需要将用户所作修改保存到服务端
14
15   */
16
17   public void action(ClientStateChangeable gui) {
18
19   gui.saveChangeToServer();
20
21   }
22
23   }
24
25
  ClientReadOnlyState类:

1 public class ClientReadOnlyState implements ClientState {
2
3   /**
4
5   * 设置GUI的数据控件为Read-Only
6
7   */
8
9   public void setComponents(ClientStateChangeable gui) {
10
11   Collection components = gui.getChageableComponents();
12
13   Iterator iter = components.iterator();
14
15   while(iter.hasNext()){
16
17   JComponent jc = (JComponent)iter.next();
18
19   jc.setEnabled(false);
20
21   String toolTip = jc.getToolTipText();
22
23   String addedTip = "只读状态";
24
25   if(toolTip == null)toolTip = addedTip;
26
27   else toolTip += ". " + addedTip;
28
29   jc.setToolTipText(toolTip);
30
31   }
32
33   }
34
35   /**
36
37   * GUI处于Read-Only状态, 无需将数据保存到server端
38
39   */
40
41   public void action(ClientStateChangeable gui) {}
42
43   }
2.2 类型2:(Reuse GUI)

  2.2.1 动机

  当多个客户端GUI布局、控件类型很相似,所完成的任务也相似时,只需要经过精心设计,将这些GUI的展示形式统一起来,同一个GUI可以用到多个场景中,达到重用的目的。此时,这些不同任务需要操作不同的数据集合。

  可以在GUI类中实现这些不同数据集合的操作,但是这会给程序维护带来麻烦。首先,属于不同逻辑的数据操作出现在同一类文件中,造成逻辑混乱、程序规模增大,不易于调试;其次,要将GUI用于新的数据集合时,只能在相同文件中增加新的代码,此时,该程序的可维护性降低,尤其是新的工作由其他程序员完成时,要理解原有代码是很费力的。

  和2.1.1节中提到的解决方法类似:将变化的部分和不变部分分离开来,使得变化的部分可以独立修改、扩充。具体地,则是将数据集合相关操作从GUI程序中抽取出来,定义一个所有数据集合操作的接口(即:状态接口),不同地数据集合操作作为该接口的一个实现类存在。这样,每个数据集合都独立的封装于一个状态对象内;而且,要对新的数据使用该GUI,只需要定义新的状态接口实现类即可,无须修改已有类,甚至不关心已有的状态。

  2.2.2 适用性

  本类型适用环境:不同的数据集合,相同的操作模式。即不变化的客户端GUI,将不同的数据集合操作委托给变化的状态对象去完成。

  2.2.4 参与者

  InvariableGUI:本身不发生变化的客户端GUI类,维护一个对VariableDataState的引用state,将数据相关操作委托给该引用对象 ;

  saveChangeToServer():调用state.processData(this)完成数据相关操作;

  VariableDataState:State接口,封装与数据相关操作的行为;

  ProcessData(gui:InvariableGUI):调用参数gui的成员获取数据并完成处理;

  DataState1、DataState2、 … … DataStateN:状态子类,实现VariableDataState接口,封装了特定某一类数据集合的操作,可以根据不同的数据集合定义多个VariableDataState接口的状态类,从而实现了对InvariableGUI的重用。

  本类型的实现代码在这里就不列出了,参照2.1.5节中的代码,很容易的就可实现本类型的结构。

  2.3 综合以上两种类型

  可以将以上两种类型结合起来使用,即实现了客户端软件的数据集合方面对GUI的重用,也实现了操作模式方面对GUI的重用。

  程序实现时,可以由GUI类分别维护一个ClientState的引用和一个VariableDataState的引用:

  初始化GUI类时,GUI的构造器调用ClientState对象的setComponents()方法设置控件的状态;

  用户提交操作时,GUI调用ClientState对象的action()方法,如图2所示,该方法使用传递的gui参数回调GUI的saveChangeToServer()方法,而saveChangeToServer()方法则按照图3所示,调用VariableDataState引用的状态对象的processData()方法完成数据操作。

  3 总结

  本文介绍的State模式应用于多类型数据、多操作模式的客户端软件,可以取得明显的效果;但如果客户端类和状态都很少时,使用本模式,反而增加了客户端类数量,增加了体系结构的复杂性,此时,可以使用继承方式的类体系来实现重用,无须使用State状态对象的委托操作和回调操作。

java视频教程
JAVA核心_75对象序列化练习
 JAVA核心_75对象序列化..
JAVA核心_74对象序列化
 JAVA核心_74对象序列化..
JAVA核心_73字符编码
 JAVA核心_73字符编码 ..
JAVA核心_72Scanner
 JAVA核心_72Scann..
JAVA核心_71 IO练习 二
 JAVA核心_71 IO练习..
相关文章 推荐文章
设计模式之State[3.18]
深入浅出单实例Singleton设计模..
Factory模式的优点
反模式可以如何提高编程
State模式在客户端软件中的应用
Flyweight(享元)模式
热门文章
MVC模式和Struts模式的理解
Java中的代理模式
JAVA设计模式之事务处理
使用Java 动态代理实现AOP
用实例学习MVC模式
Java软件架构设计概论
解析MVC模式
Java设计模式之State
怎样成为优秀的软件模型设计者?
Factory模式的优点
今日更新
深入浅出单实例Singleton设计模..
Factory模式的优点
反模式可以如何提高编程
State模式在客户端软件中的应用
Java开发下的设计模式简单说明
Flyweight(享元)模式
软件设计模式在JavaScript中的..
软件设计模式在JavaScript中的..
singleton的几种实现方式
DAO设计模式
JAVA招聘网
 评一评
正在读取…
  姓名:
  评论:
    
【注】 发表评论必需遵守以下条例: !!!
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款
关于我们 | 商务合作 | 招聘信息 | 客服中心 | 服务条款 | 免责声明 | 网站导航 QQ留言
Copyright 2009 魔乐培训MLDN.CN all rights reserved 版权所有 京ICP备07008611号