精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

責任鏈實戰的高級用法:多級校驗、工作流,這樣寫代碼才足夠優雅!

開發 前端
super.getConfig().getDown()是獲取AbstractCheckHandler處理器對象中保存的配置信息,如果處理器配置了降級,則跳過該處理器,調用super.next()執行下一個處理器邏輯。

責任鏈模式,簡而言之,就是將多個操作組裝成一條鏈路進行處理。請求在鏈路上傳遞,鏈路上的每一個節點就是一個處理器,每個處理器都可以對請求進行處理,或者傳遞給鏈路上的下一個處理器處理。

應用場景

責任鏈模式的應用場景,在實際工作中,通常有如下兩種應用場景。

  • 操作需要經過一系列的校驗,通過校驗后才執行某些操作。
  • 工作流。企業中通常會制定很多工作流程,一級一級的去處理任務。

下面通過兩個案例來學習一下責任鏈模式。

案例一:創建商品多級校驗場景

以創建商品為例,假設商品創建邏輯分為以下三步完成:①創建商品、②校驗商品參數、③保存商品。

第②步校驗商品又分為多種情況的校驗,必填字段校驗、規格校驗、價格校驗、庫存校驗等等。這些檢驗邏輯像一個流水線,要想創建出一個商品,必須通過這些校驗。如下流程圖所示:

圖片圖片

偽代碼如下:

創建商品步驟,需要經過一系列的參數校驗,如果參數校驗失敗,直接返回失敗的結果;通過所有的參數校驗后,最終保存商品信息。

圖片圖片

如上代碼看起來似乎沒什么問題,它非常工整,而且代碼邏輯很清晰。

PS:我沒有把所有的校驗代碼都羅列在一個方法里,那樣更能產生對比性,但我覺得抽象并分離單一職責的函數應該是每個程序員最基本的規范!

但是隨著業務需求不斷地疊加,相關的校驗邏輯也越來越多,新的功能使代碼越來越臃腫,可維護性較差。更糟糕的是,這些校驗組件不可復用,當你有其他需求也需要用到一些校驗時,你又變成了Ctrl+C , Ctrl+V程序員,系統的維護成本也越來越高。如下圖所示:

圖片圖片

偽代碼同上,這里就不贅述了。

終于有一天,你忍無可忍了,決定重構這段代碼。

使用責任鏈模式優化:創建商品的每個校驗步驟都可以作為一個單獨的處理器,抽離為一個單獨的類,便于復用。這些處理器形成一條鏈式調用,請求在處理器鏈上傳遞,如果校驗條件不通過,則處理器不再向下傳遞請求,直接返回錯誤信息;若所有的處理器都通過檢驗,則執行保存商品步驟。

圖片圖片

案例一實戰:責任鏈模式實現創建商品校驗

UML圖:一覽眾山小

圖片圖片

AbstractCheckHandler表示處理器抽象類,負責抽象處理器行為。其有3個子類,分別是:

  • NullValueCheckHandler:空值校驗處理器
  • PriceCheckHandler:價格校驗處理
  • StockCheckHandler:庫存校驗處理器

AbstractCheckHandler 抽象類中, handle()定義了處理器的抽象方法,其子類需要重寫handle()方法以實現特殊的處理器校驗邏輯;

protected ProductCheckHandlerConfig config 是處理器的動態配置類,使用protected聲明,每個子類處理器都持有該對象。該對象用于聲明當前處理器、以及當前處理器的下一個處理器nextHandler,另外也可以配置一些特殊屬性,比如說接口降級配置、超時時間配置等。

AbstractCheckHandler nextHandler 是當前處理器持有的下一個處理器的引用,當前處理器執行完畢時,便調用nextHandler執行下一處理器的handle()校驗方法;

protected Result next() 是抽象類中定義的,執行下一個處理器的方法,使用protected聲明,每個子類處理器都持有該對象。當子類處理器執行完畢(通過)時,調用父類的方法執行下一個處理器nextHandler。

HandlerClient 是執行處理器鏈路的客戶端,HandlerClient.executeChain()方法負責發起整個鏈路調用,并接收處理器鏈路的返回值。

擼起袖子開始擼代碼吧 !

商品參數對象:保存商品的入參

ProductVO是創建商品的參數對象,包含商品的基礎信息。并且其作為責任鏈模式中多個處理器的入參,多個處理器都以ProductVO為入參進行特定的邏輯處理。實際業務中,商品對象特別復雜。咱們化繁為簡,簡化商品參數如下:

/**
 * 商品對象
 */
@Data
@Builder
publicclass ProductVO {
    /**
     * 商品SKU,唯一
     */
    private Long skuId;
    /**
     * 商品名稱
     */
    private String skuName;
    /**
     * 商品圖片路徑
     */
    private String Path;
    /**
     * 價格
     */
    private BigDecimal price;
    /**
     * 庫存
     */
    private Integer stock;
}

