Live a simple life

沉默(zhu_xing@live.cn)
随笔 - 48, 文章 - 0, 评论 - 132, 引用 - 0
数据加载中……

【设计模式】有关策略模式

        对于工厂、策略、命令、适配、模版方法等模式大家应该都非常清楚了,因为这是使用极其频繁的一些模式。如果对于设计模式不是很清楚,估计也应该是在无形中使用了。刚review一块代码,想起和策略模式使用的时候的几个问题,仅供大家参考。
        说明:本人对设计模式理解很有限,工作经验也不长,欢迎指正,谢绝讽刺、挖苦^_^

        【策略模式的本质】
           如果先来无事想一下经典的23个OO设计模式,部分其实就是在良好地利用继承,部分则是避免继承,当然是否使用继承是由技术需求决定的。“继承封装变化”这句话听的耳朵都起茧子了,继承是多态的结构基础,多态特性给予了针对不同实例对同一请求做出不同反应的能力。我们在遇到可变的点时候,一般处理如下:
        1、对可变点抽象出一个共性的概念,用继承封装这种变化
        2、在客户端以引用的方式使用这种概念
        很显然,上面1的结果是产生抽象类型(往往是对外暴露,基于接口),并继承产生一系列扩展实现(往往是作为扩展实现,不一定对外暴露),上面2则需要我们思考如何构造实例,推荐的是讲类型本身和类型实例的构造进行分离(是客户端自己构造,还是有个工厂类型的角色来负责这种构造?如果有个工厂的角色来承担实例化的任务,那么这些扩展实现则跟容易隐藏)。
        『PS:我们常说的基于接口编程,但是接口本身必然是不能实例化的,那么是用户访问具体实现类型构造函数来实例化呢,还是从一个工厂中去取呢? 当然,理论上后者更好,也是基于接口编程的重要保证手段!』
        我们的策略模式精髓是:将算法(特定类型行为逻辑)封装进对象,便于独立使用。广义的讲,处理上面说的变化的点的手段有很多种,策略模式是其中之一,设计模式是工具层面^_^ (需求中变化的点如何处理,策略模式可能用的上,绝对不是想用策略模式,去产生一些变化的点)。但是,使用策略模式的经验告诉我们,定义的算法接口经常要修改...

        【策略模式使用时候的几个疑问】
        先说几个疑问:1、数据上下文合理吗;2、策略本身是否需要实例状态;3、如何实例化和管理这些策略;4、行为控制上下文如何处理

        场景:构建一个电子商务系统,要处理不同地区的业务,地区不同业务处理规则可能有点区别。

        很自然的,我们刚开始定义如下的接口(就叫做IPolicy吧 ~_~):
1 public interface IPolicy {
2     public boolean process(String id, String account);
3 }
       上面的id、account等参数就是我们处理一个订单需要的参数,也是从一个请求(Requst)数据结构中的部分信息。到这里,感觉一起OK。

        数据上下文合理呢?
        这个疑问出来,才回想起来,在定义IPolicy接口的时候,对process操作接受的数据上下文并没有进行足够的分析。可能就是假借一个地区的订单处理想了一下,处理一个订单需要这些信息。
        随着新地区订单策略实现的加入,最有可能带来的一个问题就是:你的process给的参数信息不够!!!一旦不够,那么你要修改的就是接口定义了,这个时候可能已经有一些策略实现了,修改吧!!!
        对IPolicy接口修改如下:
1 public interface IPolicy {
2     public boolean process(Request request);
3 }
        这里的Request是从系统接受到的请求(假设是从一个web service过来的,上面的id、account就是从Request中获取来的),那么Request里面的信息一般来说对于处理一个请求是足够的了, 这样你定义的IPolicy就更能够应对数据上下文的变化了。

        如何实例化和管理这些策略?谁承担这个责任?
        假设,有了两个实现,分别针对A区域和B区域:   
1 public class RegionAPolicy implements IPolicy {
2     
3     public boolean process(Request request) {
4         //
5         return true;
6     }
7 }
        
