Template method design pattern using Java 8
我想用Java 8新的默认方法重构模板方法。假设我在抽象类中有一个流程定义:
1 2 3 4 5 6 7 8 | public abstract class FlowManager{ public void startFlow(){ phase1(); phase2(); } public abstract void phase1(); public abstract void phase2(); } |
我有几个扩展上述流管理器的子类,每个子类实现自己的
1 2 3 4 5 6 7 8 | public interface FlowManager{ public default startFlow(){ this.phase1(); this.phase2(); } public void phase1(); public void phase2(); } |
你怎么认为?
使用带有默认方法的接口来实现模板方法模式在我看来是可疑的。
默认方法通常(尽管不总是)被实现者覆盖。如果将接口的默认方法用作模板方法,则重写方法可能会受到编程错误的影响,例如不调用
通常不打算重写模板方法。在Java类中,可以通过使用EDCOX1(2)方法来发出信号。接口不能有最终的方法;请参阅此问题了解基本原理。因此,最好使用一个抽象类来实现模板方法模式,最后一个方法作为模板。
除了前面的答案,注意还有更多的可能性。首先是将模板方法分成自己的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public interface Flow { void phase1(); void phase2(); } public final class FlowManager { private final Flow flow; public FlowManager(Flow flow) { this.flow = flow; } public void startFlow() { flow.phase1(); flow.phase2(); } } |
如果您已经在使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public final class FlowManager implements Flow { private final Flow flow; public FlowManager(Flow flow) { this.flow = flow; } public void startFlow() { flow.phase1(); flow.phase2(); } @Override public void phase1() { flow.phase1(); } @Override public void phase2() { flow.phase2(); } } |
通过这种方式,您可以明确地表示用户必须实现
Java 8增加了一个新的功能模式来解决问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public final class FlowManager { private final Runnable phase1; private final Runnable phase2; public FlowManager(Runnable phase1, Runnable phase2) { this.phase1 = phase1; this.phase2 = phase2; } public void startFlow() { phase1.run(); phase2.run(); } public void phase1() { phase1.run(); } public void phase2() { phase2.run(); } } |
这个代码甚至在Java 8之前工作,但是现在你可以使用lambdas或方法引用来创建EDCOX1 0。
您还可以组合这些方法:定义接口并提供一种从lambda构造它的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Java 8中的EDCOX1×8接口以类似的方式实现。现在,根据用户的偏好,他们要么直接实现接口,要么使用
//设计模板类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public class Template { protected interface MastSuppler{ List<Mast> apply(int projectId); } protected interface Transform<T>{ List<T> apply(List<Mast> masts); } protected interface PropertiesConsumer<T>{ void apply(List<T> properties); } public <T> void template(int projectId, MastSuppler suppler, Transform<T> transform, PropertiesConsumer<T> consumer){ System.out.println("projectId is" + projectId); //1.List<Mast> masts = step1(int projectId); List<Mast> masts = suppler.apply(projectId); //2.List<T> properties = step2(List<Mast> masts) List<T> properties = transform.apply(masts); //3.use or consume these properties(print to console ,save to datebase) consumer.apply(properties); } } |
//与客户端一起使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | public class Mast { public static void main(String[] args) { //1.save to db new Template().template(1, projectId->getMastsfromMongo(projectId), masts-> masts.stream().map(mast->mast.getName()).collect(Collectors.toList()), names->System.out.println("save names to db"+ names)); //new Template(1, id->Arrays, ); //2.print to console new Template().template(2, projectId->getMastsSomewhere(projectId), masts-> masts.stream().map(mast->mast.getLat()).collect(Collectors.toList()), names->System.out.println("print lons to console"+ names)); } private static List<Mast> getMastsfromMongo(int projectId){ Mast m1 = new Mast("1", 110, 23); Mast m2 = new Mast("2", 111, 13); return Arrays.asList(m1, m2); } private static List<Mast> getMastsSomewhere(int projectId){ Mast m1 = new Mast("3", 120, 53); Mast m2 = new Mast("4", 121, 54); return Arrays.asList(m1, m2); } private String name; private double lon; private double lat; public String getName() { return name; } public void setName(String name) { this.name = name; } public double getLon() { return lon; } public void setLon(double lon) { this.lon = lon; } public double getLat() { return lat; } public void setLat(double lat) { this.lat = lat; } public Mast(String name, double lon, double lat) { super(); this.name = name; this.lon = lon; this.lat = lat; } } |
我花了一些时间来研究Java 8中模板方法的实现,它就像魔术一样:在Java 8中实现它的方式有些不同。
1-父类没有在其主体中定义方法(稍后将在子类中实现),它在最终方法签名中将它们定义为参数。
2-基于以上内容,子类不必在其主体中提供实现,它将在实例化期间提供实现。
1 2 3 4 5 6 7 8 9 10 11 | import java.util.function.Consumer; public abstract class FlowManager<T> { public final void startFlow(T t, Consumer<T> phase1, Consumer<T> phase2){ phase1.accept(t); phase2.accept(t);; } } |
实施
1 2 3 4 | public class FlowManager2<T> extends FlowManagerJava8<String>{ } |
主类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import java.util.function.Consumer; public class Main { public static void main(String args[]){ new FlowManager2<String>().startFlow("Helo World", (String message)->System.out.println("Phase 1 :"+ message), (String message)->System.out.println("Phase 2 :"+ message)); Consumer<String> phase1 = (String message)-> System.out.println("Phase 1 :"+ message); Consumer<String> phase2 = (String message)-> System.out.println("Phase 2 :"+ message); new FlowManager2<String>().startFlow("Helo World", phase1, phase2); } } |
这两种方法都有效。
使用哪一个功能在很大程度上取决于您的
如果需要对某些状态建模,抽象类将允许您定义非静态字段。它还允许使用私有或受保护的方法。
另一方面,接口将使非相关类更容易实现,因为您不会被约束为单个继承。
Java的教程总结了它在"抽象类与接口"部分的比较好:
http://docs.oracle.com/javase/tutorial/java/iandi/abstract.html