抽象類處理器:抽象行為,子類共有屬性、方法

AbstractCheckHandler:處理器抽象類,并使用@Component注解注冊為由Spring管理的Bean對象,這樣做的好處是,我們可以輕松的使用Spring來管理這些處理器Bean。

/**
 * 抽象類處理器
 */
@Component
publicabstractclass AbstractCheckHandler {

    /**
     * 當前處理器持有下一個處理器的引用
     */
    @Getter
    @Setter
    protected AbstractCheckHandler nextHandler;


    /**
     * 處理器配置
     */
    @Setter
    @Getter
    protected ProductCheckHandlerConfig config;

    /**
     * 處理器執行方法
     * @param param
     * @return
     */
    public abstract Result handle(ProductVO param);

    /**
     * 鏈路傳遞
     * @param param
     * @return
     */
    protected Result next(ProductVO param) {
        //下一個鏈路沒有處理器了,直接返回
        if (Objects.isNull(nextHandler)) {
            return Result.success();
        }

        //執行下一個處理器
        return nextHandler.handle(param);
    }

}

在AbstractCheckHandler抽象類處理器中,使用protected聲明子類可見的屬性和方法。使用 @Component注解,聲明其為Spring的Bean對象,這樣做的好處是可以利用Spring輕松管理所有的子類,下面會看到如何使用。抽象類的屬性和方法說明如下:

  • public abstract Result handle():表示抽象的校驗方法,每個處理器都應該繼承AbstractCheckHandler抽象類處理器,并重寫其handle方法,各個處理器從而實現特殊的校驗邏輯,實際上就是多態的思想。
  • protected ProductCheckHandlerConfig config:表示每個處理器的動態配置類,可以通過“配置中心”動態修改該配置,實現處理器的“動態編排”和“順序控制”。配置類中可以配置處理器的名稱、下一個處理器、以及處理器是否降級等屬性。
  • protected AbstractCheckHandler nextHandler:表示當前處理器持有下一個處理器的引用,如果當前處理器handle()校驗方法執行完畢,則執行下一個處理器nextHandler的handle()校驗方法執行校驗邏輯。
  • protected Result next(ProductVO param):此方法用于處理器鏈路傳遞,子類處理器執行完畢后,調用父類的next()方法執行在config 配置的鏈路上的下一個處理器,如果所有處理器都執行完畢了,就返回結果了。

ProductCheckHandlerConfig配置類 :

/**
 * 處理器配置類
 */
@AllArgsConstructor
@Data
public class ProductCheckHandlerConfig {
    /**
     * 處理器Bean名稱
     */
    private String handler;
    /**
     * 下一個處理器
     */
    private ProductCheckHandlerConfig next;
    /**
     * 是否降級
     */
    private Boolean down = Boolean.FALSE;
}

子類處理器:處理特有的校驗邏輯

AbstractCheckHandler抽象類處理器有3個子類分別是:

  • NullValueCheckHandler:空值校驗處理器
  • PriceCheckHandler:價格校驗處理
  • StockCheckHandler:庫存校驗處理器

各個處理器繼承AbstractCheckHandler抽象類處理器,并重寫其handle()處理方法以實現特有的校驗邏輯。

NullValueCheckHandler:空值校驗處理器。針對性校驗創建商品中必填的參數。如果校驗未通過,則返回錯誤碼ErrorCode,責任鏈在此截斷(停止),創建商品返回被校驗住的錯誤信息。注意代碼中的降級配置!

super.getConfig().getDown()是獲取AbstractCheckHandler處理器對象中保存的配置信息,如果處理器配置了降級,則跳過該處理器,調用super.next()執行下一個處理器邏輯。

同樣,使用@Component注冊為由Spring管理的Bean對象,

/**
 * 空值校驗處理器
 */
@Component
publicclass NullValueCheckHandler extends AbstractCheckHandler{

    @Override
    public Result handle(ProductVO param) {
        System.out.println("空值校驗 Handler 開始...");
        
        //降級:如果配置了降級,則跳過此處理器,執行下一個處理器
        if (super.getConfig().getDown()) {
            System.out.println("空值校驗 Handler 已降級,跳過空值校驗 Handler...");
            returnsuper.next(param);
        }
        
        //參數必填校驗
        if (Objects.isNull(param)) {
            return Result.failure(ErrorCode.PARAM_NULL_ERROR);
        }
        //SkuId商品主鍵參數必填校驗
        if (Objects.isNull(param.getSkuId())) {
            return Result.failure(ErrorCode.PARAM_SKU_NULL_ERROR);
        }
        //Price價格參數必填校驗
        if (Objects.isNull(param.getPrice())) {
            return Result.failure(ErrorCode.PARAM_PRICE_NULL_ERROR);
        }
        //Stock庫存參數必填校驗
        if (Objects.isNull(param.getStock())) {
            return Result.failure(ErrorCode.PARAM_STOCK_NULL_ERROR);
        }
        
        System.out.println("空值校驗 Handler 通過...");
        
        //執行下一個處理器
        returnsuper.next(param);
    }
}