1 public class RegionBPolicy implements IPolicy {
2     
3     public boolean process(Request request) {
4         //
5         return true;
6     }
7 }
        
        假设,现在客户端如下使用:
 1 //
 2         String region = requst.getRegion();
 3         if ("A".equalsIgnoreCase(region)) {
 4             IPolicy policy = new RegionAPolicy();
 5             boolean result = policy.process(request);
 6             //
 7         }
 8         else if ("B".equalsIgnoreCase(region)) {
 9             IPolicy policy = new RegionBPolicy();
10             boolean result = policy.process(request);
11             //
12         }
13         //
           客户端首先获取对应的region信息,然后if else去创建对应的策略实例,然后再去调用这些实例上的服务.......那如果有个中间角色来管理这些策略,是不是能够把类型的使用和类型的实例化分开呢?引入一个管理器角色看一下:
1 public class PolicyManager {
2     private PolicyManager() {}
3     
4     public static PolicyManager getInstance() {//}
5     
6     public IPolicy getPolicy(String region) {//}
7 
8 }
        
        客户端调用代码变为如下:
1        //
2         String region = requst.getRegion();
3         IPolicy policy = PolicyManager.getInstance().getPolicy(region);
4         boolean result = policy.process(request);
5         //
        比较前面的调用代码,我们发现,客户端少干了一件事情:实例化和管理实例!!!  而且这样也可以做到具体的Policy实现类型对客户端隐藏,客户端真正基于接口。上面的PolicyManager我并没有具体写如何管理实例,在本问题场景下,只要保证一点就可以了,用户找你这个manager去获取指定region的策略。~_~

        策略对象需要实例状态吗?可以当作单态处理吗?需要同步化吗?
        需要分析你的策略,准确地判断你的策略实例是否需要实例字段来表示状态。一般是不需要的~_~ 如果引入实例变量,你的策略要有相应的管理...
       如果可以认为是单太的,那么同步化...

        控制上下文如何设计...?
        你的策略如何面对异常情况?例如发生异常时怎么办?异常信息又如何处理?是否可以把异常信息都收集起来呢,这样可以针对这些异常信息做各种处理  等等
        例如,可能将接口调整如下:
        
1 public interface IMessageCollector {
2     public void reportError();
3     
4     public void reportWarning();
5 }

1 public interface IPolicy {
2     public boolean process(Object request, IMessageCollector collector);
3 }
        
        
 1 //
 2         IMessageCollector messageCollector = new MessageCollectorImpl();
 3         
 4         String region = requst.getRegion();
 5         IPolicy policy = PolicyManager.getInstance().getPolicy(region);
 6         boolean result = policy.process(request, messageCollector);
 7         
 8         if (!result) {
 9             //TODO:处理messageCollector的一些错误和警告信息等
10         }
11         //

        这样我们的异常信息的就可以集中管理,可以在客户端做各种处理,而不需要修改策略本身,加强策略本身的复用。


        后记
        既然我们使用策略来封装策略变化,那么我们应该也要思考如何让我们的策略减少修改、易于扩展。其实如何将设计模式和一些基本的OO设计原则合理配合,在原则的指导下去用设计模式这个工具...是个很大的话题,不知道怎么去说。

        以上写的是一点自己的感想,大家有什么好的意见,敬请分享一下。文中的示意代码仅仅是为了辅助说明问题~_~

本博客中的所有文章、随笔除了标题中含有引用或者转载字样的,其他均为原创。转载请注明出处,谢谢!

posted on 2008-09-03 11:41 zhuxing 阅读(1534) 评论(3)  编辑  收藏 所属分类: Java设计

评论

# re: 【设计模式】有关策略模式  回复  更多评论   

不错,写的有道理,非常赞同一些观点!
2008-09-03 13:14 | Jack.Wang

# re: 【设计模式】有关策略模式  回复  更多评论   

> 数据上下文合理吗?

结合采用command模式。
2008-09-04 00:44 | linuxer

# re: 【设计模式】有关策略模式  回复  更多评论   

@linuxer
你说的是一种办法,在很多场景下可以这么做。我写这篇随笔的时候,就想集中精力来看一下一个行为封装伴随的上下文问题...
2008-09-04 10:03 | zhuxing

只有注册用户登录后才能发表评论。


网站导航: