大部分新代碼的冗長無關函數式編程後
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.lang.reflect.Method;
import org.springframework.beans.BeanUtils;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.util.UriComponentsBuilder;
public String functionalBuildRestUri() throws Exception {
final UriComponentsBuilder uriBuilder = UriComponentsBuilder.newInstance().scheme("https")
.host("foo.com").path("/offers");
//here is the functional
List<PropertyDescriptor> propDescList = Arrays.asList(BeanUtils.getPropertyDescriptors(getClass()));
//this part is readable and precis, but to enable it had to add 4 methods
propDescList.stream().filter(notClassProp())
.filter(notNullPropValue())
.collect(Collectors.toMap(PropertyDescriptor::getName, propValue()))//conversion to map doesn't feel good to me how can I avoid it?
.forEach(buildRestParam(uriBuilder));
return uriBuilder.build().toUriString();
}
public String imperativeBuildRestUri() throws Exception {
final UriComponentsBuilder uriBuilder = UriComponentsBuilder.newInstance().scheme("https")
.host("foo.com").path("/offers");
PropertyDescriptor[] propDescArray = BeanUtils.getPropertyDescriptors(getClass());
for (PropertyDescriptor propDesc : propDescArray) {
String propName = propDesc.getName();
if (!propName.equals("class")) {
Method getPropMethod = propDesc.getReadMethod();
Object propValue = getPropMethod.invoke(this);
if (propValue != null) {
if(propValue instanceof Date){
String dateStr = new SimpleDateFormat(DATE_FORMAT).format((Date)propValue);
uriBuilder.queryParam(propName, ":"+dateStr);
}else{
uriBuilder.queryParam(propName, propValue);
}
}
}
}
return uriBuilder.build().toUriString();
}
所有這些方法已被添加。你已經重構了代碼,將每個lambda表達式放入它自己的方法中,當然,這破壞了lambda表達式的一個主要優點,即緊湊性。即使代碼足夠複雜以證明方法的創建,該方法應該執行實際工作,然後,可以在需要函數的地方使用方法引用。
該方法進一步受到不必要的(甚至不鼓勵,因爲在返回類型中)使用通配符。您還使用了詳細語法parameter -> { return expression; }
,其中parameter -> expression
將是可能的。
還有其他問題,如不必要地創建用於每個異常類型的獨特catch
子句,當所有做同樣的或創建Stream
而不是直接流陣列之上或具有代碼重複之前包裹所述陣列分成List
,所述最後一點適用於命令式和功能式。
你可以這樣寫:
public String functionalBuildRestUri() throws Exception {
final UriComponentsBuilder uriBuilder = UriComponentsBuilder.newInstance()
.scheme("https").host("foo.com").path("/offers");
Function<PropertyDescriptor, Object> propValue = propDesc -> {
try { return propDesc.getReadMethod().invoke(HotelOfferSearchCommand.this); }
catch(ReflectiveOperationException e) { throw new RuntimeException(e); }
};
Arrays.stream(BeanUtils.getPropertyDescriptors(getClass()))
.filter(propDesc -> !propDesc.getName().equals("class"))
.filter(propDesc -> propValue.apply(propDesc) != null)
.forEach(propDesc -> {
Object value = propValue.apply(propDesc);
if (value instanceof Date)
value = ":"+new SimpleDateFormat(DATE_FORMAT).format(value);
uriBuilder.queryParam(propDesc.getName(), value);
});
return uriBuilder.build().toUriString();
}
沒有任何額外的方法。
這可能不是最好的選擇,因爲確實存在一個缺陷,即沒有元組或對的類型來保存要通過流的兩個值。通過使用Map.Entry
作爲替身,而不是填充一個Map
,我們可以表達的操作
public String functionalBuildRestUri() throws Exception {
final UriComponentsBuilder uriBuilder = UriComponentsBuilder.newInstance()
.scheme("https").host("foo.com").path("/offers");
Function<PropertyDescriptor, Object> propValue = propDesc -> {
try { return propDesc.getReadMethod().invoke(HotelOfferSearchCommand.this); }
catch(ReflectiveOperationException e) { throw new RuntimeException(e); }
};
Arrays.stream(BeanUtils.getPropertyDescriptors(getClass()))
.filter(propDesc -> !propDesc.getName().equals("class"))
.map(propDesc -> new AbstractMap.SimpleImmutableEntry<>(
propDesc.getName(), propValue.apply(propDesc)))
.filter(entry -> entry.getValue() != null)
.forEach(entry -> {
Object value = entry.getKey();
if (value instanceof Date)
value = ":"+new SimpleDateFormat(DATE_FORMAT).format(value);
uriBuilder.queryParam(entry.getKey(), value);
});
return uriBuilder.build().toUriString();
}
,或者
Arrays.stream(BeanUtils.getPropertyDescriptors(getClass()))
.filter(propDesc -> !propDesc.getName().equals("class"))
.map(propDesc -> new AbstractMap.SimpleImmutableEntry<>(
propDesc.getName(), propValue.apply(propDesc)))
.filter(entry -> entry.getValue() != null)
.map(e -> e.getValue() instanceof Date?
new AbstractMap.SimpleImmutableEntry<>(e.getKey(),
":"+new SimpleDateFormat(DATE_FORMAT).format(e.getValue())):
e)
.forEach(entry -> uriBuilder.queryParam(entry.getKey(), entry.getValue()));
有了這兩個變種,該propValue
功能只計算一次每個元素,而不是第一個變體中的兩倍,以及您的原始代碼,其中兩項均爲null
屬性值的檢查以及終端操作對其進行評估。
請注意,還有改進的餘地,例如,沒有理由在format
操作之後添加":"
,因爲您可以首先將冒號作爲格式模式字符串的一部分。
這是否是對循環的改進,是您必須自行決定的。並非每個代碼都必須重寫爲功能樣式。至少,正如上面的例子所示,它不必比命令式代碼更大......
非常好的信息!由於'propValue.apply(propDesc)'在最後2個選項中只被調用一次,所以消除變量'propValue'並使用Lambda表達式可能會更好,您怎麼看? –
'.MAP(propDesc - > { \t \t \t嘗試{ \t \t \t \t \t \t對象值= propDesc.getReadMethod()調用(HotelOfferSearchCommand.this); \t \t \t \t \t \t返回新AbstractMap.SimpleImmutableEntry <>(propDesc.getName(),值); \t \t \t \t \t}趕上(ReflectiveOperationException E){ \t \t \t \t \t \t e.printStackTrace(); \t \t \t \t \t \t throw new RuntimeException(e); \t \t \t \t \t} \t \t} \t \t)' –
可以內聯'propValue'功能;這是否更好(或更可讀)是值得商榷的。 「try ... catch」塊安靜笨拙。另一種方法是調用「方法」並處理檢查的異常的實用方法。調用實用程序方法可能會發生在lambda表達式中。 – Holger