PriceCheckHandler:價格校驗處理。針對創建商品的價格參數進行校驗。這里只是做了簡單的判斷價格>0的校驗,實際業務中比較復雜,比如“價格門”這些防范措施等。

/**
 * 價格校驗處理器
 */
@Component
publicclass PriceCheckHandler extends AbstractCheckHandler{
    @Override
    public Result handle(ProductVO param) {
        System.out.println("價格校驗 Handler 開始...");

        //非法價格校驗
        boolean illegalPrice =  param.getPrice().compareTo(BigDecimal.ZERO) <= 0;
        if (illegalPrice) {
            return Result.failure(ErrorCode.PARAM_PRICE_ILLEGAL_ERROR);
        }
        //其他校驗邏輯...

        System.out.println("價格校驗 Handler 通過...");

        //執行下一個處理器
        returnsuper.next(param);
    }
}

StockCheckHandler:庫存校驗處理器。針對創建商品的庫存參數進行校驗。

/**
 * 庫存校驗處理器
 */
@Component
publicclass StockCheckHandler extends AbstractCheckHandler{
    @Override
    public Result handle(ProductVO param) {
        System.out.println("庫存校驗 Handler 開始...");

        //非法庫存校驗
        boolean illegalStock = param.getStock() < 0;
        if (illegalStock) {
            return Result.failure(ErrorCode.PARAM_STOCK_ILLEGAL_ERROR);
        }
        //其他校驗邏輯..

        System.out.println("庫存校驗 Handler 通過...");

        //執行下一個處理器
        returnsuper.next(param);
    }
}

客戶端:執行處理器鏈路

HandlerClient客戶端類負責發起整個處理器鏈路的執行,通過executeChain()方法。如果處理器鏈路返回錯誤信息,即校驗未通過,則整個鏈路截斷(停止),返回相應的錯誤信息。

public class HandlerClient {

  public static Result executeChain(AbstractCheckHandler handler, ProductVO param) {
      //執行處理器
      Result handlerResult = handler.handle(param);
      if (!handlerResult.isSuccess()) {
          System.out.println("HandlerClient 責任鏈執行失敗返回:" + handlerResult.toString());
          return handlerResult;
      }
      return Result.success();
  }
}

以上,責任鏈模式相關的類已經創建好了。接下來就可以創建商品了。

創建商品:抽象步驟,化繁為簡

createProduct()創建商品方法抽象為2個步驟:①參數校驗、②創建商品。參數校驗使用責任鏈模式進行校驗,包含:空值校驗、價格校驗、庫存校驗等等,只有鏈上的所有處理器均校驗通過,才調用saveProduct()創建商品方法;否則返回校驗錯誤信息。

createProduct()創建商品方法中,通過責任鏈模式,我們將校驗邏輯進行解耦。createProduct()創建商品方法中不需要關注都要經過哪些校驗處理器,以及校驗處理器的細節。

/**
 * 創建商品
 * @return
 */
@Test
public Result createProduct(ProductVO param) {

    //參數校驗,使用責任鏈模式
    Result paramCheckResult = this.paramCheck(param);
    if (!paramCheckResult.isSuccess()) {
        return paramCheckResult;
    }

    //創建商品
    return this.saveProduct(param);
}

參數校驗:責任鏈模式

參數校驗paramCheck()方法使用責任鏈模式進行參數校驗,方法內沒有聲明具體都有哪些校驗,具體有哪些參數校驗邏輯是通過多個處理器鏈傳遞的。如下:

/**
 * 參數校驗:責任鏈模式
 * @param param
 * @return
 */
private Result paramCheck(ProductVO param) {

    //獲取處理器配置:通常配置使用統一配置中心存儲,支持動態變更
    ProductCheckHandlerConfig handlerConfig = this.getHandlerConfigFile();

    //獲取處理器
    AbstractCheckHandler handler = this.getHandler(handlerConfig);

    //責任鏈:執行處理器鏈路
    Result executeChainResult = HandlerClient.executeChain(handler, param);
    if (!executeChainResult.isSuccess()) {
        System.out.println("創建商品 失敗...");
        return executeChainResult;
    }

    //處理器鏈路全部成功
    return Result.success();
}

paramCheck()方法步驟說明如下:

?? 步驟1:獲取處理器配置。

