您的位置:首页 > 其它

策略模式

2012-03-31 09:43 204 查看
实例场景:
1、一位父亲有三个儿子,在临终签前告诉他们死后会留下的东西,分别给他们每人送了一个箱子,A儿子打开箱子需要解决一个问题能够拿到的是一栋别墅,B儿子打开箱子需要解决一个问题能够拿到的是一辆豪车,C儿子打开箱子需要解决一个问题能够拿到的是一笔巨款,在这个场景中我们用到了策略模式,下面详细说明。
2、在java的集合框架中,我们有多钟排序算法,只要用户想要哪种排序,生成相应的排序类,然后传入到Collection的sort参数中,就能够实现相应的排序规则,这里面用到了我们的策略模式。
策略模式:
策略模式,又叫算法簇模式,就是定义了不同的算法族,并且之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
策略模式的好处在于你可以动态的改变对象的行为。

设计原则
设计原则是把一个类中经常改变或者将来可能改变的部分提取出来,作为一个接口(c++z中可以用虚类),然后在类中包含这个对象的实例,这样类的实例在运行时就可以随意调用实现了这个接口的类的行为。下面是一个例子。 策略模式属于对象行为型模式,主要针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。通常,策略模式适用于当一个应用程序需要实现一种特定的服务或者功能,而且该程序有多种实现方式时使用。
策略模式中有三个对象:
(1) 环境对象:该类中实现了对抽象策略中定义的接口或者抽象类的引用。
(2) 抽象策略对象:它可由接口或抽象类来实现。
(3) 具体策略对象:它封装了实现同不功能的不同算法。
利用策略模式构建应用程序,可以根据用户配置等内容,选择不同有算法来实现应用程序的功能。具体的选择有环境对象来完成。采用这种方式可以避免由于使用条件语句而带来的代码混乱,提高应用程序的灵活性与条理性。
代码实现场景:
模拟场景1:

package cn.com.strategy; //定义一个策略接口,这是父亲给三个儿子设计问题取得遗产的接口 public interface IStrategy { 	//打开箱子,每个问题都是一个算法 	public void execute(); }

package cn.com.strategy; //定义一个实现类,A儿子打开箱子,解决问题,得到一栋别墅 public class ImplA implements IStrategy { 	@Override 	public void execute() { 		System.out.println("解决问题,得到一栋别墅");  	}  }

package cn.com.strategy; //定义一个实现类,B儿子打开箱子,解决问题,得到一栋豪车 public class ImplB implements IStrategy { 	@Override 	public void execute() { 		System.out.println("解决问题,得到一辆豪车"); 	}  }
package cn.com.strategy; //定义一个实现类,C儿子打开箱子,解决问题,得到一笔巨款 public class ImplC implements IStrategy { 	@Override 	public void execute() { 		System.out.println("解决问题,得到一笔巨款");  	}  }
package cn.com.strategy; //定义一个环境对象,在场景中就像那个箱子 public class Environment { 	private IStrategy iStrategy; 	//两种方法为其赋值,构造和set、get 	public Environment(IStrategy iStrategy){ 		this.iStrategy=iStrategy; 	} 	 	public void execute(){ 		iStrategy.execute(); 	} 	 }

package cn.com.strategy; //测试类,看似代码一样,但是传入不同的实现类,得到不同结果 public class Test { 	public static void main(String[] args) { 		Environment e=new Environment(new ImplA()); 		e.execute();//A儿子打开箱子解决问题,得到一栋别墅 		e=new Environment(new ImplB()); 		e.execute();//B儿子打开箱子解决问题,得到一辆豪车 		e=new Environment(new ImplC()); 		e.execute();//C儿子打开箱子解决问题,得到一笔巨款 	}  }

输出结果将会是:
解决问题,得到一栋别墅
解决问题,得到一辆豪车 解决问题,得到一笔巨款  模拟场景2: package cn.com.strategy;  import java.util.List; //定义一个排序的策略接口 public interface SortStrategy<T> { 	public void sort(List<T> list);//执行排序算法 }

package cn.com.strategy;  import java.util.List;  //定义一个实现类,实现冒泡排序 public class SortBubble<T> implements SortStrategy<T> { 	@Override 	public void sort(List<T> list) { 		System.out.println("实现冒泡排序算法"); 	}  }

