我想使用java 8 new default方法重构模板方法。 假设我在抽象类中定义了一个流程:
public abstract class FlowManager{
public void startFlow(){
phase1();
phase2();
}
public abstract void phase1();
public abstract void phase2();
}
我有几个子类扩展了上面的流管理器,每个子类都实现了自己的phase1
和phase2
方法。我想知道将代码重构为这样的接口是否有意义:
public interface FlowManager{
public default startFlow(){
this.phase1();
this.phase2();
}
public void phase1();
public void phase2();
}
您怎么看?
答案 0 :(得分:10)
使用带有默认方法的接口来实现模板方法模式对我来说似乎很可疑。
默认方法通常(但并非总是)旨在被实现者覆盖。如果接口的默认方法被用作模板方法,那么重写方法将容易受到编程错误的影响,例如不调用super
方法,在错误的时间调用它,更改调用阶段的顺序,这些都是模板方法模式要避免的编程错误。
通常模板方法不旨在被覆盖。在Java类中,可以通过创建方法final
来发出信号。接口不能有最终方法;请参阅this question了解原理。因此,最好使用带有final方法的抽象类来实现模板方法模式。
答案 1 :(得分:3)
除了早期的答案外,还有更多的可能性。首先是将模板方法分成它自己的类:
file_uploads=On
如果您已经在使用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();
}
}
方法,那么您也可以实现FlowManager.phaseX
接口:
Flow
这样您明确表示用户必须实现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();
}
}
接口,但他们无法更改Flow
模板方法,因为它在最终类中声明。
Java 8添加了一个新的功能模式来解决您的问题:
startFlow
嗯,这段代码甚至可以在Java 8之前运行,但现在你可以使用lambdas或方法引用创建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();
}
}
,这样更方便。
您还可以组合这些方法:定义接口并提供从lambdas构建它的方法:
FlowManager
Java 8中的Collector
接口以类似的方式实现。现在,根据用户的偏好,他们可以直接实现接口,也可以使用public interface Flow {
void phase1();
void phase2();
static Flow of(Runnable phase1, Runnable phase2) {
return new Flow() {
@Override
public void phase1() {
phase1.run();
}
@Override
public void phase2() {
phase2.run();
}
};
}
}
并在那里传递lambdas或方法引用。
答案 2 :(得分:2)
//设计模板类
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);
}
}
//与客户一起使用
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;
}
}
答案 3 :(得分:0)
这两种方法都有效。
使用哪一个很大程度上取决于您FlowManager
将拥有的其他功能以及稍后将如何使用它。
如果您还需要为某些状态建模,那么抽象类将允许您定义非静态字段。它还允许使用私有或受保护的方法。
另一方面,接口可以使非相关类更容易实现,因为您不会被限制为单继承。
Java教程在&#34;抽象类与接口相比&#34;中总结了它。部分:
http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html
答案 4 :(得分:0)
花了一些时间来了解Java 8中的Template方法的实现,这就像魔术一样:)我们在Java 8中实现它的方式存在一些差异。
1-父类没有在它的主体中定义方法(稍后将在子类中实现),它将它们定义为最终方法签名中的参数。
2-基于以上所述,子类不必在其体内提供实现,它将在实例化期间提供实现。
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);;
}
}
实施
public class FlowManager2<T>
extends FlowManagerJava8<String>{
}
主要课程
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);
}
}