序言
最近在看Head First 设计模式这本书,看了一点,第一感觉是简单,通俗易懂,层序渐进,基本上稍微耐心一点都看得懂,那么这本书这么多优点,我写博文是为了什么呢,方便自己以后观看吗?那我还不如看一遍书呢,既然书写的那么好,写给别人看?那还不如推荐别人直接去看书呢。写着系列的博文这也没用,那也没用,那我的出发点是什么呢? 好。我脑袋中的构思是先讲解一下每个模式的基本概念,也就是让你们先知道讲的这个模式的大概意思(尽量跟书上一样简单,就相当于我怎么想的告诉你们,你们可以用来借鉴,如果有更好的讲解方式,可能一起讨论),然后通过找一些实例,来看看这个模式在哪些地方用到了。加深我们的映像,让我们学习设计模式的时候,也能学点别的东西。 这就是我写这一系列博文的目的。
--WH
策略模式
一、准备知识
多态:父类对象引用子类实例对象,A extends B,A a = new B(); A、B中都有方法run,a.run();此时就是调用B中的run方法。这就是运行时多态,
为什么叫运行时多态呢?因为在编译的时候,不知道真正的a到底是什么,只知道a是A,在运行时才发现a是B的实例,调用的也就是B中的方法,
这样做有什么好处?灵活性、简化性、这种只能通过自己多敲代码才能感觉得到使用多态的好处。。。
行为组合:通过学习策略模式你就知道了什么是行为组合
二、什么是策略模式?
在Head First设计模式的书中,用的鸭子的各种行为的例子来说明,看的我头晕眼花,各种鸭子,橡皮鸭都出来了,而且是一步步带领你进入到他所要说的那种思想上去。我感觉对于我这种不喜欢啰嗦麻烦的人来说,是个噩梦,直接看了下面这个图,就理解了什么是策略模式
一个游戏中有很多角色,有国王、王后、骑士、妖怪等人物,并且每个人物作战的方式也不同,怎么实现呢?
1、普通方法实现。
看起来不错啊,哈哈,应有尽有,但是缺点很多,
1、如果king的战斗方式需要改变呢,变成用枪了,那么我们只好在king类中增加一个GunBehavior方法,然后将fight中改为调用GunBehavior
2、游戏不止三个骑士,那就来100个骑士,都使用弓箭,但是后面改革了,骑士改为用斧头了,本来妖怪类中就已经实现了斧头的方法,但没办法,我们还有自己在在骑士中实现一遍
3、这样一来,可扩展性和可复用性都非常差。每个人物中的方法都可能有重复。
2、通过策略者模式怎么实现的呢?看下图。
1、变化的部分:每个人物使用的战斗行为不一样。所以将战斗的行为都提取出来独立当成一个类
2、将战斗行为抽取出一个接口来,为的就是使用接口编程的方式。
3、在Character中申明一个WeaponBehavior变量,也就是为了接口编程后使用多态更方便。
通过上面三步的改造,现在king使用什么战斗方式,只需要new出来就行了,如果要使用枪,只需要在创建一个GunBehavior的类,然后让king使用
如果骑士,他们也想用斧头,那么就把骑士中改一行代码,改为用斧头类就好了。
其实策略者模式最关键的就是将行为都提取出来封装成独立类,然后让使用者想用什么就new什么。其他使用多态的地方只是设计原则所导致的。
画图有点失误,没有将weapon.useWeapon()调用。sorry。
策略者模式用到了哪些设计原则?
1、多用组合、少用继承。这句话中的“组合”的意思就像上面的那些作战行为都封装成一个独立的类,然后组合在一起。继承在这个例子中并没有体现出来。
2、针对接口编程、而不是针对实现编程。 这个其实就是为了用多态。
3、找出应用中可能需要变化的地方,把它们独立出来,不要和那些不需要改变的代码混在一起。
策略模式的标准结构图
三、JDK中使用的策略模式体现
1、Comparator接口
记得那个Comparetor这个接口吗,这个就是使用了策略模式写的。在做Comparator和Comparable的比较的时候就说过,实现Comparator是在外部实现compareTo方法,而实现Comparable接口是在内部实现compareTo方法,现在知道为什么Comparator是在外部实现compareTo方法吗,原因就是使用的是策略模式,每个功能/算法类,都必须实现策略接口。然后再需要使用该类的时候,在将其new出来使用.光说没有用,带你看一个非常熟悉的类。String类中就用到了
首先看一下JDK中实现Comparator接口后扩展了一些什么类
通过图中可以看到实现该接口的类有很多,但是我们看到一个很熟悉的,java.lang.String下的一个类,说明这个CaseInsensitiveComparator在String中用到了。
打开String的源码,查看了一下,跟我们讲解的一模一样的形式
不管这个类的作用是什么,反正我们是看到了熟悉的模式,
1、CaseInsensitiveComparator实现了Comparator接口
2、在String中Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
虽然这个是个常量,但是也没什么大的差别,跟我们自己写的规范是一样的。
2、ThreadPoolExecutor中的四种拒绝策略
在JDK中,这个也使用了策略模式,有兴趣的可以去看看,这里我就不带着大家看了。
四、总结
其实我们平常的代码中,没有那么复杂,只需要将其中变化的部分给抽取出来,达到复用的目的就行了,这种策略模式一般都是在那种比较复杂的情况下,就将其按照上面标准的结构给实现下来。
策略模式的定义:
1、策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
也个定义,通俗的讲,也就是将那些使用的方法风别封装成独立的类,然后将这一类使用接口统一管理起来,让需要使用这些方法的用户能够随时调用他们。上面例子中的战斗行为就相当于定义中的算法一词。只是算了个说法而已。
缺点:
1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
2、由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么这些类的数目就非常多了。