package cn.com.strategy;  import java.util.List;  //定义一个实现类,实现堆排序 public class SortHeap<T> implements SortStrategy<T> { 	@Override 	public void sort(List<T> list) { 		System.out.println("实现堆排序算法"); 	} }

package cn.com.strategy;  import java.util.List;  //定义一个实现类,实现快速排序 public class SortQuick<T> implements SortStrategy<T> { 	@Override 	public void sort(List<T> list) { 		System.out.println("实现快速排序算法"); 	}  }

package cn.com.strategy;  import java.util.List;  //定义一个环境对象,在场景中就是我们的算法使用者 public class Sorter { 	private SortStrategy iStrategy; 	//两种方法为其赋值,构造和set、get 	public SortStrategy getiStrategy() { 		return iStrategy; 	}  	public void setiStrategy(SortStrategy iStrategy) { 		this.iStrategy = iStrategy; 	} 	//传入相应的算法,对其进行排序 	public void sort(List list){ 		this.iStrategy.sort(list); 	} 	 }

package cn.com.strategy;  import java.util.ArrayList; import java.util.List;  //测试类,看似代码一样,但是传入不同的排序实现类,得到不同的排序结果 public class Test { 	public static void main(String[] args) { 		List list=new ArrayList(); 		list.add(2); 		list.add(3); 		list.add(1); 		Sorter sort=new Sorter(); 		SortBubble<Integer> sb=new SortBubble<Integer>(); 		sort.setiStrategy(sb);//实现冒泡排序 		sort.setiStrategy(new SortHeap<Integer>());//实现堆排序 		sort.setiStrategy(new SortQuick<Integer>());//实现快速排序 		 	}  }

输出相应的排序结果
在我们的集合框架中可以设计不同的Comparator来实现不同的比较方式,大大的提高了程序的扩展性,但值得说的类也要实现Comparable接口,只是在Comparable接口的实现方法中使用我们自己设置的排序策略,当然我们可以指定默认的排序算法,如上,这样如果有特殊需求时,在设置相应的排序算法,极大的提高了系统的灵活性。
  

策略模式优缺点
优点:
1、 简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
2、 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
缺点:
1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。
2、 在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象。(这本身没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由Context来承担,这就最大化的减轻了客户端的压力。)
简单工厂模式和策略模式区别(摘录,出处
这两种模式的作用就是拥抱变化,减少耦合。在变化来临时争取做最小的改动来适应变化。这就要求我们把些“善变”的功能从客户端分离出来,形成一个个的功能类,然后根据多态特性,使得功能类变化的同时,客户端代码不发生变化。
简单工厂模式
简单工厂模式:有一个父类需要做一个运算(其中包含了不同种类的几种运算),将父类涉及此运算的方法都设成虚方法,然后父类派生一些子类,使得每一种不同的运算都对应一个子类。另外有一个工厂类,这个类一般只有一个方法(工厂的生成方法),这个方法的返回值是一个超类,在方法的内部,根据传入参数的不同,分别构造各个不同的子类的对象,并返回。客户端并不认识子类,客户端只认识超类和工厂类。每次客户端需要一中运算时,就把相应的参数传给工厂类,让工厂类构造出相应的子类,然后在客户端用父类接收(这里有一个多态的运用)。客户端很顺理成章地用父类的计算方法(其实这是一个虚方法,并且已经被子类特化过了,其实是调用子类的方法)计算出来结果。如果要增加功能时,你只要再从父类中派生相应功能的子类,然后修改下工厂类就OK了,对于客户端是透明的。
策略模式
策略模式:策略模式更直接了一点,没有用工厂类,而是直接把工厂类的生成方法的代码写到了客户端。客户端自己构造出了具有不同功能的子类(而且是用父类接收的,多态),省掉了工厂类。策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。这里的算法家族和简单工厂模式里的父类是同一个概念。当不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为,将这些行为封装在一个个独立的策略子类中,可以在客户端中消除条件语句。
简单工厂模式+策略模式:为了将工厂方法的代码从客户端移出来,我们把这些代码搬到了父类的构造函数中,让父类在构造的时候,根据参数,自己实现工厂类的作用。这样做的好处就是,在客户端不用再认识工厂类了,客户端只要知道父类一个就OK,进一步隔离了变化,降低了耦合。
在基本的策略模式中,选择所用具体实现的职责由客户端对象成端,并转给客户端。这本身并没有减除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由父类承担,这就最大化地减轻了客户端的职责。
总结
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。
  

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息