擁抱lombok
上邊的DTO代碼,已經讓我看的很累了,我相信讀者也是一樣,看到那麼多的Getter和Setter方法,太煩躁了,那時候有什麼方法可以簡化這些呢。
請擁抱lombok,它會幫助我們解決一些讓我們很煩躁的問題
去掉Setter和Getter
其實這個標題,我不太想說,因爲網上太多,但是因爲很多人告訴我,他們根本就不知道lombok的存在,所以爲了讓讀者更好的學習,我願意寫這樣一個例子:
@Setter
@Getter
public class UserDTO {
@NotNull
private String username;
@NotNull
private int age;
public User convertToUser(){
UserDTOConvert userDTOConvert = new UserDTOConvert();
User convert = userDTOConvert.convert(this);
return convert;
}
public UserDTO convertFor(User user){
UserDTOConvert userDTOConvert = new UserDTOConvert();
UserDTO convert = userDTOConvert.reverse().convert(user);
return convert;
}
private static class UserDTOConvert extends Converter<UserDTO, User> {
@Override
protected User doForward(UserDTO userDTO) {
User user = new User();
BeanUtils.copyProperties(userDTO,user);
return user;
}
@Override
protected UserDTO doBackward(User user) {
throw new AssertionError("不支持逆向轉化方法!");
}
}
}
看到了吧,煩人的Getter和Setter方法已經去掉了。
但是上邊的例子根本不足以體現lombok的強大。我希望寫一些網上很難查到,或者很少人進行說明的lombok的使用以及在使用時程序語義上的說明。
比如:@Data,@AllArgsConstructor,@NoArgsConstructor..這些我就不進行一一說明了,請大家自行查詢資料.
bean中的鏈式風格
什麼是鏈式風格?我來舉個例子,看下面這個Student的bean:
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public Student setName(String name) {
this.name = name;
return this;
}
public int getAge() {
return age;
}
public Student setAge(int age) {
return this;
}
}
仔細看一下set方法,這樣的設置便是chain的style,調用的時候,可以這樣使用:
Student student = new Student()
.setAge(24)
.setName("zs");
相信合理使用這樣的鏈式代碼,會更多的程序帶來很好的可讀性,那看一下如果使用lombok進行改善呢,請使用 @Accessors(chain = true),看如下代碼:
@Accessors(chain = true)
@Setter
@Getter
public class Student {
private String name;
private int age;
}
這樣就完成了一個對於bean來講很友好的鏈式操作。
靜態構造方法
靜態構造方法的語義和簡化程度真的高於直接去new一個對象。比如new一個List對象,過去的使用是這樣的:
List<String> list = new ArrayList<>();
看一下guava中的創建方式:
List<String> list = Lists.newArrayList();
Lists命名是一種約定(俗話說:約定優於配置),它是指Lists是List這個類的一個工具類,那麼使用List的工具類去產生List,這樣的語義是不是要比直接new一個子類來的更直接一些呢,答案是肯定的,再比如如果有一個工具類叫做Maps,那你是否想到了創建Map的方法呢:
HashMap<String, String> objectObjectHashMap = Maps.newHashMap();
好了,如果你理解了我說的語義,那麼,你已經向成爲java程序員更近了一步了。
再回過頭來看剛剛的Student,很多時候,我們去寫Student這個bean的時候,他會有一些必輸字段,比如Student中的name字段,一般處理的方式是將name字段包裝成一個構造方法,只有傳入name這樣的構造方法,才能創建一個Student對象。
接上上邊的靜態構造方法和必傳參數的構造方法,使用lombok將更改成如下寫法(@RequiredArgsConstructor 和 @NonNull):
@Accessors(chain = true)
@Setter
@Getter
@RequiredArgsConstructor(staticName = "ofName")
public class Student {
@NonNull private String name;
private int age;
}
測試代碼:
Student student = Student.ofName("zs");
這樣構建出的bean語義是否要比直接new一個含參的構造方法(包含 name的構造方法)要好很多。
當然,看過很多源碼以後,我想相信將靜態構造方法ofName換成of會先的更加簡潔:
@Accessors(chain = true)
@Setter
@Getter
@RequiredArgsConstructor(staticName = "of")
public class Student {
@NonNull private String name;
private int age;
}
測試代碼:
Student student = Student.of("zs");
當然他仍然是支持鏈式調用的:
Student student = Student.of("zs").setAge(24);
這樣來寫代碼,真的很簡潔,並且可讀性很強。
使用builder
Builder模式我不想再多解釋了,讀者可以看一下《Head First》(設計模式) 的建造者模式。
今天其實要說的是一種變種的builder模式,那就是構建bean的builder模式,其實主要的思想是帶着大家一起看一下lombok給我們帶來了什麼。
看一下Student這個類的原始builder狀態:
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static Builder builder(){
return new Builder();
}
public static class Builder{
private String name;
private int age;
public Builder name(String name){
this.name = name;
return this;
}
public Builder age(int age){
this.age = age;
return this;
}
public Student build(){
Student student = new Student();
student.setAge(age);
student.setName(name);
return student;
}
}
}
調用方式:
Student student = Student.builder().name("zs").age(24).build();
這樣的builder代碼,讓我是在噁心難受,於是我打算用lombok重構這段代碼:
@Builder
public class Student {
private String name;
private int age;
}
調用方式:
Student student = Student.builder().name("zs").age(24).build();
代理模式
正如我們所知的,在程序中調用rest接口是一個常見的行爲動作,如果你和我一樣使用過spring 的RestTemplate,我相信你會我和一樣,對他拋出的非http狀態碼異常深惡痛絕。
所以我們考慮將RestTemplate最爲底層包裝器進行包裝器模式的設計:
public abstract class FilterRestTemplate implements RestOperations {
protected volatile RestTemplate restTemplate;
protected FilterRestTemplate(RestTemplate restTemplate){
this.restTemplate = restTemplate;
}
//實現RestOperations所有的接口
}
然後再由擴展類對FilterRestTemplate進行包裝擴展:
public class ExtractRestTemplate extends FilterRestTemplate {
private RestTemplate restTemplate;
public ExtractRestTemplate(RestTemplate restTemplate) {
super(restTemplate);
this.restTemplate = restTemplate;
}
public <T> RestResponseDTO<T> postForEntityWithNoException(String url, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException {
RestResponseDTO<T> restResponseDTO = new RestResponseDTO<T>();
ResponseEntity<T> tResponseEntity;
try {
tResponseEntity = restTemplate.postForEntity(url, request, responseType, uriVariables);
restResponseDTO.setData(tResponseEntity.getBody());
restResponseDTO.setMessage(tResponseEntity.getStatusCode().name());
restResponseDTO.setStatusCode(tResponseEntity.getStatusCodeValue());
}catch (Exception e){
restResponseDTO.setStatusCode(RestResponseDTO.UNKNOWN_ERROR);
restResponseDTO.setMessage(e.getMessage());
restResponseDTO.setData(null);
}
return restResponseDTO;
}
}
包裝器ExtractRestTemplate很完美的更改了異常拋出的行爲,讓程序更具有容錯性。在這裏我們不考慮ExtractRestTemplate完成的功能,讓我們把焦點放在FilterRestTemplate上,“實現RestOperations所有的接口”,這個操作絕對不是一時半會可以寫完的,當時在重構之前我幾乎寫了半個小時,如下:
public abstract class FilterRestTemplate implements RestOperations {
protected volatile RestTemplate restTemplate;
protected FilterRestTemplate(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Override
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
return restTemplate.getForObject(url,responseType,uriVariables);
}
@Override
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
return restTemplate.getForObject(url,responseType,uriVariables);
}
@Override
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
return restTemplate.getForObject(url,responseType);
}
@Override
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
return restTemplate.getForEntity(url,responseType,uriVariables);
}
//其他實現代碼略。。。
}
我相信你看了以上代碼,你會和我一樣覺得噁心反胃,後來我用lombok提供的代理註解優化了我的代碼(@Delegate):
@AllArgsConstructor
public abstract class FilterRestTemplate implements RestOperations {
@Delegate
protected volatile RestTemplate restTemplate;
}
這幾行代碼完全替代上述那些冗長的代碼。
是不是很簡潔,做一個擁抱lombok的程序員吧。