- 浏览: 467308 次
- 性别:
- 来自: 北京
-
最新评论
-
lm818:
最近一直在看设计模式,发现写的那本研磨真的不错,容易理解,能看 ...
大讨论:学习和应用设计模式的经验、教训、疑问等 -
yjfnwxf:
看了楼主博文,真为自己汗颜呀。。。。努力,再努力
研磨设计模式之命令模式-5 -
fengdandanweikang:
...
研磨设计模式 之 观察者模式(Observer) 1——跟着cc学设计系列 -
tiansong163:
你好,对《研磨设计模式》中UML有一个图标不知道是什么意思?希 ...
跟着cc学设计 之 研磨设计模式 目录汇总贴 -
soualliron:
国人就会溜须拍马,文章冠题“大讨论”,下面全是附会之音,无切实 ...
大讨论:学习和应用设计模式的经验、教训、疑问等
3.4 策略模式结合模板方法模式
在实际应用策略模式的过程中,经常会出现这样一种情况,就是发现这一系列算法的实现上存在公共功能,甚至这一系列算法的实现步骤都是一样的,只是在某些局部步骤上有所不同,这个时候,就需要对策略模式进行些许的变化使用了。
对于一系列算法的实现上存在公共功能的情况,策略模式可以有如下三种实现方式:
- 一个是在上下文当中实现公共功能,让所有具体的策略算法回调这些方法。
- 另外一种情况就是把策略的接口改成抽象类,然后在里面实现具体算法的公共功能。
- 还有一种情况是给所有的策略算法定义一个抽象的父类,让这个父类去实现策略的接口,然后在这个父类里面去实现公共的功能。
更进一步,如果这个时候发现“一系列算法的实现步骤都是一样的,只是在某些局部步骤上有所不同”的情况,那就可以在这个抽象类里面定义算法实现的骨架,然后让具体的策略算法去实现变化的部分。这样的一个结构自然就变成了策略模式来结合模板方法模式了,那个抽象类就成了模板方法模式的模板类。
在上一章我们讨论过模板方法模式来结合策略模式的方式,也就是主要的结构是模板方法模式,局部采用策略模式。而这里讨论的是策略模式来结合模板方法模式,也就是主要的结构是策略模式,局部实现上采用模板方法模式。通过这个示例也可以看出来,模式之间的结合是没有定势的,要具体问题具体分析。
此时策略模式结合模板方法模式的系统结构如下图5所示:
图5 策略模式结合模板方法模式的结构示意图
还是用实际的例子来说吧,比如上面那个记录日志的例子,如果现在需要在所有的消息前面都添加上日志时间,也就是说现在记录日志的步骤变成了:第一步为日志消息添加日志时间;第二步具体记录日志。
那么该怎么实现呢?
(1)记录日志的策略接口没有变化,为了看起来方便,还是示例一下,示例代码如下:
/** * 日志记录策略的接口 */ public interface LogStrategy { /** * 记录日志 * @param msg 需记录的日志信息 */ public void log(String msg); } |
(2)增加一个实现这个策略接口的抽象类,在里面定义记录日志的算法骨架,相当于模板方法模式的模板,示例代码如下:
/** * 实现日志策略的抽象模板,实现给消息添加时间 */ public abstract class LogStrategyTemplate implements LogStrategy{ public final void log(String msg) { //第一步:给消息添加记录日志的时间 DateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss SSS"); msg = df.format(new java.util.Date())+" 内容是:"+ msg; //第二步:真正执行日志记录 doLog(msg); } /** * 真正执行日志记录,让子类去具体实现 * @param msg 需记录的日志信息 */ protected abstract void doLog(String msg); } |
(3)这个时候那两个具体的日志算法实现也需要做些改变,不再直接实现策略接口了,而是继承模板,实现模板方法了。这个时候记录日志到数据库的类,示例代码如下:
/** * 把日志记录到数据库 */ public class DbLog extends LogStrategyTemplate{ //除了定义上发生了改变外,具体的实现没变 public void doLog(String msg) { //制造错误 if(msg!=null && msg.trim().length()>5){ int a = 5/0; } System.out.println("现在把 '"+msg+"' 记录到数据库中"); } } |
同理实现记录日志到文件的类如下:
/** * 把日志记录到数据库 */ public class FileLog extends LogStrategyTemplate{ public void doLog(String msg) { System.out.println("现在把 '"+msg+"' 记录到文件中"); } } |
(4)算法实现的改变不影响使用算法的上下文,上下文跟前面一样,示例代码如下:
/** * 日志记录的上下文 */ public class LogContext { /** * 记录日志的方法,提供给客户端使用 * @param msg 需记录的日志信息 */ public void log(String msg){ //在上下文里面,自行实现对具体策略的选择 //优先选用策略:记录到数据库 LogStrategy strategy = new DbLog(); try{ strategy.log(msg); }catch(Exception err){ //出错了,那就记录到文件中 strategy = new FileLog(); strategy.log(msg); } } } |
(5)客户端跟以前也一样,示例代码如下:
public class Client { public static void main(String[] args) { LogContext log = new LogContext(); log.log("记录日志"); log.log("再次记录日志"); } } |
运行一下客户端再次测试看看,体会一下,看看结果是否带上了时间。
通过这个示例,好好体会一下策略模式和模板方法模式的组合使用,在实用开发中是很常见的方式。
3.5 策略模式的优缺点
- 定义一系列算法
策略模式的功能就是定义一系列算法,实现让这些算法可以相互替换。所以会为这一系列算法定义公共的接口,以约束一系列算法要实现的功能。如果这一系列算法具有公共功能,可以把策略接口实现成为抽象类,把这些公共功能实现到父类里面,对于这个问题,前面讲了三种处理方法,这里就不罗嗦了。
避免多重条件语句
根据前面的示例会发现,策略模式的一系列策略算法是平等的,可以互换的,写在一起就是通过if-else结构来组织,如果此时具体的算法实现里面又有条件语句,就构成了多重条件语句,使用策略模式能避免这样的多重条件语句。
如下示例来演示了不使用策略模式的多重条件语句,示例代码如下:
public class OneClass { /** * 示范多重条件语句 * @param type 某个用于判断的类型 */ public void oneMethod(int type){ //使用策略模式的时候,这些算法的处理代码就被拿出去, //放到单独的算法实现类去了,这里就不再是多重条件了
if(type==1){ //算法一示范 //从某个地方获取这个s的值 String s = ""; //然后判断进行相应处理 if(s.indexOf("a") > 0){ //处理 }else{ //处理 } }else if(type==2){ //算法二示范 //从某个地方获取这个a的值 int a = 3; //然后判断进行相应处理 if(a > 10){ //处理 }else{ //处理 } } } } |
- 更好的扩展性
在策略模式中扩展新的策略实现非常容易,只要增加新的策略实现类,然后在选择使用策略的地方选择使用这个新的策略实现就好了。 - 客户必须了解每种策略的不同
策略模式也有缺点,比如让客户端来选择具体使用哪一个策略,这就可能会让客户需要了解所有的策略,还要了解各种策略的功能和不同,这样才能做出正确的选择,而且这样也暴露了策略的具体实现。 - 增加了对象数目
由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。 - 只适合扁平的算法结构
策略模式的一系列算法地位是平等的,是可以相互替换的,事实上构成了一个扁平的算法结构,也就是在一个策略接口下,有多个平等的策略算法,就相当于兄弟算法。而且在运行时刻只有一个算法被使用,这就限制了算法使用的层级,使用的时候不能嵌套使用。
对于出现需要嵌套使用多个算法的情况,比如折上折、折后返卷等业务的实现,需要组合或者是嵌套使用多个算法的情况,可以考虑使用装饰模式、或是变形的职责链、或是AOP等方式来实现。
3.6 思考策略模式
1:策略模式的本质
策略模式的本质:分离算法,选择实现。
仔细思考策略模式的结构和实现的功能,会发现,如果没有上下文,策略模式就回到了最基本的接口和实现了,只要是面向接口编程的,那么就能够享受到接口的封装隔离带来的好处。也就是通过一个统一的策略接口来封装和隔离具体的策略算法,面向接口编程的话,自然不需要关心具体的策略实现,也可以通过使用不同的实现类来实例化接口,从而实现切换具体的策略。
看起来好像没有上下文什么事情,但是如果没有上下文,那么就需要客户端来直接与具体的策略交互,尤其是当需要提供一些公共功能,或者是相关状态存储的时候,会大大增加客户端使用的难度。因此,引入上下文还是很必要的,有了上下文,这些工作就由上下文来完成了,客户端只需要与上下文交互就可以了,这样会让整个设计模式更独立、更有整体性,也让客户端更简单。
但纵观整个策略模式实现的功能和设计,它的本质还是“分离算法,选择实现”,因为分离并封装了算法,才能够很容易的修改和添加算法;也能很容易的动态切换使用不同的算法,也就是动态选择一个算法来实现需要的功能了。
2:对设计原则的体现
从设计原则上来看,策略模式很好的体现了开-闭原则。策略模式通过把一系列可变的算法进行封装,并定义出合理的使用结构,使得在系统出现新算法的时候,能很容易的把新的算法加入到已有的系统中,而已有的实现不需要做任何修改。这在前面的示例中已经体现出来了,好好体会一下。
从设计原则上来看,策略模式还很好的体现了里氏替换原则。策略模式是一个扁平结构,一系列的实现算法其实是兄弟关系,都是实现同一个接口或者继承的同一个父类。这样只要使用策略的客户保持面向抽象类型编程,就能够使用不同的策略的具体实现对象来配置它,从而实现一系列算法可以相互替换。
3:何时选用策略模式
建议在如下情况中,选用策略模式:
- 出现有许多相关的类,仅仅是行为有差别的情况,可以使用策略模式来使用多个行为中的一个来配置一个类的方法,实现算法动态切换
- 出现同一个算法,有很多不同的实现的情况,可以使用策略模式来把这些“不同的实现”实现成为一个算法的类层次
- 需要封装算法中,与算法相关的数据的情况,可以使用策略模式来避免暴露这些跟算法相关的数据结构
- 出现抽象一个定义了很多行为的类,并且是通过多个if-else语句来选择这些行为的情况,可以使用策略模式来代替这些条件语句
3.7 相关模式
- 策略模式和状态模式
这两个模式从模式结构上看是一样的,但是实现的功能是不一样的。
状态模式是根据状态的变化来选择相应的行为,不同的状态对应不同的类,每个状态对应的类实现了该状态对应的功能,在实现功能的同时,还会维护状态数据的变化。这些实现状态对应的功能的类之间是不能相互替换的。
策略模式是根据需要或者是客户端的要求来选择相应的实现类,各个实现类是平等的,是可以相互替换的。
另外策略模式可以让客户端来选择需要使用的策略算法,而状态模式一般是由上下文,或者是在状态实现类里面来维护具体的状态数据,通常不由客户端来指定状态。 - 策略模式和模板方法模式
这两个模式可组合使用,如同前面示例的那样。
模板方法重在封装算法骨架,而策略模式重在分离并封装算法实现。 - 策略模式和享元模式
这两个模式可组合使用。
策略模式分离并封装出一系列的策略算法对象,这些对象的功能通常都比较单一,很多时候就是为了实现某个算法的功能而存在,因此,针对这一系列的、多个细粒度的对象,可以应用享元模式来节省资源,但前提是这些算法对象要被频繁的使用,如果偶尔用一次,就没有必要做成享元了。
策略模式结束,谢谢观赏!鞠躬ing
评论


例外貌似前面没有提到模板方法。
楼主说的是抽象模板,不是泛型编程


1:首先,策略模式并不是要完全替换掉if-else,也就是说,并不是有了策略模式,就不再写if-else了,其实,每个策略封装的是原来if-else中的功能处理代码,而不是if-else本身。再说了,if-else只是策略模式要解决的问题之一。
2:你提出的这个问题的核心本质是:如何进行策略的选择。
对于策略模式而言,一般有两个地方来进行策略的选择,其一:是在使用策略模式的客户端来进行选择,等客户端选择好了,设置到策略模式的Context中即可,这种情况是把if-else移到策略模式的外部;其二是在策略模式的Context中进行选择。
3:既然是需要做选择,那么if-else是一种最简单的写法。如果希望不再出现if-else,也有很多方法来处理,但那已经不是策略模式所关心的内容了。
4:那么怎么处理就能够不再出现if-else呢?常见的方案有:
(1)简单点的,用反射:
这种方案可以在数据库或者是配置文件里面配置,比如:
姓名 工资支付方式
张三 cn.abc.paytype.RMBStrategy
李四 cn.abc.paytype.DollarStrategy
这样在程序里面读到这些数据了,就可以循环这些数据,然后根据工资支付方式所配置的类,进行反射调用,当然调用的方法是预先定好的,这样就不会出现if-else了。
(2)复杂点的,还可以做成原型注册器,这个可能需要对原型模式很熟悉才行。大致思路是这样,在原型注册器里面注册很多原型,原型注册器可以用Map实现:
原型的Key 原型
人民币 一个人民币支付的策略对象实例
美元 一个美元支付的策略对象实例
运行期间,根据数据库里面的记录,这个时候数据库中的记录可能是这样的:
姓名 工资支付方式
张三 人民币
李四 美元
程序里面这么写:
PaytypeApi api = PrototypeRegister.get("具体的工资支付方式").clone();
也就是根据每个人员的支付方式的设置,到原型注册器里面去查找并得到原型,然后通过克隆原型,得到支付方式的对象,接下来,就可以调用支付对象的具体的支付方法来实现工资支付了。
(3)当然还有其它的方式,这里全当抛砖引玉。
不知道是否能解答你心中的疑问。
平心而论,这样的例子基本掌握java的人都能写出来

例外貌似前面没有提到模板方法。
这种使用方法,我们在开发中也用过,但没有博主理解得这么清晰。
另外,博主对策略模式本质的分析,真是好,我以前就很忽略上下文,也认为策略模式不过就是接口和实现,其实是我理解不到位,谢谢博主的好文章
发表评论
-
私塾在线推出《一案贯通GoF设计模式》项目实战
2012-10-19 22:12 20《研磨设计模式》出版以来,包括iteye上的朋友,很多人 ... -
研磨设计模式 之 组合模式(Composite) 3——跟着cc学设计系列
2012-08-22 08:50 423915.3 模式讲解 15.3.1 认识组合模式 ... -
研磨设计模式 之 组合模式(Composite) 2——跟着cc学设计系列
2012-08-20 13:53 340415.2 解决方案 15.2.1 组合模式来解决 ... -
研磨设计模式 之 组合模式(Composite) 1——跟着cc学设计系列
2012-08-20 12:17 309015.1 场景问题 15.1.1 商品类别树 ... -
研磨设计模式 之 迭代器模式(Iterator)3——跟着cc学设计系列
2012-08-19 07:07 384814.3 模式讲解 14.3.1 ... -
研磨设计模式 之 迭代器模式(Iterator)2——跟着cc学设计系列
2012-08-18 03:48 265714.2 解决方案 14.2.1 ... -
研磨设计模式 之 迭代器模式(Iterator)2——跟着cc学设计系列
2012-08-17 18:26 10614.2 解决方案 14.2.1 ... -
研磨设计模式 之 迭代器模式(Iterator)1——跟着cc学设计系列
2012-08-17 10:38 205014.1 场景问题 14.1.1 ... -
私塾在线《研磨设计模式》,精品课程上线特大惊喜
2012-08-17 10:03 6072《研磨设计模式》——跟着CC学设计,视频课程在 私塾在线 ... -
研磨设计模式 之 观察者模式(Observer) 3——跟着cc学设计系列
2012-08-16 08:51 302312.3 模式讲解 12.3.1 认识观察者模式 ... -
研磨设计模式 之 观察者模式(Observer) 2——跟着cc学设计系列
2012-08-15 07:03 287912.2 解决方案 12.2 ... -
研磨设计模式 之 观察者模式(Observer) 1——跟着cc学设计系列
2012-08-15 07:03 212812.1 场景问题 12.1.1 订阅报纸的过程 ... -
跟着cc学设计系列 之 研磨设计模式 目录汇总贴
2012-08-14 14:49 36研磨设计模式 的 前言 ——跟着cc学 ... -
研磨设计模式 之 代理模式(Proxy)3——跟着cc学设计系列
2012-08-14 14:36 235611.3 模式讲解 11.3.1 认识代理模式 ... -
研磨设计模式 之 代理模式(Proxy)2——跟着cc学设计系列
2012-08-13 12:36 290611.2 解决方案 11.2.1 代理模式来解 ... -
研磨设计模式 之 代理模式(Proxy)1——跟着cc学设计系列
2012-08-13 12:35 219411.1 场景问题 11.1.1 访问多条数据 ... -
研磨设计模式 之 中介者模式(Mediator)3 ——跟着cc学设计系列
2012-08-11 11:50 123710.3 模式讲解 10.3.1 认识中介者模式 ... -
研磨设计模式 之 中介者模式(Mediator)2 ——跟着cc学设计系列
2012-08-09 08:23 147210.2 解决方案 10.2.1 中介者模式来 ... -
研磨设计模式 之 中介者模式(Mediator)1 ——跟着cc学设计系列
2012-08-09 08:23 148810.1 场景问题 10.1.1 ... -
研磨设计模式 之 原型模式(Prototype)3 ——跟着cc学设计系列
2012-08-08 08:14 15879.3 模式讲解 9.3.1 ...
相关推荐
《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。初级内容从基本讲起,包括每个模式的定义、功能、思路、结构、基本实现、运行调用顺序、基本应用示例等,让读者能系统、完整、准确地掌握每个模式,...
《研磨设计模式》是一本深入探讨软件设计原则与实践的经典书籍,其配套源代码提供了丰富的实例,帮助读者更好地理解和应用各种设计模式。这个UTF-8格式的压缩包包含了书中介绍的各种设计模式的实现,是学习和研究...
研磨设计模式的过程是持续学习和实践的过程,chjavach的博客文章提供了深入探讨这些模式的宝贵资源,值得我们仔细阅读和学习。通过深入理解和运用这些设计模式,可以提升个人的编程技巧,同时也为团队合作和项目维护...
《研磨设计模式》这本书是陈臣和王斌两位作者合作的成果,专注于讲解软件设计中的模式应用。设计模式是软件工程中的一种最佳实践,它总结了在特定上下文中解决问题的常见方法,使得开发者可以复用这些解决方案,提高...
《研磨设计模式》是由陈臣和王斌合著,由清华大学出版社出版的一本深入探讨设计模式的专业书籍。设计模式是软件工程中的一个重要概念,它代表了在特定上下文中解决问题的常见方法,经过时间和实践的验证,具有很高的...
《研磨设计模式》是一本深入探讨软件设计模式的书籍,配套源代码是作者为了帮助读者更好地理解和应用书中介绍的设计模式而提供的实践示例。设计模式是软件开发中经过实践检验的、解决常见问题的模板,它为软件设计...
这个压缩包“研磨设计模式全部源代码”包含了多种设计模式的实现,这些模式可以帮助开发者写出更可维护、可扩展和可复用的代码。下面将详细讲解其中可能包含的一些重要设计模式及其应用。 1. 工厂模式:这是最简单...
《研磨设计模式》这本书是软件开发领域中的经典之作,主要关注的是面向对象设计中的设计模式。设计模式是在特定上下文中解决常见问题的最佳实践,它为开发者提供了在类似情况下重复使用解决方案的模板,有助于提高...
这个“研磨设计模式博文集”显然是一份深入探讨设计模式的资料集合,其中可能包含了对多种设计模式的详细解析、示例代码以及实际应用中的经验分享。在软件开发中,设计模式能够帮助开发者提高代码质量、可读性和可...
研磨设计模式是一本深入探讨软件设计原则与实践的书籍,其讲课PPT为我们提供了丰富的设计模式知识。设计模式是软件工程中经过实践验证的、解决常见问题的模板,是经验丰富的开发人员智慧的结晶。这些模式可以帮助...
《研磨设计模式》是一本深入探讨软件设计模式的经典书籍,源代码包含了书中所讲解的各种设计模式的实际应用示例。设计模式是软件工程中的重要概念,它们是经过反复验证、在特定情境下解决常见问题的有效解决方案。...
《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。初级内容从基本讲起,包括每个模式的定义、功能、思路、结构、基本实现、运行调用顺序、基本应用示例等,让读者能系统、完整、准确地掌握每个模式,...
《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。初级内容从基本讲起,包括每个模式的定义、功能、思路、结构、基本实现、运行调用顺序、基本应用示例等,让读者能系统、完整、准确地掌握每个模式,...
《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。初级内容从基本讲起,包括每个模式的定义、功能、思路、结构、基本实现、运行调用顺序、基本应用示例等,让读者能系统、完整、准确地掌握每个模式,...
《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。初级内容从基本讲起,包括每个模式的定义、功能、思路、结构、基本实现、运行调用顺序、基本应用示例等,让读者能系统、完整、准确地掌握每个模式,...
这个名为“研磨设计模式视频课程PPT”的压缩包包含了一份关于23种核心设计模式的详细教学资料,旨在帮助开发者提升软件设计的效率和可维护性。下面将对这些设计模式进行深入解析。 1. **单例模式(Singleton)**:...
这篇“设计模式学习心得(研磨设计模式)”博客及其相关的PDF文档,为我们提供了一个深入理解和应用设计模式的宝贵资源。以下将针对单例模式、工厂方法模式、策略模式、命令模式和桥接模式进行详细讲解。 1. **单例...
《研磨设计模式》实战是IT领域中关于软件设计的一份重要资料,它主要探讨了设计模式在实际项目中的应用。设计模式是软件工程中经过长期实践总结出的通用问题解决方案,是解决常见设计问题的经验总结。这份PPT可能是...
设计模式Golang实现《研磨设计模式》读书笔记Go语言设计模式Go语言设计模式的实例代码创建模式工厂简单模式(Simple Factory)工厂方法模式(工厂方法)抽象工厂模式(Abstract Factory)创建者模式(Builder)原型...