通過getHandlerConfigFile()方法獲取處理器配置類對象,配置類保存了鏈上各個處理器的上下級節點配置,支持流程編排、動態擴展。通常配置是通過Ducc(京東自研的配置中心)、Nacos(阿里開源的配置中心)等配置中心存儲的,支持動態變更、實時生效。

基于此,我們便可以實現校驗處理器的編排、以及動態擴展了。我這里沒有使用配置中心存儲處理器鏈路的配置,而是使用JSON串的形式去模擬配置,大家感興趣的可以自行實現。

/**
 * 獲取處理器配置:通常配置使用統一配置中心存儲,支持動態變更
 * @return
 */
private ProductCheckHandlerConfig getHandlerConfigFile() {
    //配置中心存儲的配置
    String configJson = "{\"handler\":\"nullValueCheckHandler\",\"down\":true,\"next\":{\"handler\":\"priceCheckHandler\",\"next\":{\"handler\":\"stockCheckHandler\",\"next\":null}}}";
    //轉成Config對象
    ProductCheckHandlerConfig handlerConfig = JSON.parseObject(configJson, ProductCheckHandlerConfig.class);
    return handlerConfig;
}

ConfigJson存儲的處理器鏈路配置JSON串,在代碼中可能不便于觀看,我們可以使用json.cn等格式化看一下,如下,配置的整個調用鏈路規則特別清晰。

圖片圖片

getHandlerConfigFile()類獲到配置類的結構如下,可以看到,就是把在配置中心儲存的配置規則,轉換成配置類ProductCheckHandlerConfig對象,用于程序處理。

注意,此時配置類中存儲的僅僅是處理器Spring Bean的name而已,并非實際處理器對象。

圖片圖片

接下來,通過配置類獲取實際要執行的處理器。

?? 步驟2:根據配置獲取處理器。

上面步驟1通過getHandlerConfigFile()方法獲取到處理器鏈路配置規則后,再調用getHandler()獲取處理器。

getHandler()參數是如上ConfigJson配置的規則,即步驟1轉換成的ProductCheckHandlerConfig對象;根據ProductCheckHandlerConfig配置規則轉換成處理器鏈路對象。代碼如下:

* 使用Spring注入:所有繼承了AbstractCheckHandler抽象類的Spring Bean都會注入進來。Map的Key對應Bean的name,Value是name對應相應的Bean
 */
@Resource
private Map<String, AbstractCheckHandler> handlerMap;

/**
 * 獲取處理器
 * @param config
 * @return
 */
private AbstractCheckHandler getHandler (ProductCheckHandlerConfig config) {
    //配置檢查:沒有配置處理器鏈路,則不執行校驗邏輯
    if (Objects.isNull(config)) {
        returnnull;
    }
    //配置錯誤
    String handler = config.getHandler();
    if (StringUtils.isBlank(handler)) {
        returnnull;
    }
    //配置了不存在的處理器
    AbstractCheckHandler abstractCheckHandler = handlerMap.get(config.getHandler());
    if (Objects.isNull(abstractCheckHandler)) {
        returnnull;
    }
    
    //處理器設置配置Config
    abstractCheckHandler.setConfig(config);
    
    //遞歸設置鏈路處理器
    abstractCheckHandler.setNextHandler(this.getHandler(config.getNext()));

    return abstractCheckHandler;
}

?? ?? 步驟2-1:配置檢查。

代碼14~27行,進行了配置的一些檢查操作。如果配置錯誤,則獲取不到對應的處理器。代碼23行handlerMap.get(config.getHandler())是從所有處理器映射Map中獲取到對應的處理器Spring Bean。

注意第5行代碼,handlerMap存儲了所有的處理器映射,是通過Spring @Resource注解注入進來的。注入的規則是:所有繼承了AbstractCheckHandler抽象類(它是Spring管理的Bean)的子類(子類也是Spring管理的Bean)都會注入進來。

注入進來的handlerMap中 Map的Key對應Bean的name,Value是name對應的Bean實例,也就是實際的處理器,這里指空值校驗處理器、價格校驗處理器、庫存校驗處理器。如下:

圖片圖片

這樣根據配置ConfigJson(?? 步驟1:獲取處理器配置)中handler:"priceCheckHandler"的配置,使用handlerMap.get(config.getHandler())便可以獲取到對應的處理器Spring Bean對象了。

?? ?? 步驟2-2:保存處理器規則。

代碼29行,將配置規則保存到對應的處理器中abstractCheckHandler.setConfig(config),子類處理器就持有了配置的規則。

?? ?? 步驟2-3:遞歸設置處理器鏈路。

代碼32行,遞歸設置鏈路上的處理器。

//遞歸設置鏈路處理器 abstractCheckHandler.setNextHandler(this.getHandler(config.getNext()));

這一步可能不太好理解,結合ConfigJson配置的規則來看,似乎就很很容易理解了。

