设计模式之策略模式

Posted 菜鸟_lch

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之策略模式相关的知识,希望对你有一定的参考价值。

  在写详细的策略模式之前,先搞懂java的普通类、抽象类或者接口,以及为什么要继承这些类或者实现这些接口。

  类是一种模板,所有的对象归根到底都是根据类来创建的。普通类和抽象类的区别,普通类的所有方法都必须给予一个具体的实现,而抽象类可以不实现它的一些方法,这些方法为抽象方法。

 1 package cn.liu.java.myjava;
 2 
 3 public class People {
 4     //人的基本属性,假设这里的人都是正常的
 5     private String mouse;
 6     private String foot;
 7     private String hand;
 8     private String eye;
 9     //人的基本日常
10     public void say(){
11         System.out.println("I can say");
12     }
13     public void eat(){
14         System.out.println("I can eat");
15     }
16     public void walk(){
17         System.out.println("I can walk");
18     }
19     public void sleep(){
20         System.out.println("I can sleep");
21     }
22     public String getMouse() {
23         return mouse;
24     }
25     public void setMouse(String mouse) {
26         this.mouse = mouse;
27     }
28     public String getFoot() {
29         return foot;
30     }
31     public void setFoot(String foot) {
32         this.foot = foot;
33     }
34     public String getHand() {
35         return hand;
36     }
37     public void setHand(String hand) {
38         this.hand = hand;
39     }
40     public String getEye() {
41         return eye;
42     }
43     public void setEye(String eye) {
44         this.eye = eye;
45     }
46     
47 }

  通过上面代码中的people类,可以以People类为模板创建一系列的people对象,也可以通过继承上面的类来创建一个更为确定的类,例如学生,医生,教师,并且在继承的类中添加他们各自的特性,例如教书,救人,学习,这里面还可以延伸到教什么科目,什么类型的医生,什么专业的学生等等。

 

  接下来是抽象类,抽象类是使用了abstract修饰的类,类中的方法可实现可不实现,但是不实现的必须都是抽象方法。下面上一个具体的代码:

 1 package cn.liu.java.myjava;
 2 
 3 public abstract class  People {
 4     //人的基本属性,假设这里的人都是正常的
 5     private String mouse;
 6     private String foot;
 7     private String hand;
 8     private String eye;
 9     //这里假设有不同地方的人,语种不同
10     public abstract void say();
11     public void eat(){
12         System.out.println("I can eat");
13     }
14     public void walk(){
15         System.out.println("I can walk");
16     }
17     public void sleep(){
18         System.out.println("I can sleep");
19     }
20 }

 

  为什么我这里的say不进行实现呢?而其他的方法都实现了,因为其他方法都是人类共有的,而say方法是不确定的,所以不予以具体的实现。继承抽象类的子类必须实现其中的抽象方法。那么从上面的对比就可以看出,如果一个类的行为是确定的,并且继承该类的子类的行为也不会变,我们应该使用普通父类,如果不同的子类的实现都是不尽相同的,此时我们应该使用抽象类,然后在它的子类中给出具体的实现,因为这时候如果继承普通的类还是要重写其中的方法。

  接下来就是接口,接口中的属性默认都是static final的,并且接口中只能有方法签名,而不能有具体的实现。实现了接口的类必须实现其中的所有的方法。所以我认为接口更像是抽象类的具体实现,其中的方法都是抽象方法。

  那么实现了继承类和实现接口的好处是什么呢?主要是运行时动态绑定具体的实现类,java编程思想第四版中有相关资料,用父类的引用来接受一个具体对象的实现会在运行时向上转型,通过该引用会调用具体子类实现的方法。

  

package cn.liu.java.myjava;
//这是people类的一个具体实现
public class China extends People{

    @Override
    public void say() {
        // TODO Auto-generated method stub
        System.out.println("I can speak Chinese");
    }
    
}

  

  下面是测试结果:

  好了,接下来就进入策略模式了,今天看了head first 设计模式的策略模式,其中对策略模式的描述是这样的:“策略模式定义了算法族(一系列的算法),分别封装起来,让他们可以互相替换,此模式让算法的变化独立于使用算法的客户。”我认为最重要的是可以互相替换,互相替换就是说具体算法的实现可以任意改变,这和我们使用抽象类或者接口有很大关系,我认为相对而言,使用接口来进行组装会更好。下面通过一个具体的例子来展示一个策略模式。

  还是用到上面的People类以及China类,其中的China不仅仅会讲中文,还能讲英文,德语,俄语,甚至更多。。。那么显然,我们可以在say的方法实现中来写具体的代码,但是这样的硬编码的方式对于后期添加功能或者维护带来的代价是巨大的,通常都不会这么做。我将say的这个方法提出来放到一个接口中。

