在学习策略模式之前我们首先看一段有趣的对话:
大师与门徒 <来自Head First设计模式>
大师:徒儿,告诉我你在面向对象的道路上,你学到了什么?
门徒:大师,我学到了,面向对象之路承诺了“复用”。
大师:继续说……
门徒:大师,借由继承,好东西可以再被利用,所以程序开发时间就会大幅减少,就好像在林中很快的砍竹子一样。
大师:徒儿呀!软件开发玩成“前”以及玩成"后",何者需要花费更多的时间啊。
门徒:答案是”后“,我们总是需要花许多时间在系统维护和变化上,比原先开发花的时间更多。
大师:对呀,那么我们是不是应该致力于提高可维护性和可拓展性上的复用呀?
门徒:是的大师,的确是如此。
大师:我觉得你还有很多东西要学,希望你在深入研究继承。你会发现,继承有它的问题,还有一些其他的方式可以达到。
所以本文的目标是为了提高代码的可维护性和可拓展性上的复用,并解决继承所产生的问题。
我们一般都会将相同事物或方法抽离出来,封装成一个类,然后让其他子类继承它。但是如果有些子类的具体实现方法不一样,就会导致很多子类需要覆盖并实现父类的方法。
这不仅会导致。子类的代码量变多,而且没有实现复用。如果需要修改这个方法,那么凡是用到了这个方法的子类都需要修改,给后期的维护带来了很多问题。那么怎么解决这个问题呢?接下来我用一个例子来教大家怎么解决。
上图定义了一个Person类,里面包含了run(),see(),以及skill()三个方法,因为所有人都具备上面三个特性,所以将其封装到Person类里面,让其他子类去继承它。
因为run()和see()方法,所有人都是一样的,所以继承并不会出现问题。但是skill()方法的的实现因人而异,不同的人具备不同的skill。按照以前的做法,我们会将skill方法教给子类去实现。但是这样产生的问题有:
1、代码无法复用,每个子类的skill方法都不同
2、子类中的代码量变大了。
3、如果后期维护需要修改skill方法,那么所有实现了skill方法的子类都需要修改,维护带来了困难。
所以我们不能将skill方法交给子类去实现,而是定义一个SkillManager接口来管理skill();让不同类型的子类去继承实现。然后在Person类的skill方法中调用SkillManager的skill方法就行了。而子类就不用关心skill方法的具体实现了,只需要获取对应SkillManager的实例就行了。这样做的好处:
1、提高了代码复用,子类无需实现skill方法。
2、减少了子类中的代码量。
3、如果后期维护需要修改skill方法,我们只需要修改SkillManager对应的子类就行了。而不需要去修改Person类的子类。
下面是代码实现:
public class Person {
public SkillManager skillmanager;
//每个人都能双脚,跑步的方式是一样的
public void run(){
System.out.println("我有双脚,我能跑步");
}
//每个人都有双眼,看东西的方式是一样的
public void see(){
System.out.println("我有眼睛,我能看见东西");
}
//每个人的职业不同所以 技能不一样,所以需要将该方式交给专门的类去实现
public void skill(){
skillmanager.skill();
}
public SkillManager getSkillmanager() {
return skillmanager;
}
public void setSkillmanager(SkillManager skillmanager) {
this.skillmanager = skillmanager;
}
}
public interface SkillManager {
public void skill();
}
public class Student extends Person{
public Student(){
skillmanager = new Learning();
}
}
public class Programmer extends Person{
public Programmer(){
skillmanager = new Coding();
}
}
public class Learning implements SkillManager{
@Override
public void skill() {
// TODO Auto-generated method stub
System.out.println("我拥有学习能力");
}
}
public class Coding implements SkillManager{
@Override
public void skill() {
// TODO Auto-generated method stub
System.out.println("我有写代码的能力");
}
}
public class Test {
public static void main(String[] args)
{
Student student = new Student();
Programmer programmer = new Programmer();
student.run();
programmer.run();
student.see();
programmer.see();
student.skill();
programmer.skill();
//这个学生学了编程之后 具备了编程的技能
student.setSkillmanager(new Coding());
student.skill();
}
/*
out~
我有双脚,我能跑步
我有双脚,我能跑步
我有眼睛,我能看见东西
我有眼睛,我能看见东西
我拥有学习能力
我有写代码的能力
我有写代码的能力
*/
}
策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的容器。
设计原则:多用组合,少用继承