圖片圖片

由上而下,NullValueCheckHandler 空值校驗處理器通過setNextHandler()方法設置自己持有的下一節點的處理器,也就是價格處理器PriceCheckHandler。

接著,PriceCheckHandler價格處理器,同樣需要經過步驟2-1配置檢查、步驟2-2保存配置規則,并且最重要的是,它也需要設置下一節點的處理器StockCheckHandler庫存校驗處理器。

StockCheckHandler庫存校驗處理器也一樣,同樣需要經過步驟2-1配置檢查、步驟2-2保存配置規則,但請注意StockCheckHandler的配置,它的next規則配置了null,這表示它下面沒有任何處理器要執行了,它就是整個鏈路上的最后一個處理節點。

通過遞歸調用getHandler()獲取處理器方法,就將整個處理器鏈路對象串聯起來了。如下:

圖片圖片

友情提示:遞歸雖香,但使用遞歸一定要注意截斷遞歸的條件處理,否則可能造成死循環哦!

實際上,getHandler()獲取處理器對象的代碼就是把在配置中心配置的規則ConfigJson,轉換成配置類ProductCheckHandlerConfig對象,再根據配置類對象,轉換成實際的處理器對象,這個處理器對象持有整個鏈路的調用順序。

?? 步驟3:客戶端執行調用鏈路。

public class HandlerClient {

  public static Result executeChain(AbstractCheckHandler handler, ProductVO param) {
      //執行處理器
      Result handlerResult = handler.handle(param);
      if (!handlerResult.isSuccess()) {
          System.out.println("HandlerClient 責任鏈執行失敗返回:" + handlerResult.toString());
          return handlerResult;
      }
      return Result.success();
  }
}

getHandler()獲取完處理器后,整個調用鏈路的執行順序也就確定了,此時,客戶端該干活了!

HandlerClient.executeChain(handler, param)方法是HandlerClient客戶端類執行處理器整個調用鏈路的,并接收處理器鏈路的返回值。

executeChain()通過AbstractCheckHandler.handle()觸發整個鏈路處理器順序執行,如果某個處理器校驗沒有通過!handlerResult.isSuccess(),則返回錯誤信息;所有處理器都校驗通過,則返回正確信息Result.success()

總結:串聯方法調用流程

基于以上,再通過流程圖來回顧一下整個調用流程。

圖片圖片

測試:代碼執行結果

場景1:創建商品參數中有空值(如下skuId參數為null),鏈路被空值處理器截斷,返回錯誤信息

//創建商品參數
ProductVO param = ProductVO.builder()
      .skuId(null).skuName("華為手機").Path("http://...")
      .price(new BigDecimal(1))
      .stock(1)
      .build();

測試結果

圖片圖片

場景2:創建商品價格參數異常(如下price參數),被價格處理器截斷,返回錯誤信息

ProductVO param = ProductVO.builder()
      .skuId(1L).skuName("華為手機").Path("http://...")
      .price(new BigDecimal(-999))
      .stock(1)
      .build();

測試結果

圖片圖片

場景 3:創建商品庫存參數異常(如下stock參數),被庫存處理器截斷,返回錯誤信息。

//創建商品參數,模擬用戶傳入
ProductVO param = ProductVO.builder()
      .skuId(1L).skuName("華為手機").Path("http://...")
      .price(new BigDecimal(1))
      .stock(-999)
      .build();

測試結果

圖片圖片

場景4:創建商品所有處理器校驗通過,保存商品。

![15](C:\Users\18796\Desktop\文章\15.png)![15](C:\Users\18796\Desktop\文章\15.png)![15](C:\Users\18796\Desktop\文章\15.png)![15](C:\Users\18796\Desktop\文章\15.png)//創建商品參數,模擬用戶傳入
ProductVO param = ProductVO.builder()
      .skuId(1L).skuName("華為手機").Path("http://...")
      .price(new BigDecimal(999))
      .stock(1).build();

測試結果

圖片

案例二:工作流,費用報銷審核流程

同事小賈最近剛出差回來,她迫不及待的就提交了費用報銷的流程。根據金額不同,分為以下幾種審核流程。報銷金額低于1000元,三級部門管理者審批即可,1000到5000元除了三級部門管理者審批,還需要二級部門管理者審批,而5000到10000元還需要一級部門管理者審批。即有以下幾種情況:

  1. 小賈需報銷500元,三級部門管理者審批即可。
  2. 小賈需報銷2500元,三級部門管理者審批通過后,還需要二級部門管理者審批,二級部門管理者審批通過后,才完成報銷審批流程。
  3. 小賈需報銷7500元,三級管理者審批通過后,并且二級管理者審批通過后,流程流轉到一級部門管理者進行審批,一級管理者審批通過后,即完成了報銷流程。

