
正文
策略模式java代码 策略模式编程
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
Java中,状态模式和策略模式的区别
策略模式通过封装一组相关算法,为Client提供运行时的灵活性。Client可以在运行时,选择任一算法,而不改变使用算法的Context。一些流行的策略模式的例子是写那些使用算法的代码,例如加密算法、压缩算法、排序算法。另一方面,状态模式允许对象,在不同的状态拥有不同的行为。因为现实世界中的对象通常都是有状态的,所以在不同状态,行为也不一样。例如,VM(自动售货机)只在hasCoin状态才给吐商品;不投币,是不会吐的。现在可以清楚的看出不同之处了:意图是不同的。状态模式帮助对象管理状态,而策略模式允许Client选择不同的行为。
另一个不那么容易能看出来的区别是:是谁促使了行为的改变。策略模式中,是Client提供了不同的策略给Context;状态模式中,状态转移由Context或State管理。另外,如果在State中管理状态转移,那么必须持有Context的引用。例如,在VM的例子中,State对象需要调用VM的setState()方法去改变的状态。另一方面,Strategy从不持有Context的引用,是Client把所选择的Strategy传递给Context。由于状态模式和策略模式的区别,是流行的Java设计原则类面试题之一,将会在本文探讨在Java中,状态模式和策略模式的异同,这可以加深理解。
(1)相似之处
看看状态模式和策略模式的UML图,就会发现结构非常相似。使用State对象改变自己行为的对象被称为Context对象;相似的,使用Strategy对象改变自己行为的对象叫Context对象。记住,Client和Context打交道。在状态模式中,Context把方法调用委托给当前的状态对象,而在策略模式中,Context使用的Strategy对象,是被当做参数传递过来的,或在Context对象被创建时就被提供的。
这是专为经典的VM问题而设计的状态模式UML类图。可以看出,VM的状态是个接口,表示不同状态的具体实现。每一个状态都持有Context的引用,用来管理由Context触发的行为导致的状态转移。
这是专为实现排序功能而设计的策略模式UML类图。因为存在很多排序算法,该模式让Client在排序时选择适当的算法。事实上,Java的集合框架就使用这个模式,实现了用来排序的Collections.sort()方法。不同的是,不允许Client选择排序算法,而是让传递Comparator或Comparable接口的实例来指定比较策略。
(2)不同之处
策略模式封装了一组相关算法,允许Client在运行时使用可互换的行为;状态模式帮助一个类在不同的状态显示不同的行为。
状态模式封装了对象的状态,而策略模式封装算法或策略。因为状态是跟对象密切相关的,不能被重用;而通过从Context中分离出策略或算法,可以重用。
在状态模式中,每个状态通过持有Context的引用,来实现状态转移;但是每个策略都不持有Context的引用,只是被Context使用。
策略实现可以作为参数传递给使用的对象,例如Collections.sort(),参数包含一个Comparator策略。另一方面,状态是Context对象自己的一部分,随着时间的推移,Context对象从一个状态转移到另一个状态。
虽然都符合OCP原则,策略模式也符合SRP原则(单一职责原则),因为每个策略都封装自己的算法,且不依赖其他策略。一个策略的改变,并不会导致其他策略的变化。
另一个理论上的不同:策略模式定义了对象“怎么做”的部分。例如,排序对象怎么对数据排序。状态模式定义了对象“是什么”和“什么时候做”的部分。例如,对象处于什么状态,什么时候处在某个特定的状态。
状态模式中很好的定义了状态转移的次序;而策略模式并无此需要:Client可以自由的选择任何策略。
一些常见的策略模式的例子是封装算法,例如排序算法,加密算法或者压缩算法。如果看到代码需要使用不同类型的相关算法,那么考虑使用策略模式吧。而识别何时使用状态模式是很简单的:如果需要管理状态和状态转移,但不想使用大量嵌套的条件语句,那么就是了。
最后但最重要的一个不同之处是,策略的改变由Client完成;而状态的改变,由Context或状态自己。
相关问答
Q1: 在jvav中,什么是策略设计模式?
当 我们掌握了Java的语法,当我们了解了面向对象的封装、继承、多态等特性,当我们可以用Swing、Servlet、JSP技术构建桌面以及Web应 用,不意味着我们可以写出面向对象的程序,不意味着我们可以很好的实现代码复用,弹性维护,不意味着我们可以实现在维护、扩展基础上的代码复用。一把刀, 可以使你制敌于无形而于江湖扬名,也可以只是一把利刃而使你切菜平静。Java,就是这把刀,它的威力取决于你使用的方式。当我们陷入无尽无止重复代码的 泥沼,当我们面临牵一发而动全身的维护恶梦, 你应该想起“设计模式”这个行动秘笈。面向对象的精义,看似平淡,其实要经过艰苦实践才能成功。而构造OO系统的隐含经验于是被前人搜集而成并冠以“设计 模式”之名。我们应该在编码行动初始就携带以它。接下来,让我们步“四人组”先行者之后,用中国文字、用实际案例领略模式于我们代码焕然一新的改变:
设计模式解读之一: 策略模式
1. 模式定义
把会变化的内容取出并封装起来,以便以后可以轻易地改动或扩充部分,而不影响不需要变化的其他部分;
2. 问题缘起
当涉及至代码维护时,为了复用目的而使用继承,结局并不完美。对父类的修改,会影响到子类型。在超类中增加的方法,会导致子类型有该方法,甚至连那些不该具备该方法的子类型也无法免除。示例,一个鸭子类型:
public abstract class Duck
{
//所有的鸭子均会叫以及游泳,所以父类中处理这部分代码
public void quack()
{
System.out.println("Quack");
}
public void swim()
{
System.out.println("All ducks float, even decoys.");
}
//因为每种鸭子的外观是不同的,所以父类中该方法是抽象的,由子类型自己完成。
public abstract void display();
}
public class MallardDuck extends Duck
{
//野鸭外观显示为绿头
public void display()
{
System.out.println("Green head.");
}
}
public class RedHeadDuck extends Duck {
//红头鸭显示为红头
public void display() {
System.out.println("Red head.");
}
}
public class RubberDuck extends Duck {
//橡皮鸭叫声为吱吱叫,所以重写父类以改写行为
public void quack() {
System.out.println("Squeak");
}
//橡皮鸭显示为黄头
public void display() {
System.out.println("Yellow head.");
}
}
上述代码,初始实现得非常好。现在我们如果给Duck.java中加入fly()方法的话,那么在子类型中均有了该方法,于是我们看到了 会飞的橡皮鸭子,你看过吗?当然,我们可以在子类中通过空实现重写该方法以解决该方法对于子类型的影响。但是父类中再增加其它的方法呢?
通过继承在父类中提供行为,会导致以下缺点:
a. 代码在多个子类中重复;
b. 运行时的行为不容易改变;
c. 改变会牵一发动全身,造成部分子类型不想要的改变;
好啦,还是刚才鸭子的例子,你也许想到使用接口,将飞的行为、叫的行为定义为接口,然后让Duck的各种子类型实现这些接口。这时侯代码类似于:
public abstract class Duck {
//将变化的行为 fly() 以及quake()从Duck类中分离出去定义形成接口,有需求的子类中自行去实现
public void swim() {
System.out.println("All ducks float, even decoys.");
}
public abstract void display();
}
//变化的 fly() 行为定义形成的接口
public interface FlyBehavior {
void fly();
}
//变化的 quack() 行为定义形成的接口
public interface QuackBehavior {
void quack();
}
//野鸭子会飞以及叫,所以实现接口 FlyBehavior, QuackBehavior
public class MallardDuck extends Duck implements FlyBehavior, QuackBehavior{
public void display() {
System.out.println("Green head.");
}
public void fly() {
System.out.println("Fly.");
}
public void quack() {
System.out.println("Quack.");
}
}
//红头鸭子会飞以及叫,所以也实现接口 FlyBehavior, QuackBehavior
public class RedHeadDuck extends Duck implements FlyBehavior, QuackBehavior{
public void display() {
System.out.println("Red head.");
}
public void fly() {
System.out.println("Fly.");
}
public void quack() {
System.out.println("Quack.");
}
}
//橡皮鸭不会飞,但会吱吱叫,所以只实现接口QuackBehavior
public class RubberDuck extends Duck implements QuackBehavior{
//橡皮鸭叫声为吱吱叫
public void quack() {
System.out.println("Squeak");
}
//橡皮鸭显示为黄头
public void display() {
System.out.println("Yellow head.");
}
}
上述代码虽然解决了一部分问题,让子类型可以有选择地提供一些行为(例如 fly() 方法将不会出现在橡皮鸭中).但我们也看到,野鸭子MallardDuck.java和红头鸭子RedHeadDuck.java的一些相同行为代码不能 得到重复使用。很大程度上这是从一个火坑跳到另一个火坑。
在一段程序之后,让我们从细节中跳出来,关注一些共性问题。不管使用什么语言,构建什么应用,在软件开发上,一直伴随着的不变的真理是:需要一直在变化。不管当初软件设计得多好,一段时间之后,总是需要成长与改变,否则软件就会死亡。
我们知道,继承在某种程度上可以实现代码重用,但是父类(例如鸭子类Duck)的行为在子类型中是不断变化的,让所有子类型都有这些行为是不恰当的。我们 可以将这些行为定义为接口,让Duck的各种子类型去实现,但接口不具有实现代码,所以实现接口无法达到代码复用。这意味着,当我们需要修改某个行为,必 须往下追踪并在每一个定义此行为的类中修改它,一不小心,会造成新的错误。
设计原则:把应用中变化的地方独立出来,不要和那些不需要变化的代码混在一起。这样代码变化引起的不经意后果变少,系统变得更有弹性。
按照上述设计原则,我们重新审视之前的Duck代码。
1) 分开变化的内容和不变的内容
Duck类中的行为 fly(), quack(), 每个子类型可能有自己特有的表现,这就是所谓的变化的内容。
Duck类中的行为 swim() 每个子类型的表现均相同,这就是所谓不变的内容。
我们将变化的内容从Duck()类中剥离出来单独定义形成接口以及一系列的实现类型。将变化的内容定义形成接口可实现变化内容和不变内容的剥离。其实现类 型可实现变化内容的重用。这些实现类并非Duck.java的子类型,而是专门的一组实现类,称之为"行为类"。由行为类而不是Duck.java的子类 型来实现接口。这样,才能保证变化的行为独立于不变的内容。于是我们有:
变化的内容:
//变化的 fly() 行为定义形成的接口
public interface FlyBehavior {
void fly();
}
//变化的 fly() 行为的实现类之一
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("I'm flying.");
}
}
//变化的 fly() 行为的实现类之二
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("I can't fly.");
}
}
-----------------------------------------------------------------
//变化的 quack() 行为定义形成的接口
public interface QuackBehavior {
void quack();
}
//变化的 quack() 行为实现类之一
public class Quack implements QuackBehavior {
public void quack() {
System.out.println("Quack");
}
}
//变化的 quack() 行为实现类之二
public class Squeak implements QuackBehavior {
public void quack() {
System.out.println("Squeak.");
}
}
//变化的 quack() 行为实现类之三
public class MuteQuack implements QuackBehavior {
public void quack() {
System.out.println(" Slience ");
}
}
通过以上设计,fly()行为以及quack()行为已经和Duck.java没有什么关系,可以充分得到复用。而且我们很容易增加新的行为, 既不影响现有的行为,也不影响Duck.java。但是,大家可能有个疑问,就是在面向对象中行为不是体现为方法吗?为什么现在被定义形成类(例如 Squeak.java)?在OO中,类代表的"东西"一般是既有状态(实例变量)又有方法。只是在本例中碰巧"东西"是个行为。既使是行为,也有属性及 方法,例如飞行行为,也需要一些属性记录飞行的状态,如飞行高度、速度等。
2) 整合变化的内容和不变的内容
Duck.java将 fly()以及quack()的行为委拖给行为类处理。
不变的内容:
public abstract class Duck {
//将行为类声明为接口类型,降低对行为实现类型的依赖
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public void performFly() {
//不自行处理fly()行为,而是委拖给引用flyBehavior所指向的行为对象
flyBehavior.fly();
}
public void performQuack() {
quackBehavior.quack();
}
public void swim() {
System.out.println("All ducks float, even decoys.");
}
public abstract void display();
}
Duck.java不关心如何进行 fly()以及quack(), 这些细节交由具体的行为类完成。
public class MallardDuck extends Duck{
public MallardDuck() {
flyBehavior=new FlyWithWings();
quackBehavior=new Quack();
}
public void display() {
System.out.println("Green head.");
}
}
测试类:
public class DuckTest {
public static void main(String[] args) {
Duck duck=new MallardDuck();
duck.performFly();
duck.performQuack();
}
}
在Duck.java子类型MallardDuck.java的构造方法中,直接实例化行为类型,在编译的时侯便指定具体行为类型。当然,我们可以:
1) 我们可以通过工厂模式或其它模式进一步解藕(可参考后续模式讲解);
2) 或做到在运行时动态地改变行为。
3) 动态设定行为
在父类Duck.java中增加设定行为类型的setter方法,接受行为类型对象的参数传入。为了降藕,行为参数被声明为接口类型。这样,既便在运行时,也可以通过调用这二个方法以改变行为。
public abstract class Duck {
//在刚才Duck.java中加入以下二个方法。
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior=flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior=quackBehavior;
}
//其它方法同,省略...
}
测试类:
public class DuckTest {
public static void main(String[] args) {
Duck duck=new MallardDuck();
duck.performFly();
duck.performQuack();
duck.setFlyBehavior(new FlyNoWay());
duck.performFly();
}
}
如果,我们要加上火箭助力的飞行行为,只需再新建FlyBehavior.java接口的实现类型。而子类型可通过调用setQuackBehavior(...)方法动态改变。至此,在Duck.java增加新的行为给我们代码所带来的困绕已不复存在。
该是总结的时侯了,让我们从代码的水中浮出来,做一只在水面上自由游动的鸭子吧:
3. 解决方案
MallardDuck 继承 Duck抽象类; - 不变的内容
FlyWithWings 实现 FlyBehavior接口; - 变化的内容,行为或算法
在Duck.java提供setter方法以装配关系; - 动态设定行为
以上就是策略模式的实现三步曲。接下来,让我们透过步骤看本质:
1) 初始,我们通过继承实现行为的重用,导致了代码的维护问题。 - 继承, is a
2) 接着,我们将行为剥离成单独的类型并声明为不变内容的实例变量并通过 - 组合, has a
setter方法以装配关系;
继承,可以实现静态代码的复用;组合,可以实现代码的弹性维护;使用组合代替继承,可以使代码更好地适应软件开发完后的需求变化。
策略模式的本质:少用继承,多用组合
Q2: java策略模式与接口直接实现
我是一个java初学者,我下面写的东西都是我个人的猜想,楼主可以作为参考,但不要相信。--------感觉由context包装以后的耦合性更低一些,传入不同的new AddStrategy(),context.calculate(10, 5)会实现不同的功能,通过这样的方式,如果通过配置文件的方式来实现对算法的配置,直接利用接口就把代码写死了,而策略模式由于具有低耦合性就可以通过配置对象来配置不同的算法。。
Q3: JAVA设计模式
嗯,是策略模式,代码如下。你把里面你要的奖金计算好就可以用了。建议楼主去看下《大话设计模式》或者《java设计模式》里面讲的挺生动的。
//奖金接口
public interface Bonus {
Double calcBonus();
}
//累计奖金
public class AccumulativeBouns implements Bonus{
public Double calcBonus() {
//总的汇款额*0.1%
return 0.0;
}
}
//业务奖金
public class BusinessBonus implements Bonus{
public Double calcBonus(){
//每个人当月业务奖金=当月销售额*3%
return 0.0;
}
}
//团队奖金
public class TeamBonus implements Bonus{
public Double calcBonus(){
//团队总销售额*1%
return 0.0;
}
}
public class BounsContext {
private Bonus bonus;
public BounsContext(Bonus bonus){
this.bonus=bonus;
}
public double calcBonus(){
return bonus.calcBonus();
}
}
调用..
public class MainTest {
public static void main(String [] args){
//累计奖金
BounsContext bounsContext=null;
bounsContext =new BounsContext(new AccumulativeBouns());
bounsContext.calcBonus();
//团队奖金
bounsContext=new BounsContext(new TeamBonus());
bounsContext.calcBonus();
//业务奖金
bounsContext=new BounsContext(new BusinessBonus());
bounsContext.calcBonus();
}
}
Q4: java策略模式应用场景为何?
我认为策略模式是java众多模式中最常用,最常见策略模式java代码的一种模式。
一句话说,针对同一命令(或行为),不同的策略做不同的动作。 (个人总结 难免疏漏 海涵)
举例来说,一个接口有两个实现:
interface RunBehavior {
public void performRun();
}
class Run implements RunBehavior {
public void performRun() { System.out.println(" I can run!"); }
}
class JumpAsRun implements RunBehavior {
public void performRun() { System.out.println("I cannot run, but I can jump!"); }
}
解释一下这段简单的程序
跑是一种行为(接口)
正常的动物都可以跑(class Run)
麻雀是不能跑的,它只能跳(class JumpAsRun)
这样的话,其实我们有策略模式java代码了一个行为的请求,那就是跑,然后我们有两个策略供选择。
那么如何调用呢?
class Anmial {
RunBehavior runBehavior;
public void run() {
this.runBehavior.performRun();
}
public void setRunBehavior(RunBehavior runBehavior) {
this.runBehavior = runBehavior;
}
}
下面真正开始调用:
public class TestStrategy{
public static void main(String args[]) {
RunBehavior rb1 = new Run();
Animal tiger = new Animal();
tiger.setRunBehavior(rb1);
tiger.run();
RunBehavior rb2 = new JumpAsRun();
Animal bird = new Animal();
bird.setRunBehavior(rb2);
bird.run();
// 上面的老虎和鸟分别用了一个策略,下面让老虎用鸟的策略
// 看看会发生什么
tiger.setRunBehavior(rb2);
tiger.run();
// 其实策略模式的重点就在这儿,给对象传入什么样的策略,执行什么样的动作。
}
}
具体的还要策略模式java代码你自己多思考了,推荐《Head First Desigh Pattern》
全手工敲入代码,估计策略模式java代码你需要微调如果想运行的话
Q5: java新手,求解决下面代码:
策略模式java代码你这是一种设计模式:策略模式。首先策略模式java代码你的Mp3playCommand类中的execute方法写错策略模式java代码了策略模式java代码,应该是mp3.play(),其次是你的测试类写的有问题:以Mp3playCommand 举例:你new了一个SimpleRemoteControl,一个mp3,并把mp3通过构造方法的方式传入到了 Mp3playCommand,此时Mp3playCommand类中有一个变量时mp3,最后你应该把Mp3play传入到SimpleRemoteControl类中,而你传的是什么策略模式java代码???最后,SimpleRemoteControl类中有一个command类型的Mp3play变量,然后调用execute方法,即Mp3play.execute,剩下的就应该懂了吧???可以参照java设计模式。。。。。。
关于策略模式java代码和策略模式编程的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。