package cn.liu.java.myjava.Interface;
//说话接口
public interface Say {
    public void sayDiffer();
}

  下面给出不同的几种实现:

package cn.liu.java.myjava;

import cn.liu.java.myjava.Interface.Say;
//说中文
public class sayChinese implements Say{

    public void sayDiffer() {
        // TODO Auto-generated method stub
        System.out.println("I can speak Chinese");
    }
    
}

 

 1 package cn.liu.java.myjava;
 2 
 3 import cn.liu.java.myjava.Interface.Say;
 4 //说英语
 5 public class SayEnglish implements Say{
 6 
 7     public void sayDiffer() {
 8         // TODO Auto-generated method stub
 9         System.out.println("I can speak English");
10     }
11 
12 }
 1 package cn.liu.java.myjava;
 2 
 3 import cn.liu.java.myjava.Interface.Say;
 4 //说德语
 5 public class SayGerman implements Say{
 6 
 7     public void sayDiffer() {
 8         // TODO Auto-generated method stub
 9         System.out.println("I can speak German");
10     }
11 
12 }
package cn.liu.java.myjava;

import cn.liu.java.myjava.Interface.Say;
//说俄语
public class SayRussian implements Say{

    public void sayDiffer() {
        // TODO Auto-generated method stub
        System.out.println("I can speak Russian");
    }
    
}

 

  下面给出People类的具体实现

package cn.liu.java.myjava;

import cn.liu.java.myjava.Interface.Say;


//因为People所有的方法在子类中的实现都是一样的,那么使用普通类就可以了
public class People {
    //人的基本属性,假设这里的人都是正常的
    private String mouse;
    private String foot;
    private String hand;
    private String eye;
    //说话接口
    private Say sayPerform;
    /*这里由于是根据接口来动态调用具体的语言,
    所以无论继承该类的什么对象,
    从实质上看来他们的说话方式是一样的,
    都是通过接口来调用*/
    public void say(){
        sayPerform.sayDiffer();
    }
    public void eat(){
        System.out.println("I can eat");
    }
    public void walk(){
        System.out.println("I can walk");
    }
    public void sleep(){
        System.out.println("I can sleep");
    }
    public String getMouse() {
        return mouse;
    }
    public void setMouse(String mouse) {
        this.mouse = mouse;
    }
    public String getFoot() {
        return foot;
    }
    public void setFoot(String foot) {
        this.foot = foot;
    }
    public String getHand() {
        return hand;
    }
    public void setHand(String hand) {
        this.hand = hand;
    }
    public String getEye() {
        return eye;
    }
    public void setEye(String eye) {
        this.eye = eye;
    }
    public Say getSayPerform() {
        return sayPerform;
    }
    //这里注入说话接口的具体实现类
    public void setSayPerform(Say sayPerform) {
        this.sayPerform = sayPerform;
    }
    
}

 

 

   正如我对抽象类和普通类的理解,抽象类是因为子类有不同的实现,才使用抽象类,但是如果所有的子类的方法都相同,那么使用普通类即可,所以上面的People我使用了非抽象类。

 

  下面给出China的代码

package cn.liu.java.myjava;

public class China extends People{
    //构造函数初始化为说中文
    public China(){
        setSayPerform(new sayChinese());
    }
}

 

 

  上面的China类继承了People类,并且重写了构造函数,初始化该类本来就应该会说中文。

  下面给出测试代码:

  

package cn.liu.java.myjava;

import org.junit.Test;

public class MyTest {
    @Test
    public void testName() throws Exception {
        People chinaPeople = new China();
        //可以看到直接构造出来的China就会说中文
        chinaPeople.say();
        //除了中文还能说英语,德语,俄语
        chinaPeople.setSayPerform(new SayEnglish());
        chinaPeople.say();
        chinaPeople.setSayPerform(new SayGerman());
        chinaPeople.say();
        chinaPeople.setSayPerform(new SayRussian());
        chinaPeople.say();
    }
}

 

测试结果:

  通过上面可以看到,实现了说不同语种的话,这样其实就是实现了说话策略的选择,也就是策略模式。

  所以,从上面看来,策略模式最重要的是接口的实现,通过接口动态调用不同的策略,这样比起硬编码的方式代码更加容易维护。

  希望该文章对看客会有帮助,如果有什么不对的地方,感谢您的提出,我会及时去思考并加以改正。

 

以上是关于设计模式之策略模式的主要内容,如果未能解决你的问题,请参考以下文章

java设计模式之策略模式

设计模式之策略模式

阿昌之丑陋代码优化通过策略模式&模版模式来优化Controller执行流程

设计模式之策略模式

设计模式之策略模式

Javascript设计模式总结之 -- 策略模式