摘要:解決思路或生產對象,扮演生產者的角色而消費對象,扮演消費者的角色。正常情況下它們生產對象,而異常情況下,則拋出異常。重構的思路在于將異常處理更加明晰化,讓生產者與消費者之間的關系流水化。容器化其中,與包內私有,對外不公開。
場景
以一個簡化了的用戶登錄的鑒權流程,流程大體如下:
首先嘗試本站鑒權,如果失敗,再嘗試twiter的方式恢復;
之后再進行Two Factor認證;
快速實現按照流程定義,可以快速實現第一個版本。這段代碼充滿了很多的壞味道,職責不單一,復雜的異常分支處理,流程的脈絡不夠清晰等等,接下來我們嘗試一種很特別的重構方式來改善設計。
public boolean authenticate(String id, String passwd) { User user = null; try { user = login(id, passwd); } catch (AuthenticationException e) { try { user = twiterLogin(id, passwd); } catch (AuthenticationException et) { return false; } } return twoFactor(user); }解決思路
login或twiterLogin生產User對象,扮演生產者的角色;而twoFactor消費User對象,扮演消費者的角色。
最難處理的就是login或twiterLogin可能會存在異常處理。正常情況下它們生產User對象,而異常情況下,則拋出異常。
重構的思路在于將異常處理更加明晰化,讓生產者與消費者之間的關系流水化。為此,可以將User對象,可能拋出的異常進行容器化,它們形成互斥關系,不能共生于這個世界。
容器化public interface Try{ static Try trying(ExceptionalSupplier s) { try { return new Success<>(s.get()); } catch (Exception e) { return new Failure<>(e); } } boolean isFailure(); default boolean isSuccess() { return !isFailure(); } T get(); }
其中,Success與Failure包內私有,對外不公開。
final class Successimplements Try { private final T value; Success(T value) { this.value = value; } @Override public boolean isFailure() { return false; } @Override public T get() { return value; } }
import java.util.NoSuchElementException; final class Failure生產者implements Try { private final Exception e; Failure(Exception e) { this.e = e; } @Override public boolean isFailure() { return true; } @Override public T get() { throw new NoSuchElementException("Failure.get"); } }
與JDK8標準庫中不一樣,它能處理受檢異常。
@FunctionalInterface public interface ExceptionalSupplier第一次重構{ T get() throws E; }
import static Try.trying; public boolean authenticate(String id, String passwd) { Try鏈式DSLuser = trying(() -> login(id, passwd)); if (user.isFailure()) { user = trying(() -> twiterLogin(id, passwd)); } return user.isSuccess() && twoFactor(user.get()); }
上述trying的應用,使用狀態查詢的Try.isFailure/isSuccess方法顯得有些笨拙,可以通過構造鏈式的DSL改善設計。
public interface Try{ ...... Try recover(Function > f); }
final class Successimplements Try { ...... @Override @SuppressWarnings("unchecked") public Try recover(Function > f) { return (Try)this; } }
final class Failure第二次重構implements Try { private final Exception e; Failure(Exception e) { this.e = e; } @Override public Try recover(Function > f) { try { return f.apply(e); } catch (Exception e) { return new Failure(e); } } }
使用recover關鍵字,進一步地改善表達力。首先試圖login生產一個User,如果失敗從twiterLogin中恢復;最后由twoFactor消費User對象。
public boolean authenticate(String id, String passwd) { Try徹底鏈化user = trying(() -> login(id, passwd)) .recover(e -> trying(() -> twiterLogin(id, passwd))); return user.isSuccess() && twoFactor(user.get()); }
public interface Try{ ...... default T getOrElse(T defaultValue) { return isSuccess() ? get() : defaultValue; } Try map(Function f); }
final class Successimplements Try { ...... @Override public Try map(Function f) { try { return new Success(f.apply(value)); } catch (Exception e) { return new Failure(e); } } }
final class Failure第三次重構implements Try { ...... @Override @SuppressWarnings("unchecked") public Try map(Function f) { return (Try)this; } }
public boolean authenticate(String id, String passwd) { return trying(() -> login(id, passwd)) .recover(e -> trying(() -> twiterLogin(id, passwd))) .map(user -> twoFactor(user)) .getOrElse(false); }應用Scala
Java8 Lambda表達式() -> login(id, passwd)中空的參數列表頗讓人費解,但這是Java8語言本身限制的,應用Scala表達力可進一步提高。
import scala.util.Try def authenticate(id: String, passwd: String): Boolean = { Try(login(id, passwd)) .recover{ case e: => twiterLogin(id, passwd) } .map(twoFactor) .getOrElse(false) }Try的本質
Try是Monad的一個應用,使得異常的處理可以在流水線上傳遞。使用Scala的Try簡化實現版本是這樣的。
sealed abstract class Try[+T] { def isSuccess: Boolean def get: T } final case class Failure[+T](val exception: Throwable) extends Try[T] { def isSuccess: Boolean = false def get: T = throw exception } final case class Success[+T](value: T) extends Try[T] { def isSuccess: Boolean = true def get = value } object Try { def apply[T](r: => T): Try[T] = { try Success(r) catch { case NonFatal(e) => Failure(e) } } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65595.html
摘要:注不做翻譯是中最小的構建部件。在里渲染讓我們看一下在下面有在你文件中無處不在的標簽我們會把這元素成為元素因為的所有東西都會放在這個元素里面。通過方法,我們能吧渲染到我們根節點上。更新被渲染的是不可變的。 下面是react官方文檔的個人翻譯,如有翻譯錯誤,請多多指出原文地址:https://facebook.github.io/re...特別感謝Hevaen,同時也向豪大React群所有...
摘要:例如,將函數修改為小恐龍眨眼這樣小恐龍會不停的眨眼睛。小恐龍的開場動畫下面來實現小恐龍對鍵盤按鍵的響應。接下來還需要更新動畫幀才能實現小恐龍的奔跑動畫。 文章首發于我的 GitHub 博客 前言 上一篇文章:《Chrome 小恐龍游戲源碼探究七 -- 晝夜模式交替》實現了游戲晝夜模式的交替,這一篇文章中,將實現:1、小恐龍的繪制 2、鍵盤對小恐龍的控制 3、頁面失焦后,重新聚焦會重置...
摘要:在中已經澄清分號恩,這也是規范一部分閱讀更多類型分配強制轉換執行強制類型轉換的語句。對于整型值大于位的進行位運算將導致不可預見的行為。在范圍內使用進行對象查詢譯文出處 類型 基本類型:訪問基本類型時,應該直接操作類型值 string number boolean null undefined javascriptvar foo = 1; var bar = foo; bar ...
摘要:,已過期,請使用代替。解決方案安卓增加屬性并設置為官方解釋當圖片實際尺寸和容器樣式尺寸不一致時,決定以怎樣的策略來調整圖片的尺寸。 本文均為RN開發過程中遇到的問題、坑點的分析及解決方案,各問題點之間無關聯,希望能幫助讀者少走彎路,持續更新中... (2019年3月29日更新) 原文鏈接:http://www.kovli.com/2018/06/... 作者:Kovli - 如何在原生...
摘要:最近工作中重構了抽獎轉盤,給大家提供一個開發轉盤抽獎的思路需求轉盤根據獎品數量不同而有變化目錄結構由于業務需要所以開發了兩個版本抽獎,和,不過部分只能替換圖片,沒有功能邏輯。 最近工作中重構了抽獎轉盤,給大家提供一個開發轉盤抽獎的思路 需求 1、轉盤根據獎品數量不同而有變化 2、canvas 目錄結構 showImg(https://segmentfault.com/img/bVbwL...
閱讀 3459·2019-08-30 15:44
閱讀 800·2019-08-30 13:46
閱讀 2077·2019-08-30 11:05
閱讀 3338·2019-08-29 18:32
閱讀 2160·2019-08-29 13:56
閱讀 1299·2019-08-29 12:57
閱讀 764·2019-08-28 18:21
閱讀 1742·2019-08-26 12:16