UML圖UML圖

AbstractFlowHandler作為處理器抽象類,抽象了approve()審核方法,一級、二級、三級部門管理者處理器繼承了抽象類,并重寫其approve()審核方法,從而實現特有的審核邏輯。

圖片圖片

配置類如下所示,每層的處理器都要配置審核人、價格審核規則(審核的最大、最小金額)、下一級處理人。配置規則是可以動態變更的,如果三級部門管理者可以審核的金額增加到2000元,修改一下配置即可動態生效。

圖片圖片

代碼實現與案例一相似,感興趣的自己動動小手吧~

責任鏈的優缺點

圖片圖片

圖片

責任編輯:武曉燕 來源: JAVA日知錄
相關推薦

2021-04-20 10:50:38

Spring Boot代碼Java

2023-03-28 08:07:12

2025-08-04 01:00:00

責任鏈模式權限

2017-07-07 16:57:35

代碼Python

2017-06-26 09:40:50

Python代碼寫法

2025-09-22 09:31:34

2020-10-25 19:58:04

Pythonic代碼語言

2021-03-29 11:20:39

前端代碼工作流

2010-05-28 15:16:33

SharePoint 工作流

2022-10-26 08:00:43

Activiti工作流BPM

2021-10-14 11:34:05

技術工作流引擎

2013-04-23 10:28:08

IBeamMDAAWF

2024-04-25 08:00:00

DevOps架構軟件開發

2015-07-14 09:26:28

微型工作流引擎設計

2023-08-01 08:54:02

接口冪等網絡

2014-02-28 13:46:35

Angular代碼

2022-07-29 08:40:20

設計模式責任鏈場景

2021-04-27 22:38:41

代碼開發前端

2021-10-17 22:23:43

5G4G數據

2024-05-09 12:17:00

責任鏈設計模式
點贊
收藏

51CTO技術棧公眾號

