如何在 Java 的函数中应用 DRY 原则?
How to apply DRY principle with Functions in Java?
在 JSF 应用程序中,我有一些具有多个 return 点的方法,如下所示:
public String Method1() {
...
if (condition1) {
elaborate1();
return "Case1";
}
if (condition2) {
elaborate2();
return "Case2";
}
if (condition3) {
elaborate3();
return "Case3";
}
...
}
条件和后续的阐述对于更多的方法是通用的,我想只写一次。不幸的是,由于多个 return 点,重构并不容易。
我对 函数式接口 不太满意,但我希望我可以使用它们来重构上面的代码。我的想法是这样写:
String elaboration1 ( T NextElaborations) {
if (condition1) {
elaborate1();
return "Case1";
}
return NextElaborations.get();
}
可能吗?我卡住了,互联网上的大多数示例都使用 lambda 而不是方法。
更新 1
@亚历山大·伊万琴科
在评估您的答案之前,我需要研究您提供的链接,我对流不熟悉。
发布问题后,我对函数式接口进行了一些练习,最后得到以下伪 java 代码:
@FunctionalInterface
public interface MethodComposition {
String Execute(Supplier<String> supplier)
}
...
String FinalStep() {
FinalElaboration();
return "finalResult";
}
String ElaborationX(Supplier<String> supplier) {
if (conditionX) {
elaborationX();
return "ResultX";
}
return supplier.get();
}
...
public String Method1() {
Supplier<String> finalSupplier = this::FinalStep;
MethodComposition stepX = this::ElaborationX;
...
return step1.Execute(() ->
step2.Execute(() ->
...
stepN.Execute(finalSupplier)));
}
尽管语法丑陋,但它与我想要的非常接近,我需要更多的灵活性,因为 ElaborationX() 与 Method1 非常相似但不相等 和 Method_N.
您可以定义一个 集合 的 函数 来表示这些 条件 并将这个集合封装在里面a class 通过提供方便的交互方式。这将使该逻辑可重用。
例如,您可以创建一个 map 关联一个 Predicate
,它是一个表示条件的 function解析为 boolean
值,具有特定的 return 值 。集合应该有一个可预测的迭代顺序以确保应用条件的顺序,因此您可以使用 LinkedHashMap
来强加迭代顺序。
并且您可以通过将单独的条件表示为对象来实现更大的灵活性,其中包含谓词和一些附加属性。这将允许对条件进行分类并构建不同的谓词组合。
您的方法 Method1()
带有一系列 if
语句,可以用映射条目流代替。或者,如果您对流感到不舒服,您可以使用普通的 for
-loop 来代替它。关键是你需要一个方法来对 functions.
的集合执行迭代
I'm not comfortable with functional interfaces, but ...
I'm stuck, most examples on internet use lambdas and not methods.
无需产生许多相互依赖的方法,如elaboration1()
、elaboration2()
等,这种方法不灵活。而且这种方法与 Java 中的 函数式接口 (您已经提到)和一般的函数式方法编程无关。要重构您的代码以使其在 stile 中发挥作用,除了真正熟悉函数式编程之外别无他法。
我鼓励你看看 lambda expressions 上的这些官方教程和
streams.
stream-logic的描述:
- 确定具有匹配谓词的条目
true
。
- 通过应用操作
findFirst()
. 将此条目作为类型 Optional
的对象(如果未找到结果将为空)来终止流管道
- 转换optional的内容和return结果值(there are a lot of ways to deal with Optional, take a look at this page for more information)
实现可能如下所示:
public class FunctionStorage {
public static final Map<Predicate<String>, String> conditions = new LinkedHashMap<>(); // LinkedHashMap insures the order of iteration
static {
conditions.put(s -> false, "1");
conditions.put(s -> true, "2");
conditions.put(s -> false, "3");
}
public static Stream<Map.Entry<Predicate<String>, String>> getConditions() {
return conditions.entrySet().stream();
}
}
main()
public static void main(String[] args) {
String result = FunctionStorage.getConditions() // a stream of entries
.filter(entry -> entry.getKey().test("someValue")) // filtering the entry containing the matching condition
.findFirst()
.map(Map.Entry::getValue) // extracting the return value
.orElse("defaultValue"); // orElseThrow() depending on your needs
}
在 JSF 应用程序中,我有一些具有多个 return 点的方法,如下所示:
public String Method1() {
...
if (condition1) {
elaborate1();
return "Case1";
}
if (condition2) {
elaborate2();
return "Case2";
}
if (condition3) {
elaborate3();
return "Case3";
}
...
}
条件和后续的阐述对于更多的方法是通用的,我想只写一次。不幸的是,由于多个 return 点,重构并不容易。
我对 函数式接口 不太满意,但我希望我可以使用它们来重构上面的代码。我的想法是这样写:
String elaboration1 ( T NextElaborations) {
if (condition1) {
elaborate1();
return "Case1";
}
return NextElaborations.get();
}
可能吗?我卡住了,互联网上的大多数示例都使用 lambda 而不是方法。
更新 1 @亚历山大·伊万琴科 在评估您的答案之前,我需要研究您提供的链接,我对流不熟悉。
发布问题后,我对函数式接口进行了一些练习,最后得到以下伪 java 代码:
@FunctionalInterface
public interface MethodComposition {
String Execute(Supplier<String> supplier)
}
...
String FinalStep() {
FinalElaboration();
return "finalResult";
}
String ElaborationX(Supplier<String> supplier) {
if (conditionX) {
elaborationX();
return "ResultX";
}
return supplier.get();
}
...
public String Method1() {
Supplier<String> finalSupplier = this::FinalStep;
MethodComposition stepX = this::ElaborationX;
...
return step1.Execute(() ->
step2.Execute(() ->
...
stepN.Execute(finalSupplier)));
}
尽管语法丑陋,但它与我想要的非常接近,我需要更多的灵活性,因为 ElaborationX() 与 Method1 非常相似但不相等 和 Method_N.
您可以定义一个 集合 的 函数 来表示这些 条件 并将这个集合封装在里面a class 通过提供方便的交互方式。这将使该逻辑可重用。
例如,您可以创建一个 map 关联一个 Predicate
,它是一个表示条件的 function解析为 boolean
值,具有特定的 return 值 。集合应该有一个可预测的迭代顺序以确保应用条件的顺序,因此您可以使用 LinkedHashMap
来强加迭代顺序。
并且您可以通过将单独的条件表示为对象来实现更大的灵活性,其中包含谓词和一些附加属性。这将允许对条件进行分类并构建不同的谓词组合。
您的方法 Method1()
带有一系列 if
语句,可以用映射条目流代替。或者,如果您对流感到不舒服,您可以使用普通的 for
-loop 来代替它。关键是你需要一个方法来对 functions.
I'm not comfortable with functional interfaces, but ...
I'm stuck, most examples on internet use lambdas and not methods.
无需产生许多相互依赖的方法,如elaboration1()
、elaboration2()
等,这种方法不灵活。而且这种方法与 Java 中的 函数式接口 (您已经提到)和一般的函数式方法编程无关。要重构您的代码以使其在 stile 中发挥作用,除了真正熟悉函数式编程之外别无他法。
我鼓励你看看 lambda expressions 上的这些官方教程和 streams.
stream-logic的描述:
- 确定具有匹配谓词的条目
true
。 - 通过应用操作
findFirst()
. 将此条目作为类型 - 转换optional的内容和return结果值(there are a lot of ways to deal with Optional, take a look at this page for more information)
Optional
的对象(如果未找到结果将为空)来终止流管道
实现可能如下所示:
public class FunctionStorage {
public static final Map<Predicate<String>, String> conditions = new LinkedHashMap<>(); // LinkedHashMap insures the order of iteration
static {
conditions.put(s -> false, "1");
conditions.put(s -> true, "2");
conditions.put(s -> false, "3");
}
public static Stream<Map.Entry<Predicate<String>, String>> getConditions() {
return conditions.entrySet().stream();
}
}
main()
public static void main(String[] args) {
String result = FunctionStorage.getConditions() // a stream of entries
.filter(entry -> entry.getKey().test("someValue")) // filtering the entry containing the matching condition
.findFirst()
.map(Map.Entry::getValue) // extracting the return value
.orElse("defaultValue"); // orElseThrow() depending on your needs
}