Themeplate Method
public abstract class Application { protected abstract void init(); protected abstract void idle(); protected abstract void cleanup(); private boolean isDone = false; protected void setDone(){ this.isDone = true; } protected boolean done(){ return isDone; } public void run(){ init(); while(!done()) idle(); cleanup(); } }
public class WorkTemplateMethod extends Application { public static void main(String[] args){ new WorkTemplateMethod().run(); } @Override protected void cleanup() { System.out.println("clean up."); } @Override protected void idle() { System.out.println("idle.");setDone(); } @Override protected void init() { System.out.println("init."); } }
Template Method模式展示了面向对象编程中诸多经典重用形式的一种。其中通用算法run()被放置在基类中,并且通过继承在不同的具体上下文中实现该通用算法。
但这项技术也是有代价的。继承是一种非常强的关系,派生类不可避免地要和它们的基类绑定在一起。
如果有个类Application2也需要WorkTemplateMethod中的idle()方法。然而却没法重用,由于继承了Application,就注定把WorkTemplateMethod永远地和Application绑定在了一起。
这时,我们就需要Strategy模式。
Strategy模式
public class ApplicationRunner { private Application app = null; public ApplicationRunner(Application app){ this.app = app; } public void run(){ app.init(); while(!app.done()) app.idle(); app.cleanup(); } }
public interface Application { public void init(); public void idle(); public void cleanup(); public boolean done(); }
public class WorkStrategy implements Application { private boolean isDone = false; @Override public void cleanup() { System.out.println("clean up."); } @Override public boolean done() { return isDone; } @Override public void idle() { System.out.println("idle."); isDone = true; } @Override public void init() { System.out.println("init."); } public static void main(String[] args) { new ApplicationRunner(new WorkStrategy()).run(); } }
WorkStrategy对ApplicationRunner一无所知(main方法作为一个调用的例子,通常它都会在测试类中),它不依赖于run逻辑的任何实现方式。这和TemplateMethod模式是不同的,WorkTemplateMethod完全依赖于它的父类Application中run的逻辑,因而违反了DIP原则,而Strategy方法中不包含这中依赖。因此,当有别的逻辑出现时,也可以复用WorkStrategy实例中的方法。
public class ApplicationRunner2 { private Application app = null; public ApplicationRunner2(Application app){ this.app = app; } public void go(){ //app.init(); 不调用init方法 while(!app.done()) app.idle(); app.cleanup(); } }
这样,只要使用new ApplicationRunner2(new WorkStrategy()).go()就可以了。
因此,Strategy模式比TemplateMethod模式多推荐了一个额外的好处。尽管TemplateMethod模式允许一个通用算法(run逻辑)操作多个可能的具体实现,但是由于Strategy模式完全遵循DIP原则,从而请允许每个具体实现都可以被多个不两只的通用算法(run逻辑或go逻辑)操纵。
一句话,少用继承,多用接口。