运用适配器模式应对项目中的变化
2014-01-26 13:25
295 查看
在前一篇文章一个示例让你明白适配器模式中,详细介绍了适配器模式,本文以实际项目中遇到的问题来演示适配器模式的实际应用。
public class ESPMenu {
private String id;
private String caption;
private String normalIcon;
private String selectedIcon;
private String viewType;
private String icon
private String content;
public String getId() {
return id;
}
public String getCaption() {
return caption;
}
public String getNormalIcon() {
return normalIcon;
}
public String getViewType() {
return viewType;
}
public String getSelectedIcon() {
return selectedIcon;
}
public String getIcon() {
return icon;
}
public String getContent() {
return content;
}
}
从上面的代码可见,这是一个普通的实体类。 在项目中一个ESPMenu对象代表一个菜单项。这里的菜单是从后台中的XML中配置的。一个菜单项对应对应一个XML中的一个标签
<node id="my_task" caption="任务" selectedIcon="myTask.png" normalIcon="myTask_n.png"/>
这个标签和上面的ESPMenu对象表达的是相同的意思,都是表示一个菜单项,包括菜单的id,菜单显示的标题,显示的背景图片等信息。
目前,项目原有接口指的就是这个类了。这里要做一个说明:并不是只有实现一个interface才叫接口, 这里所说的接口是广义上的接口概念,能被外界访问到的部分都可以称作接口。 比如ESPMenu中有一个公共方法getIcon(), 这个方法就可以称作接口的一部分,因为它能被外界访问。
点击菜单项后, 使用webview来显示内容, 显示的内容来源用一个url指定。这就要求菜单项有以下扩展:
那么我就得在上面的ESPMenu中增加一个url字段。这是不可行的,因为增加一个字段没什么大不了, 但是每次扩展都要增加字段,这就毫无扩展性可言。所以后台提供了实现方式,用一个叫做StubObject的对象表示每个菜单项。这个对象是面向抽象的,使用基本的键值存储来描述菜单中的各个属性,不会有具体的字段名字。比如要获得菜单的标题,只需要调用getObject("caption"), 要获取url字段,只需调用getObject("url"), 使用一个getObject方法获取所有信息,只要传入对应的参数。
StubObject应该是我们公司的大牛写的,内部代码较多, 我们只关注一个方法就行了:
参数是Object类型的,返回值也是Object类型的,能适应所有的需求。
将XML菜单解析成Java对象的方法后台也实现好了, 我们回去XML后, 只需要调用一个解析的方法,XML就能解析成功, 解析成StubObject对象。
所以, 现在面临一个严重的问题, 我使用的接口是ESPMenu, 而后台提供的是StubObject。这就表示接口已经变了。
那就得该多个文件, 容易引起不一致和混乱。这不是一个好的对策。
那么怎样才能在不改变原有接口的情况下, 有能使用新的接口呢? 那就要使用适配器模式。使用适配器模式,需要做以下的修改。
1)将ESPMenu抽象成一个接口, 项目中已经使用过的方法,在接口中保持不变。并且扩展在这个接口中新加入一个getObject方法:
2) 为这一个接口编写一个实现类ESPMenuImpl, 这个实现类本质上就是一个适配器:
这个实现类在成员变量位置组合了一个StubObject对象, 也就是我们要使用的新接口。 并且将对就接口的调用, 都委托到对StubObject对象的调用。每个获取菜单属性的方法接口都没有改变,只是将调用都分发到StubObject对象的getObject方法,并且传入对应的key。
在该类的最后, 实现了ESPMenu中新加入的getObject方法,
同样将这个方法的调用委托给StubObject中getObject的方法。这样, StubObject的扩展性就传递到了ESPMenuImpl中, 使得ESPMenuImpl和StubObject具有同样的扩展性。
这样就完成了新旧接口的适配。项目上层中使用到的API没有改变,
只是将原来的ESPMenu类改成了ESPMenu接口。将ESPMenuImpl的访问权限设成包访问权限,那么对于上层代码,ESPMenuImpl就是不可见的,上层能使用的只能是ESPMenu接口, 不会涉及任何实现, 这样也就做到了比较好的封装性。
最后给出类图:
我为什么要学习Linux?中提到过。设计模式是个好东西,以后我肯定还会进一步的学习,并且在项目中多实践,提升自己的设计能力。
其实设计模式并不难,难的是真正领悟他的精妙,并且能灵活的运用于日常项目的开发。
项目中使用的原有接口
原来的项目中使用到了一个类ESPMenu,该类的代码很简单:public class ESPMenu {
private String id;
private String caption;
private String normalIcon;
private String selectedIcon;
private String viewType;
private String icon
private String content;
public String getId() {
return id;
}
public String getCaption() {
return caption;
}
public String getNormalIcon() {
return normalIcon;
}
public String getViewType() {
return viewType;
}
public String getSelectedIcon() {
return selectedIcon;
}
public String getIcon() {
return icon;
}
public String getContent() {
return content;
}
}
从上面的代码可见,这是一个普通的实体类。 在项目中一个ESPMenu对象代表一个菜单项。这里的菜单是从后台中的XML中配置的。一个菜单项对应对应一个XML中的一个标签
<node id="my_task" caption="任务" selectedIcon="myTask.png" normalIcon="myTask_n.png"/>
这个标签和上面的ESPMenu对象表达的是相同的意思,都是表示一个菜单项,包括菜单的id,菜单显示的标题,显示的背景图片等信息。
目前,项目原有接口指的就是这个类了。这里要做一个说明:并不是只有实现一个interface才叫接口, 这里所说的接口是广义上的接口概念,能被外界访问到的部分都可以称作接口。 比如ESPMenu中有一个公共方法getIcon(), 这个方法就可以称作接口的一部分,因为它能被外界访问。
需求的变更
随着项目的进行,越来越多的需求被提出。上面的菜单对象ESPMenu能表述的信息太少了,并且无法扩展。Java语言的动态性远不如Python和Ruby,Java只能动态的加载类,不能在运行时改变类的结构,而Python和Ruby能够在运行时改变类的结构。现在必须要在菜单中增加一些扩展信息,比如必须由这样的信息:点了菜单之后,如何显示这个菜单项指定的内容。举例来说,一个菜单项指定,点击菜单项后, 使用webview来显示内容, 显示的内容来源用一个url指定。这就要求菜单项有以下扩展:
<node id="xinwen" caption="新闻" normalIcon="/images/icons/mobile/myHome.png" viewType="webView" url="3g.sina.com.cn"/>
那么我就得在上面的ESPMenu中增加一个url字段。这是不可行的,因为增加一个字段没什么大不了, 但是每次扩展都要增加字段,这就毫无扩展性可言。所以后台提供了实现方式,用一个叫做StubObject的对象表示每个菜单项。这个对象是面向抽象的,使用基本的键值存储来描述菜单中的各个属性,不会有具体的字段名字。比如要获得菜单的标题,只需要调用getObject("caption"), 要获取url字段,只需调用getObject("url"), 使用一个getObject方法获取所有信息,只要传入对应的参数。
StubObject应该是我们公司的大牛写的,内部代码较多, 我们只关注一个方法就行了:
public Object getObject(Object Key) {
参数是Object类型的,返回值也是Object类型的,能适应所有的需求。
将XML菜单解析成Java对象的方法后台也实现好了, 我们回去XML后, 只需要调用一个解析的方法,XML就能解析成功, 解析成StubObject对象。
所以, 现在面临一个严重的问题, 我使用的接口是ESPMenu, 而后台提供的是StubObject。这就表示接口已经变了。
使用适配器模式应对需求变更
从上面可知随着项目的进行, 导致了接口的改变。但是我的前端工程中已经大量使用了ESPMenu对象, 大量调用了ESPMenu的方法,并且对ESPMenu的访问分散在不同的文件中。如果要把ESPMenu替换成StubObject,那就得该多个文件, 容易引起不一致和混乱。这不是一个好的对策。
那么怎样才能在不改变原有接口的情况下, 有能使用新的接口呢? 那就要使用适配器模式。使用适配器模式,需要做以下的修改。
1)将ESPMenu抽象成一个接口, 项目中已经使用过的方法,在接口中保持不变。并且扩展在这个接口中新加入一个getObject方法:
public interface ESPMenu { public String getId(); public String getCaption(); public String getNormalIcon(); public String getViewType() ; public String getSelectedIcon() ; public String getIcon() ; public String getContent() ; public Object getObject(Object key); }
2) 为这一个接口编写一个实现类ESPMenuImpl, 这个实现类本质上就是一个适配器:
/*package*/class ESPMenuImpl implements ESPMenu{ private StubObject stubObj; public ESPMenuImpl(StubObject stubObj) { this.stubObj = stubObj; } @Override public String getId() { return (String) stubObj.getObject("id"); } @Override public String getCaption() { return (String)stubObj.getObject("caption")); } @Override public String getNormalIcon() { return (String)stubObj.getObject("normalIcon")); } @Override public String getViewType() { return (String) stubObj.getObject("viewType"); } @Override public String getSelectedIcon() { return (String) stubObj.getObject("selectedIcon"); } @Override public String getIcon() { return (String) stubObj.getObject("icon"); } @Override public String getContent() { return (String) stubObj.getObject("content"); } @Override public Object getObject(Object key) { return stubObj.getObject(key, ""); } }
这个实现类在成员变量位置组合了一个StubObject对象, 也就是我们要使用的新接口。 并且将对就接口的调用, 都委托到对StubObject对象的调用。每个获取菜单属性的方法接口都没有改变,只是将调用都分发到StubObject对象的getObject方法,并且传入对应的key。
在该类的最后, 实现了ESPMenu中新加入的getObject方法,
同样将这个方法的调用委托给StubObject中getObject的方法。这样, StubObject的扩展性就传递到了ESPMenuImpl中, 使得ESPMenuImpl和StubObject具有同样的扩展性。
这样就完成了新旧接口的适配。项目上层中使用到的API没有改变,
只是将原来的ESPMenu类改成了ESPMenu接口。将ESPMenuImpl的访问权限设成包访问权限,那么对于上层代码,ESPMenuImpl就是不可见的,上层能使用的只能是ESPMenu接口, 不会涉及任何实现, 这样也就做到了比较好的封装性。
最后给出类图:
总结
设计上的事就是这样,想到了, 就能比较优雅的解决问题,想不到的话, 就只能使用到处修改代码的方法比较笨拙的应对问题,还容易将项目弄的混乱。现在我比较庆幸当初学习了设计模式,而没有听其他人的“建议”, 很多人都说“我们做的项目中用不到设计模式,学这个没用”。关于学习这个问题在我的另一篇博客我为什么要学习Linux?中提到过。设计模式是个好东西,以后我肯定还会进一步的学习,并且在项目中多实践,提升自己的设计能力。
其实设计模式并不难,难的是真正领悟他的精妙,并且能灵活的运用于日常项目的开发。
相关文章推荐
- 运用适配器模式应对项目变化
- 测试人员遇到不断变化的项目需求该如何应对?
- 应对Scrum项目带来的变化
- 应对Scrum项目带来的变化
- 成佩涛——如何快速应对项目需求中的变化
- 如何快速应对项目需求中的变化?
- 项目管理者如何应对需求的不断变化
- 探析项目主导型的IT业的人员需求变化及其应对办法
- 探析项目主导型的IT业的人员需求变化及其应对办法
- 应对Scrum项目带来的变化
- 『转载』应对Scrum项目带来的变化
- 解决项目需求变化:如何应对数据表中字段的增加
- 做web项目时对代码修改后浏览器端不生效的应对方法
- 企业应用业务需求变化的分析与应对-常见的业务需求变化
- 项目中EXTjs中运用到的下拉树
- 第11周 项目1-1 运用函数输出星图
- AndroidStudio项目提交到github以及工作中实际运用(详细步骤)
- struts2项目迁移为springmvc项目时jsp标签变化记录
- 结合项目实例 回顾传统设计模式(七)适配器模式(附外观模式)
- 第十三周项目三——车辆类(运用虚基数实现)