在進入正題以前,說一些廢話,談談對於個人前一篇文章被移出博客園首頁的想法。不談我對於其餘首頁文章的見解,光從我自身找找緣由。下面分析下可能的緣由:java
不給本身找理由,我認可本身水平有限、能力不足,但願本身之後努力能達到要求,歡迎你們在評論區和我交流。程序員
在前一篇文章中,我已經用分頁查詢的實例,說明了如何用模板方法模式去消除代碼重複。那個例子相對比較簡單,下面分享一個稍微難一點的例子,加深你們的理解。數據庫
說一個我工做中遇到的場景,有一個消息隊列(MQ)監聽上游系統推送過來的消息,只對接數據庫表中的幾個字段,大概流程以下:app
假設和上游系統對接的是用戶模塊,用戶表中有id、name、age、sex四個字段,id是惟一鍵,如今只要對接id、name、age四個字段。ui
@Data @NoArgsConstructor @AllArgsConstructor public class User { private Integer id; private String name; private Integer age; private Integer sex; }
public class UserDAO { private User oneUser = new User(1, "u1", 18, 1); public User getById(Integer id) { if (id != 1) { return null; } return oneUser; } public void updateById(User user) { Integer id = user.getId(); if (id == null || id != 1) { return; } if (user.getName() != null) { oneUser.setName(user.getName()); } if (user.getAge() != null) { oneUser.setAge(user.getAge()); } if (user.getSex() != null) { oneUser.setSex(user.getSex()); } } }
/** * 對比兩個對象,獲取用於更新的對象 * * @param toUpdate 要更新的對象 * @param original 原來的對象 * @return 用於更新的對象。若是要比較的字段都同樣則返回null */ private User getUserUpdate(User toUpdate, User original) { User updateUser = new User(); if (!original.getName().equals(toUpdate.getName())) { updateUser.setName(toUpdate.getName()); } if (!original.getAge().equals(toUpdate.getAge())) { updateUser.setAge(toUpdate.getAge()); } if (Stream.of(updateUser.getName(), updateUser.getAge()) .allMatch(Objects::isNull)) { // 沒有更新 return null; } return updateUser; }
MQ監聽的方法this
// MQ監聽方法接收到上游系統推送過來的一條記錄,只對接id、name、age字段。id是惟一鍵(實際中通常不會是id) User toUpdate = new User(1, "uu1", 20, null); System.out.println("toUpdate user: " + toUpdate); // 根據惟一鍵鍵去數據庫查詢查詢查詢 User original = userDAO.getById(toUpdate.getId()); System.out.println("original user: " + original); // 若是查到則用上游系統推送的記錄去更新這條記錄,若是沒查到則插入一條新記錄 if (original != null) { // 對比兩個對象,獲取用於更新的對象 User updateUser = getUserUpdate(toUpdate, original); // 若是兩對象要比較的字段都同樣就不操做,不然更新不一樣的字段 if (updateUser != null) { // 設置主鍵id updateUser.setId(toUpdate.getId()); System.out.println("update user: " + updateUser); // 根據主鍵id去更新 userDAO.updateById(updateUser); System.out.println("updated user: " + userDAO.getById(toUpdate.getId())); } } else { // 插入一條新記錄 }
運行結果:code
toUpdate user: User(id=1, name=uu1, age=20, sex=null) original user: User(id=1, name=u1, age=18, sex=1) update user: User(id=1, name=uu1, age=20, sex=null) updated user: User(id=1, name=uu1, age=20, sex=1)
分析下上述代碼,核心是getUserUpdate
方法,若是實際中對接的字段有不少,那麼這個方法中的代碼很容易出錯。這個方法看起來重複的代碼是:先比較兩對象的同一字段是否相等,若是相等則設值。能不能把這塊代碼提煉出一個方法來,請思考一會,而後看下面的章節。對象
/** * 對象更新比較器 * * @param <T> 待比較的對象類型 */ public final class UpdateDiffer<T> { /** * 原來的對象 */ private final T original; /** * 要更新的對象 */ private final T toUpdate; /** * 」原來的對象「和「要更新的對象」比較出來用於更新的對象 */ private final T difference; /** * 須要比較的字段的get方法 */ private final List<Function<T, ?>> getMethodList; /** * Initializes a newly created UpdateDiffer object * * @param original 原來的對象 * @param toUpdate 要更新的對象 * @param tConstructor T類型對象構造方法 */ public UpdateDiffer(T original, T toUpdate, Supplier<T> tConstructor) { Objects.requireNonNull(original); Objects.requireNonNull(toUpdate); Objects.requireNonNull(tConstructor); this.original = original; this.toUpdate = toUpdate; this.difference = tConstructor.get(); getMethodList = new ArrayList<>(); } /** * 比較字段是否相同,若是不一樣,把要更新的對象字段值設置到difference對象裏 * * @param getMethod get方法 * @param setMethod set方法 * @param <R> get方法的返回值類型/set方法參數類型 * @return this */ public <R> UpdateDiffer<T> diffing(Function<T, R> getMethod, BiConsumer<T, R> setMethod) { Objects.requireNonNull(getMethod); Objects.requireNonNull(setMethod); R toUpdateValue = getMethod.apply(toUpdate); R originalValue = getMethod.apply(original); Objects.requireNonNull(originalValue, "數據庫中的字段不該該爲null"); if (!originalValue.equals(toUpdateValue)) { setMethod.accept(difference, toUpdateValue); } // 保存已經調用的get方法 getMethodList.add(getMethod); return this; } /** * 獲取」原來的對象「和「要更新的對象」比較出來的對象,用於去數據庫更新(更新前還要再設置id等字段)。 * * @return 」原來的對象「和「要更新的對象」比較出來用於更新的對象。若是「原來的對象」和「要更新的對象」中全部要比較的字段都相同,返回null */ public T diff() { // 若是difference對象中全部要比較的字段都爲null if ( getMethodList.stream() .map(getFunction -> getFunction.apply(difference)) .allMatch(Objects::isNull) ) { return null; } return this.difference; } }
用如下的代碼替換上一節中的getUserUpdate
方法隊列
User updateUser = new UpdateDiffer<>(original, toUpdate, User::new) .diffing(User::getName, User::setName) .diffing(User::getAge, User::setAge) .diff();
運行結果和上一節的結果同樣。get
是否是以爲代碼比以前的清楚了不少,並且不容易出錯。代碼沒啥解釋的,我是由JDK中的Comparator
啓發想出來的,不明白的能夠先看看Comparator
的用法。
模板模式至此就介紹完了,你們的"CTRL"、"C"、"V"鍵會不會所以增長几年壽命 : )