精品视频免费观看| 国内精品一区二区三区四区| 五月天婷婷亚洲| av小次郎在线| 久久在线观看免费| 成人免费网站在线看| 国产污片在线观看| 成人影院天天5g天天爽无毒影院| 日韩欧美一级二级| 色婷婷综合久久久久中文字幕 | 国产亚洲激情在线| 在线视频日韩欧美| 成人美女大片| 亚洲自拍与偷拍| 五月天国产一区| 熟妇高潮一区二区三区| 久久99精品久久久久久久久久久久| 色综合天天综合网国产成人网| 午夜在线观看一区| 国产精品一区二区三区美女| 欧美精品乱人伦久久久久久| 熟妇人妻va精品中文字幕| 调教一区二区| 国产精品久久久久久久久免费丝袜| 精品国产免费人成电影在线观...| 国产精品永久久久久久久久久| 久久福利精品| 国内揄拍国内精品少妇国语| 欧美激情图片小说| 久久成人高清| 日韩精品一区二区视频| 国产伦精品一区二区三区精品| 国产精品诱惑| 在线影视一区二区三区| 欧美 国产 日本| аⅴ资源天堂资源库在线| 亚洲六月丁香色婷婷综合久久 | 成人在线免费电影| 99精品热视频| 国产欧美韩日| 国产91绿帽单男绿奴| 国产精品一区二区三区四区| 成人在线视频网站| 中文字幕在线观看视频一区| 久久久精品日韩| 日韩免费观看在线观看| 亚洲欧美日韩激情| 久久婷婷av| 国产精品白嫩美女在线观看 | 欧美xxxxx牲另类人与| 中文字幕第22页| 日韩一二三区| 精品国产麻豆免费人成网站| 国产调教打屁股xxxx网站| 精品一区二区三区亚洲| 日韩欧美第一区| 久久精品aⅴ无码中文字字幕重口| 日韩免费高清视频网站| 欧美一级午夜免费电影| 国产欧美视频一区| 精品欧美午夜寂寞影院| 精品调教chinesegay| 尤物视频最新网址| 欧美一区三区| 久久久成人的性感天堂| 欧美日韩综合一区二区| 国内精品久久久久久久97牛牛| 欧美区在线播放| 国产精品美女毛片真酒店| 亚洲精品专区| 国产成人一区二区三区| 中文字幕一二三四| 精彩视频一区二区| 粉嫩高清一区二区三区精品视频| 免费看日韩av| 国产婷婷色一区二区三区| 亚洲欧洲一二三| a视频在线观看| 亚洲第一福利一区| 日日碰狠狠躁久久躁婷婷| 91在线成人| 日韩欧美国产一区二区三区 | 欧美手机视频| 欧美成人免费大片| 久久精品一二区| 日本vs亚洲vs韩国一区三区| 亚洲一区二区三区xxx视频| 人妻丰满熟妇av无码区hd| ww久久中文字幕| 亚洲资源在线网| 不卡av免费观看| 欧美在线观看一区| 国产免费a级片| 成人vr资源| 不卡伊人av在线播放| 日韩欧美高清在线观看| 蜜臀av性久久久久蜜臀aⅴ流畅| 亚洲xxx自由成熟| 欧美日韩免费做爰大片| 亚洲人成人一区二区在线观看| 国产欧美日韩网站| 黄页免费欧美| 日韩av在线最新| 日韩精品一区二区三区在线视频| 一区二区黄色| 亚洲一区二区在线播放| 免费av在线电影| 亚洲综合丁香婷婷六月香| 超碰在线公开97| 台湾色综合娱乐中文网| 欧美裸身视频免费观看| 国产男人搡女人免费视频| 成人精品高清在线| 亚洲在线视频一区二区| 成人av免费电影网站| 精品免费视频.| 久久精品日韩无码| 久久伊人亚洲| 久久久神马电影| 波多野结衣中文在线| 91精品欧美福利在线观看| 国产熟妇久久777777| 在线播放一区| 999日本视频| 蜜桃av在线免费观看| 在线观看中文字幕不卡| 粉嫩av蜜桃av蜜臀av| 亚洲国产99| 99久久99久久| av网站大全在线| 欧美日韩国产欧美日美国产精品| 中文精品在线观看| 亚洲女优在线| 久久综合狠狠综合久久综青草| 久久激情久久| 国产剧情一区二区三区| 免费成人深夜夜行视频| 视频在线这里都是精品| 欧美挠脚心视频网站| 永久免费成人代码| 午夜一级久久| 欧美日韩一本到| 超碰男人的天堂| 精品1区2区3区4区| a级国产乱理论片在线观看99| 日本网站在线免费观看视频| 欧美写真视频网站| 怡红院一区二区三区| 日韩一区精品字幕| 日韩一区不卡| 精品久久99| 精品国产美女在线| 国产黄色一区二区| 亚洲美女免费视频| 亚洲熟女乱综合一区二区| 午夜日韩av| av一区二区在线看| av白虎一区| 日韩精品视频三区| 一二三区免费视频| 欧美激情一区二区| 在线能看的av网站| 欧美一区视频| 国产日韩亚洲精品| 成人欧美大片| 中文字幕国内精品| 国产精品污视频| 亚洲一区二区在线播放相泽| wwwxx日本| 免费久久99精品国产自在现线| 欧美性xxxx69| 亚洲欧美久久精品| 九九热这里只有精品免费看| 天天操天天射天天| 在线亚洲高清视频| 多男操一女视频| 成人免费三级在线| 欧美私人情侣网站| 日韩1区在线| 国产精品麻豆免费版| 成人美女视频| 久久成人国产精品| 亚洲欧美日韩免费| 5566中文字幕一区二区电影| 久草免费在线观看视频| 久久久五月婷婷| 四虎成人在线播放| 羞羞视频在线观看欧美| 在线观看福利一区| 欧美精品国产白浆久久久久| 日韩av黄色在线观看| av大大超碰在线| 国产午夜精品全部视频在线播放| 国产乱淫a∨片免费视频| 午夜欧美大尺度福利影院在线看| 午夜精产品一区二区在线观看的| 国内久久精品视频| 夫妻免费无码v看片| 国产韩国精品一区二区三区| 精品国产综合区久久久久久| 国产一区精品福利| 2024亚洲男人天堂| 中文字幕免费高清电视剧网站在线观看 | 欧美成人免费在线| 国产aa精品| 国产97免费视| 麻豆福利在线观看| 日韩在线视频线视频免费网站| 人妻精品一区二区三区| 欧美精选在线播放| 男人天堂av在线播放| 亚洲一区二区黄色| 亚洲熟女毛茸茸| 久久免费美女视频| 男男做爰猛烈叫床爽爽小说 | 亚洲国产天堂av| 成人福利在线看| 亚洲自拍第三页| 九色porny丨国产精品| 久久精品午夜福利| 一区二区三区国产在线| 97中文字幕在线| 中文字幕一区二区精品区| 亚洲不卡1区| 日韩精品a在线观看91| 国产精品一区在线播放| 91精品国产色综合久久不卡粉嫩| 国产成人高潮免费观看精品| av中文在线资源库| 欧美精品18videosex性欧美| 麻豆电影在线播放| 色天天综合狠狠色| av网站在线免费观看| 亚洲视频在线看| 你懂得网站在线| 日韩av在线免费观看| 蜜桃视频污在线观看| 日韩免费高清视频| 亚洲精品97久久中文字幕| 欧美一级片在线看| 91一区二区视频| 欧美美女激情18p| 亚洲综合视频在线播放| 欧美视频中文字幕| 亚洲精品一区二区二区| 欧美视频中文一区二区三区在线观看| 波多野结衣在线观看视频| 91国产免费观看| 中文在线免费看视频| 欧美日韩免费视频| 91 中文字幕| 欧美一区二区三区日韩视频| 国产毛片久久久久| 日韩精品中文字幕一区| 成人无码一区二区三区| 日韩国产欧美精品一区二区三区| 三区在线观看| 在线精品高清中文字幕| 888av在线| 欧美乱大交xxxxx另类电影| 男女视频在线| 欧美尤物巨大精品爽| 3d欧美精品动漫xxxx无尽| 国产精品日韩久久久久| 动漫一区二区三区| av免费观看久久| 欧美调教在线| 日韩资源av在线| 国产精品毛片一区二区在线看| 韩国黄色一级大片| 亚洲二区精品| 免费看a级黄色片| 国内欧美视频一区二区| 午夜剧场免费看| 国产欧美一区二区三区沐欲| 欧美色视频一区二区三区在线观看| 亚洲码国产岛国毛片在线| 91视频免费网址| 欧美日本一区二区在线观看| 亚洲美女性生活| 亚洲丝袜在线视频| 3d玉蒲团在线观看| 欧美有码在线观看视频| 91嫩草国产线观看亚洲一区二区| 高清一区二区三区视频| 精品freesex老太交| 51xx午夜影福利| 久久午夜精品| 国产成人av免费观看| 久久久久久麻豆| 久久r这里只有精品| 欧美性猛交xxxxx免费看| 国产免费av电影| 亚洲欧洲国产一区| 污污在线观看| 国产精品视频一| 久久悠悠精品综合网| 伊人久久大香线蕉综合75| 99riav国产精品| 最新av免费在线观看| 久久综合久色欧美综合狠狠| 成年人午夜剧场| 91国内精品野花午夜精品| 国产 日韩 欧美 精品| 中文字幕亚洲在线| 丝袜老师在线| http;//www.99re视频| 日韩.com| 激情网站五月天| 成人av网站在线| 男人的天堂久久久| 欧美日韩免费不卡视频一区二区三区 | 裸体武打性艳史| 欧美影院一区二区| 日韩电影在线观看完整版| 欧美精品午夜视频| 2019年精品视频自拍| 狠狠综合久久av| 国产精品激情电影| 在线观看免费不卡av| 国产三级精品在线| 欧美 日韩 精品| 精品福利视频一区二区三区| av片在线观看永久免费| 国产精品视频一| 成人看的视频| 日韩精品一区二区三区不卡| 26uuuu精品一区二区| 日韩三级一区二区三区| 精品99999| 国内在线视频| 国产三区精品| 亚洲国产黄色| 大桥未久恸哭の女教师| 亚洲影视资源网| 午夜精品久久久久久久99| 久久九九全国免费精品观看| 国产精品高潮久久| 亚洲精品日韩成人| 日产国产高清一区二区三区| 国产又粗又猛又爽视频| 日韩欧美国产黄色| 你懂的视频在线播放| 国产91精品网站| 免费一区二区| 中文字幕国内自拍| 国产精品久久久久久久久动漫| 国产精品国产精品国产| 尤物yw午夜国产精品视频明星 | 国产精品久久久久9999高清| 午夜男人的天堂| 午夜在线电影亚洲一区| 午夜视频在线播放| 青青久久av北条麻妃海外网| 亚洲宅男一区| 天天色综合社区| 亚洲欧美日韩国产成人精品影院| 国产成人精品亚洲精品色欲| 欧美日韩国产123| 久久悠悠精品综合网| 激情五月开心婷婷| 国产精品久久久久三级| 国产又爽又黄又嫩又猛又粗| 欧美成人免费全部| 白白在线精品| 欧美视频第一区| 中文字幕在线观看不卡视频| www日本高清| 国产91成人在在线播放| 成人羞羞动漫| 在线播放国产视频| 狠狠色狠狠色综合日日五| 888av在线| 国产伦视频一区二区三区| 麻豆91精品| 可以免费看av的网址| 精品国内二区三区| gogo亚洲高清大胆美女人体| 在线播放 亚洲| 9i在线看片成人免费| 国产精品尤物视频| 九色精品免费永久在线| 亚洲动漫精品| 一级黄色片在线免费观看| 天天影视涩香欲综合网| av福利精品| 国产美女精品在线观看| 日本亚洲视频在线| 麻豆成人在线视频| 亚洲少妇中文在线| 日韩在线亚洲| 亚洲少妇第一页| 亚洲夂夂婷婷色拍ww47| 国产福利小视频在线观看| 91手机在线视频| 美女久久久精品| 粉嫩aⅴ一区二区三区| y97精品国产97久久久久久| 欧美顶级毛片在线播放| 91小视频在线播放|