模板方法设计模式使用 Java 8
Template method design pattern using Java 8
我想使用 java 8 个新的默认方法重构模板方法。
假设我有一个抽象定义的流程 class:
public abstract class FlowManager{
public void startFlow(){
phase1();
phase2();
}
public abstract void phase1();
public abstract void phase2();
}
我有几个子class扩展了上面的流管理器,每个子class实现了它自己的phase1
和phase2
方法。我想知道将代码重构为这样的接口是否有意义:
public interface FlowManager{
public default startFlow(){
this.phase1();
this.phase2();
}
public void phase1();
public void phase2();
}
你怎么看?
两种方法都可以。
使用哪一个在很大程度上取决于您的 FlowManager
将具有的其他功能以及您以后将如何使用它。
抽象 class 允许您定义非静态字段,如果您随后还需要对某些状态建模的话。它还将允许拥有私有或受保护的方法。
另一方面,接口可以让不相关的 class 实体更容易实现,因为您不会受限于单一继承。
Java 的教程在 "Abstract Classes Compared to Interfaces" 部分总结得很好:
http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html
使用带有默认方法的接口来实现模板方法模式对我来说似乎很可疑。
默认方法通常(尽管不总是)旨在被实现者覆盖。如果将接口的默认方法用作模板方法,则重写方法很容易出现编程错误,例如未调用 super
方法、在错误的时间调用它、更改调用阶段的顺序、等等。这些都是模板方法模式旨在避免的编程错误。
通常模板方法不会被覆盖。在 Java classes 中,这可以通过方法 final
发出信号。接口不能有最终方法;参见 this question 了解基本原理。因此,最好使用抽象 class 和最终方法作为模板来实现模板方法模式。
除了前面的答案之外,请注意还有更多的可能性。首先是将模板方法分离到它自己的 class:
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
接口,但他们无法更改 startFlow
模板方法,因为它在最终 class.[=22= 中声明]
Java8新增功能模式解决你的问题:
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 之前就可以工作,但现在您可以使用更方便的 lambda 或方法引用来创建 FlowManager
。
您还可以组合这些方法:定义接口并提供一种从 lambdas 构造它的方法:
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();
}
};
}
}
Java8 中的Collector
接口以类似的方式实现。现在,根据用户的偏好,他们可以直接实现接口或使用 Flow.of(...)
并在那里传递 lambda 或方法引用。
//设计模板class
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;
}
}
我花了一些时间来了解 Java 8 中模板方法的实现,这就像魔术 :) 我们在 Java 8 中实现它的方式有些不同.
1-父class没有在它的主体中定义方法(稍后将在子class中实现),它将它们定义为最终方法签名中的参数。
2- 基于以上,子 class 不必在其主体中提供实现,它将在实例化期间提供实现。
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>{
}
主要class
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 8 个新的默认方法重构模板方法。 假设我有一个抽象定义的流程 class:
public abstract class FlowManager{
public void startFlow(){
phase1();
phase2();
}
public abstract void phase1();
public abstract void phase2();
}
我有几个子class扩展了上面的流管理器,每个子class实现了它自己的phase1
和phase2
方法。我想知道将代码重构为这样的接口是否有意义:
public interface FlowManager{
public default startFlow(){
this.phase1();
this.phase2();
}
public void phase1();
public void phase2();
}
你怎么看?
两种方法都可以。
使用哪一个在很大程度上取决于您的 FlowManager
将具有的其他功能以及您以后将如何使用它。
抽象 class 允许您定义非静态字段,如果您随后还需要对某些状态建模的话。它还将允许拥有私有或受保护的方法。
另一方面,接口可以让不相关的 class 实体更容易实现,因为您不会受限于单一继承。
Java 的教程在 "Abstract Classes Compared to Interfaces" 部分总结得很好:
http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html
使用带有默认方法的接口来实现模板方法模式对我来说似乎很可疑。
默认方法通常(尽管不总是)旨在被实现者覆盖。如果将接口的默认方法用作模板方法,则重写方法很容易出现编程错误,例如未调用 super
方法、在错误的时间调用它、更改调用阶段的顺序、等等。这些都是模板方法模式旨在避免的编程错误。
通常模板方法不会被覆盖。在 Java classes 中,这可以通过方法 final
发出信号。接口不能有最终方法;参见 this question 了解基本原理。因此,最好使用抽象 class 和最终方法作为模板来实现模板方法模式。
除了前面的答案之外,请注意还有更多的可能性。首先是将模板方法分离到它自己的 class:
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
接口,但他们无法更改 startFlow
模板方法,因为它在最终 class.[=22= 中声明]
Java8新增功能模式解决你的问题:
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 之前就可以工作,但现在您可以使用更方便的 lambda 或方法引用来创建 FlowManager
。
您还可以组合这些方法:定义接口并提供一种从 lambdas 构造它的方法:
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();
}
};
}
}
Java8 中的Collector
接口以类似的方式实现。现在,根据用户的偏好,他们可以直接实现接口或使用 Flow.of(...)
并在那里传递 lambda 或方法引用。
//设计模板class
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;
}
}
我花了一些时间来了解 Java 8 中模板方法的实现,这就像魔术 :) 我们在 Java 8 中实现它的方式有些不同.
1-父class没有在它的主体中定义方法(稍后将在子class中实现),它将它们定义为最终方法签名中的参数。
2- 基于以上,子 class 不必在其主体中提供实现,它将在实例化期间提供实现。
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>{
}
主要class
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);
}
}