从诸葛亮的三个锦囊妙计谈策略模式

来源:互联网 发布:恢复回收站的软件 编辑:程序博客网 时间:2024/05/16 10:43
                                                从诸葛亮的三个锦囊妙计谈策略模式
 
话说当年东吴孙权为刘备借走了荆州不还而耿耿于怀,却不料甘夫人去世,周郎顿时计上心来,让孙权将其妹嫁与刘备,骗刘备来东吴完婚。刘备又不是傻子,当然知道其中的猫腻,当即表态:打死也不去。诸葛亮却说无妨,当下给了赵云三个锦囊妙计,让他陪刘备去东吴完婚……最后的结果大家都知道,赵云在恰当的时候一一打开三个锦囊妙计,从而将危机一一化解。周瑜只落了个“周郎妙计安天下,赔了夫人又折兵”的笑柄。
这个故事,我们感兴趣的是三个锦囊妙计。诸葛亮一定是运用策略模式的高手,我们想啊:诸葛亮为什么要这么麻烦,做三个锦囊,他完全可以只做一个锦囊,将这三个妙计都写在它上面。可他没有这么做,而是正确的运用了策略模式做了三个锦囊。这样做的好处十分明显:诸葛亮一个锦囊写一个妙计,他的思路十分清晰,不会三个计策相互混乱。而赵云看妙计的时候也十分方便,什么时候看哪个妙计,使用十分方便,如果三个妙计混在一起,他就没这么方便了。
现在看来,这个故事正包含了策略模式的解决问题的思路:解决某一问题有多种方法或算法,如诸葛亮给了三个妙计,类的提供者要将这多种方法或算法独立开来,如诸葛亮将三个妙计分三个锦囊装,类的使用者,如赵云,在相应的条件,如故事中的恰当时间,使用相应的方法或算法。
前面我们说过命令模式,命令模式是对行为的封装;策略模式其实跟它的解决思路一样,是对算法的封装。当然,它们都一样的拥有同一个接口。
下面我们以一些例子来详细的说一说策略模式。
首先的一个例子是我们对数组的排序,对数组的排序有很多算法,我们想做一个由用户指定一个整形数组和对它排序的算法,我们给出结果这样一个方法,我们可以这样做:

public int[] sort(int[] inArray,String type)

{
       if(type.equals(“a”))
        {
               int k = 0;
               for(int i=0;i
               {
                      for(int j=i+1;j
                      {

                             if(inArray[i]> inArray[j])

                             {

                                    k = inArray[i];

                                    inArray[i] = inArray[j];

                                    inArray[j] = k;

                             }
                      }
               }
}
else if(type.equals(“b”))
{
       int k = 0;
       for(int i=0;i< inArray.length;i++)
       {

              for(int j=0;j< inArray.length-i-1;j++)

              {
                     if(inArray [j]> inArray [j+1])
                     {
                            k = inArray [j];
                            inArray [j] = inArray [j+1];
                            inArray [j+1] = k;
                     }
              }
       }
}
}
一看上面的代码,大家就觉得问题很多:第一,所有的算法都搅和在一起,不满足单一职责原则。第二,依赖细节,不满足依赖颠倒原则。第三,扩展性不好,如果要增加一个新的算法,需要对该方法进行改动。
下面我们来看看策略模式的解决思路。
首先是对所有的算法抽象一个接口,这是为了满足扩展性的基本条件,几乎所有的模式都要以接口为基础:
public interface SortAlgorithm
{

        public void sort(int[] inArr);

}
然后是各个算法的具体实现,它们实现了SortAlgorithm接口,完成了对关注点的分离,满足单一职责原则:

public class SortOne implements SortAlgorithm

{

        public void sort(int[] inArr)

        {
               int k = 0;
               for(int i=0;i
               {
                      for(int j=i+1;j
                      {

                             if(inArr[i]> inArr[j])

                             {

                                    k = inArr[i];

                                    inArr[i] = inArr[j];

                                    inArr[j] = k;

                             }
                      }
               }
}
}

public class SortTwo implements SortAlgorithm

{

        public void sort(int[] inArr)

        {
               int k = 0;
       for(int j=0;j< inArr.length-i-1;j++)
       {

              if(inArr[j]> inArr[j+1])

              {
                     k = inArr[j];
                     inArr[j] = inArr[j+1];
                     inArr[j+1] = k;
              }
               }
}
}
然后我们来看客户端的调用:

public int[] sort(int[] inArray,String type)

{
       SortAlgorithm sa;
       if(type.equals(“a”))
        {

               sa = new SortOne();

        }

        else if(type.equals(“b”))

        {

               sa = new SrotTwo();

}
else
{
       ……
}
sa.sort(inArray);
}
上面我们就完成了策略模式对该问题的解决,满足了单一职责原则;有了一定的扩展性,如果新增一个算法的话,去实现SortAlgorithm接口就可以了。
当然,我们在客户端代码可以看到,策略模式依然没有完全去掉客户端对具体实现类的依赖,这使得我们增加一个新的算法以后,仍然需要对客户端进行修改。所以策略模式和命令模式一样,只是初步增加了一定的扩展性。如果要完全去掉客户端对具体类的依赖,可以结合工厂模式来对该问题作进一步优化,具体做法大家可以自己做,这里不再讲述。
所谓策略模式,简单说来就是对算法的封装。在实际的编码过程中,我们会遇到各种各种的选择算法的问题,这时候就可以使用策略模式了,下面以一个例子来作为本文的结束。
假设我们有一个POJO类:Employee,里面存储了很多用户的信息,如下:
public class Employee
{

        private int id;

        private String name;

        private double servedYears;

        ……

        public void setId(int id)

        {
               this.id = id;
}
public int getId()
{
       return this.id;
}

public void setName(String name)

{
       this.name = name;
}
public String getName()
{
       return this.name;
}

public void setServedYears(double servedYears)

{
       this.servedYears = servedYears;
}

public double getServedYears()

{
       return this.servedYears;
}
……
}
现在我们有一个关于Employee的数组,需要对数组进行排序,我们知道该使用Arrays.sort()对该数组进行排序,但是在使用该方法的时候,我们首先要实现Comparator接口,如下:

Arrays.sort(emps,new Comparator(){

        Public int compare(Object o1,Object o2)

        {

               return ((Employee)o1).getServedYears()-((Employee)o2).getServedYears();

}
});
上面实现了一个对POJO对象的排序过程,现在的问题是我们分别需要对Employee类的id、name和servedYears为索引进行排序。我们可以看出,这明显是多个算法的问题,可以使用策略模式来解决问题:
以id为索引的比较器:

public class IdComparator implements Comparator

{

        Public int compare(Object o1,Object o2)

        {

               return ((Employee)o1).getId()-((Employee)o2).getId();

}
}
以name为索引的比较器:

public class NameComparator implements Comparator

{

        Public int compare(Object o1,Object o2)

        {

               return ((Employee)o1).getName()-((Employee)o2).getName();

}
}
以servedYears为索引的比较器:

public class ServedYearsComparator implements Comparator

{

        Public int compare(Object o1,Object o2)

        {

               return ((Employee)o1).getservedYears()-((Employee)o2).getServedYears();

}
}
然后,我们做一个工厂来获取具体的比较器:
public class Factory
{

        public static Comparator getInstance(String type)

        {
               if(type.equals(“id”))
               {
                      return new IdComparator();
}
else if(type.equals(“name”))
{
   return new NameComparator();
}
else
{
   return ServedYearsComparator();
}
}
}
那么我们就可以对客户端进行编码了:

public void sort(Employee[] emps,String type)

{
       Arrays.sort(emps,Factory.getInstance(type));
}
我们可以看到,结合了工厂模式的策略模式的确使多算法的问题得到了很好的解决:抽取了各个算法来单独关注,满足了单一职责原则;满足了依赖颠倒原则,有了很好的扩展性。等等。
还等什么呢?赶快到实践中去灵活运用这些模式吧!



原创粉丝点击