Pipeline模式應用

来源:https://www.cnblogs.com/Jcloud/archive/2023/12/08/17884476.html
-Advertisement-
Play Games

本文重點為管道模式的抽象與應用,上述示例僅為個人理解。實際應用中,此案例長於應對各種規則冗雜的業務場景,便於規則編排。 ...


本文記錄Pipeline設計模式在業務流程編排中的應用

前言

Pipeline模式意為管道模式,又稱為流水線模式。旨在通過預先設定好的一系列階段來處理輸入的數據,每個階段的輸出即是下一階段的輸入。

本案例通過定義PipelineProduct(管道產品),PipelineJob(管道任務),PipelineNode(管道節點),完成一整條流水線的組裝,並將“原材料”加工為“商品”。其中管道產品負責承載各個階段的產品信息;管道任務負責不同階段對產品的加工;管道節點約束了管道產品及任務的關係,通過信號量定義了任務的執行方式。

依賴

工具依賴如下

            <!-- 工具類大全 -->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>最新版本</version>
            </dependency>



編程示例

1. 管道產品定義

package com.example.demo.pipeline.model;

/**
 * 管道產品介面
 *
 * @param <S> 信號量
 * @author 
 * @date 2023/05/15 11:49
 */
public interface PipelineProduct<S> {
}




2. 管道任務定義

package com.example.demo.pipeline.model;

/**
 * 管道任務介面
 *
 * @param <P> 管道產品
 * @author 
 * @date 2023/05/15 11:52
 */
@FunctionalInterface
public interface PipelineJob<P> {
    /**
     * 執行任務
     *
     * @param product 管道產品
     * @return {@link P}
     */
    P execute(P product);
}



3. 管道節點定義

package com.jd.baoxian.mall.market.service.pipeline.model;

import java.util.function.Predicate;

/**
 * 管道節點定義
 *
 * @param <S> 信號量
 * @param <P> 管道產品
 * @author 
 * @date 2023/05/15 11:54
 */
public interface PipelineNode<S, P extends PipelineProduct<S>> {
    /**
     * 節點組裝,按照上個管道任務傳遞的信號,執行 pipelineJob
     *
     * @param pipelineJob 管道任務
     * @return {@link PipelineNode}<{@link S},  {@link P}>
     */
    PipelineNode<S, P> flax(PipelineJob<P> pipelineJob);

    /**
     * 節點組裝,按照傳遞的信號,判斷當前管道的信號是否相等,執行 pipelineJob
     *
     * @param signal      信號
     * @param pipelineJob 管道任務
     * @return {@link PipelineNode}<{@link S},  {@link P}>
     */
    PipelineNode<S, P> flax(S signal, PipelineJob<P> pipelineJob);

    /**
     * 節點組裝,按照傳遞的信號,判斷當前管道的信號是否相等,執行 pipelineJob
     *
     * @param predicate   信號
     * @param pipelineJob 管道任務
     * @return {@link PipelineNode}<{@link S},  {@link P}>
     */
    PipelineNode<S, P> flax(Predicate<S> predicate, PipelineJob<P> pipelineJob);

    /**
     * 管道節點-任務執行
     *
     * @param product 管道產品
     * @return {@link P}
     */
    P execute(P product);
}




4. 管道產品、任務,節點的實現

4.1 管道產品

package com.example.demo.pipeline.factory;


import com.example.demo.model.request.DemoReq;
import com.example.demo.model.response.DemoResp;
import com.example.demo.pipeline.model.PipelineProduct;
import lombok.*;

/**
 * 樣例-管道產品
 *
 * @author 
 * @date 2023/05/15 14:04
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DemoPipelineProduct implements PipelineProduct<DemoPipelineProduct.DemoSignalEnum> {
    /**
     * 信號量
     */
    private DemoSignalEnum signal;

    /**
     * 產品-入參及回參
     */
    private DemoProductData productData;

    /**
     * 異常信息
     */
    private Exception exception;

    /**
     * 流程Id
     */
    private String tradeId;

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class DemoProductData {
        /**
         * 待驗證入參
         */
        private DemoReq userRequestData;

        /**
         * 待驗證回參
         */
        private DemoResp userResponseData;
    }

    /**
     * 產品-信號量
     *
     * @author 
     * @date 2023/05/15 13:54
     */
    @Getter
    public enum DemoSignalEnum {
        /**
         *
         */
        NORMAL(0, "正常"),
        /**
         *
         */
        CHECK_NOT_PASS(1, "校驗不通過"),
        /**
         *
         */
        BUSINESS_ERROR(2, "業務異常"),
        /**
         *
         */
        LOCK_ERROR(3, "鎖處理異常"),
        /**
         *
         */
        DB_ERROR(4, "事務處理異常"),

        ;
        /**
         * 枚舉碼值
         */
        private final int code;
        /**
         * 枚舉描述
         */
        private final String desc;

        /**
         * 構造器
         *
         * @param code
         * @param desc
         */
        DemoSignalEnum(int code, String desc) {
            this.code = code;
            this.desc = desc;
        }
    }
}





4.2 管道任務(抽象類)

package com.example.demo.pipeline.factory.job;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.json.JSONUtil;
import com.example.demo.pipeline.factory.DemoPipelineProduct;
import com.example.demo.pipeline.model.PipelineJob;
import lombok.extern.slf4j.Slf4j;

/**
 * 管道任務-抽象層
 *
 * @author 
 * @date 2023/05/15 19:48
 */
@Slf4j
public abstract class AbstractDemoJob implements PipelineJob<DemoPipelineProduct> {

    /**
     * 公共執行邏輯
     *
     * @param product 產品
     * @return
     */
    @Override
    public DemoPipelineProduct execute(DemoPipelineProduct product) {
        DemoPipelineProduct.DemoSignalEnum newSignal;
        try {
            newSignal = execute(product.getTradeId(), product.getProductData());
        } catch (Exception e) {
            product.setException(e);
            newSignal = DemoPipelineProduct.DemoSignalEnum.BUSINESS_ERROR;
        }
        product.setSignal(newSignal);
        defaultLogPrint(product.getTradeId(), product);
        return product;
    }

    /**
     * 子類執行邏輯
     *
     * @param tradeId     流程Id
     * @param productData 請求數據
     * @return
     * @throws Exception 異常
     */
    abstract DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) throws Exception;

    /**
     * 預設的日誌列印
     */
    public void defaultLogPrint(String tradeId, DemoPipelineProduct product) {
        if (!DemoPipelineProduct.DemoSignalEnum.NORMAL.equals(product.getSignal())) {
            log.info("流水線任務處理異常:流程Id=【{}】,信號量=【{}】,任務=【{}】,參數=【{}】", tradeId, product.getSignal(),
                    ClassUtil.getClassName(this, true), JSONUtil.toJsonStr(product.getProductData()), product.getException());
        }
    }

}




4.3 管道節點

package com.example.demo.pipeline.factory;


import cn.hutool.core.util.ClassUtil;
import cn.hutool.json.JSONUtil;
import com.example.demo.pipeline.model.PipelineJob;
import com.example.demo.pipeline.model.PipelineNode;
import lombok.extern.slf4j.Slf4j;

import java.util.function.Predicate;

/**
 * 審核-管道節點
 *
 * @author 
 * @date 2023/05/15 14:32
 */
@Slf4j
public class DemoPipelineNode implements PipelineNode<DemoPipelineProduct.DemoSignalEnum, DemoPipelineProduct> {

    /**
     * 下一管道節點
     */
    private DemoPipelineNode next;

    /**
     * 當前管道任務
     */
    private PipelineJob<DemoPipelineProduct> job;

    /**
     * 節點組裝,按照上個管道任務傳遞的信號,執行 pipelineJob
     *
     * @param pipelineJob 管道任務
     * @return {@link DemoPipelineNode}
     */
    @Override
    public DemoPipelineNode flax(PipelineJob<DemoPipelineProduct> pipelineJob) {
        return flax(DemoPipelineProduct.DemoSignalEnum.NORMAL, pipelineJob);
    }

    /**
     * 節點組裝,按照傳遞的信號,判斷當前管道的信號是否相等,執行 pipelineJob
     *
     * @param signal      信號
     * @param pipelineJob 管道任務
     * @return {@link DemoPipelineNode}
     */
    @Override
    public DemoPipelineNode flax(DemoPipelineProduct.DemoSignalEnum signal, PipelineJob<DemoPipelineProduct> pipelineJob) {
        return flax(signal::equals, pipelineJob);
    }

    /**
     * 節點組裝,上個管道過來的信號運行 predicate 後是true的話,執行 pipelineJob
     *
     * @param predicate
     * @param pipelineJob
     * @return
     */
    @Override
    public DemoPipelineNode flax(Predicate<DemoPipelineProduct.DemoSignalEnum> predicate,
                                 PipelineJob<DemoPipelineProduct> pipelineJob) {
        this.next = new DemoPipelineNode();
        this.job = (job) -> {
            if (predicate.test(job.getSignal())) {
                return pipelineJob.execute(job);
            } else {
                return job;
            }
        };
        return next;
    }

    /**
     * 管道節點-任務執行
     *
     * @param product 管道產品
     * @return
     */
    @Override
    public DemoPipelineProduct execute(DemoPipelineProduct product) {
        // 執行當前任務
        try {
            product = job == null ? product : job.execute(product);
            return next == null ? product : next.execute(product);
        } catch (Exception e) {
            log.error("流水線處理異常:流程Id=【{}】,任務=【{}】,參數=【{}】", product.getTradeId(), ClassUtil.getClassName(job, true), JSONUtil.toJsonStr(product.getProductData()), product.getException());
            return null;
        }

    }
}





5. 業務實現

通過之前的定義,我們已經可以通過Pipeline完成流水線的搭建,接下來以“審核信息提交”這一業務場景,完成應用。

5.1 定義Api、入參、回參

package com.example.demo.api;

import com.example.demo.model.request.DemoReq;
import com.example.demo.model.response.DemoResp;
import com.example.demo.pipeline.factory.PipelineForManagerSubmit;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * 演示-API
 *
 * @author 
 * @date 2023/08/06 16:27
 */
@Service
public class DemoManagerApi {

    /**
     * 管道-審核提交
     */
    @Resource
    private PipelineForManagerSubmit pipelineForManagerSubmit;

    /**
     * 審核提交
     *
     * @param requestData 請求數據
     * @return {@link DemoResp}
     */
    public DemoResp managerSubmit(DemoReq requestData) {
        return pipelineForManagerSubmit.managerSubmitCheck(requestData);
    }
}


package com.example.demo.model.request;

/**
 * 演示入參
 *
 * @author 
 * @date 2023/08/06 16:33
 */
public class DemoReq {
}


package com.example.demo.model.response;

import lombok.Data;

/**
 * 演示回參
 *
 * @author 
 * @date 2023/08/06 16:33
 */
@Data
public class DemoResp {
    /**
     * 成功標識
     */
    private Boolean success = false;

    /**
     * 結果信息
     */
    private String resultMsg;

    /**
     * 構造方法
     *
     * @param message 消息
     * @return {@link DemoResp}
     */
    public static DemoResp buildRes(String message) {
        DemoResp response = new DemoResp();
        response.setResultMsg(message);
        return response;
    }
}





5.2 定義具體任務

假定審核提交的流程需要包含:參數驗證、加鎖、解鎖、事務提交

package com.example.demo.pipeline.factory.job;

import cn.hutool.json.JSONUtil;
import com.example.demo.model.request.DemoReq;
import com.example.demo.pipeline.factory.DemoPipelineProduct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 加鎖-實現層
 *
 * @author 
 * @date 2023/05/17 17:00
 */
@Service
@Slf4j
public class CheckRequestLockJob extends AbstractDemoJob {

    /**
     * 子類執行邏輯
     *
     * @param tradeId     流程Id
     * @param productData 請求數據
     * @return
     * @throws Exception 異常
     */
    @Override
    DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) throws Exception {
        DemoReq userRequestData = productData.getUserRequestData();
        log.info("任務[{}]加鎖,線程號:{}", JSONUtil.toJsonStr(userRequestData), tradeId);
        return DemoPipelineProduct.DemoSignalEnum.NORMAL;
    }
}


package com.example.demo.pipeline.factory.job;

import cn.hutool.json.JSONUtil;
import com.example.demo.model.request.DemoReq;
import com.example.demo.pipeline.factory.DemoPipelineProduct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 解鎖-實現層
 *
 * @author 
 * @date 2023/05/17 17:00
 */
@Service
@Slf4j
public class CheckRequestUnLockJob extends AbstractDemoJob {

    /**
     * 子類執行邏輯
     *
     * @param tradeId     流程Id
     * @param productData 請求數據
     * @return
     * @throws Exception 異常
     */
    @Override
    DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) throws Exception {
        DemoReq userRequestData = productData.getUserRequestData();
        log.info("任務[{}]解鎖,線程號:{}", JSONUtil.toJsonStr(userRequestData), tradeId);
        return DemoPipelineProduct.DemoSignalEnum.NORMAL;
    }
}

package com.example.demo.pipeline.factory.job;

import cn.hutool.json.JSONUtil;
import com.example.demo.model.request.DemoReq;
import com.example.demo.pipeline.factory.DemoPipelineProduct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;


/**
 * 審核-參數驗證-實現類
 *
 * @author 
 * @date 2023/05/15 19:50
 */
@Slf4j
@Component
public class ManagerCheckParamJob extends AbstractDemoJob {

    /**
     * 執行基本入參驗證
     *
     * @param tradeId
     * @param productData 請求數據
     * @return
     */
    @Override
    DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) {
        /*
         * 入參驗證
         */
        DemoReq userRequestData = productData.getUserRequestData();
        log.info("任務[{}]入參驗證,線程號:{}", JSONUtil.toJsonStr(userRequestData), tradeId);
        // 非空驗證

        // 有效驗證

        // 校驗通過,退出
        return DemoPipelineProduct.DemoSignalEnum.NORMAL;
    }

}


package com.example.demo.pipeline.factory.job;

import cn.hutool.json.JSONUtil;
import com.example.demo.model.request.DemoReq;
import com.example.demo.model.response.DemoResp;
import com.example.demo.pipeline.factory.DemoPipelineProduct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 審核-信息提交-業務實現
 *
 * @author 
 * @date 2023/05/12 14:36
 */
@Service
@Slf4j
public class ManagerSubmitJob extends AbstractDemoJob {

    /**
     * 子類執行邏輯
     *
     * @param tradeId     流程Id
     * @param productData 請求數據
     * @return
     * @throws Exception 異常
     */
    @Override
    DemoPipelineProduct.DemoSignalEnum execute(String tradeId, DemoPipelineProduct.DemoProductData productData) throws Exception {
        DemoReq userRequestData = productData.getUserRequestData();
        try {
            /*
             * DB操作
             */
            log.info("任務[{}]信息提交,線程號:{}", JSONUtil.toJsonStr(userRequestData), tradeId);
            productData.setUserResponseData(DemoResp.buildRes("成功"));
        } catch (Exception ex) {
            log.error("審核-信息提交-DB操作失敗,入參:{}", JSONUtil.toJsonStr(userRequestData), ex);
            throw ex;
        }
        return DemoPipelineProduct.DemoSignalEnum.NORMAL;
    }
}





5.3 完成流水線組裝

針對入回參轉換,管道任務執行順序及執行信號量的構建

package com.example.demo.pipeline.factory;

import com.example.demo.model.request.DemoReq;
import com.example.demo.model.response.DemoResp;
import com.example.demo.pipeline.factory.job.CheckRequestLockJob;
import com.example.demo.pipeline.factory.job.CheckRequestUnLockJob;
import com.example.demo.pipeline.factory.job.ManagerCheckParamJob;
import com.example.demo.pipeline.factory.job.ManagerSubmitJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Objects;
import java.util.UUID;

/**
 * 管道工廠入口-審核流水線
 *
 * @author 
 * @date 2023/05/15 19:52
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PipelineForManagerSubmit {

    /**
     * 審核-管道節點
     */
    private final DemoPipelineNode managerSubmitNode = new DemoPipelineNode();


    /**
     * 審核-管道任務-提交-防刷鎖-加鎖
     */
    private final CheckRequestLockJob checkRequestLockJob;

    /**
     * 審核-管道任務-提交-防刷鎖-解鎖
     */
    private final CheckRequestUnLockJob checkRequestUnLockJob;

    /**
     * 審核-管道任務-參數驗證
     */
    private final ManagerCheckParamJob managerCheckParamJob;

    /**
     * 審核-管道任務-事務操作
     */
    private final ManagerSubmitJob managerSubmitJob;


    /**
     * 組裝審核的處理鏈
     */
    @PostConstruct
    private void assembly() {
        assemblyManagerSubmit();
    }

    /**
     * 組裝處理鏈
     */
    private void assemblyManagerSubmit() {

        managerSubmitNode
                // 參數驗證及填充
                .flax(managerCheckParamJob)
                // 防刷鎖
                .flax(checkRequestLockJob)
                // 事務操作
                .flax(managerSubmitJob)
                // 鎖釋放
                .flax((ignore) -> true, checkRequestUnLockJob);
    }

    /**
     * 審核-提交處理
     *
     * @param requestData 入參
     * @return
     */
    public DemoResp managerSubmitCheck(DemoReq requestData) {
        DemoPipelineProduct initialProduct = managerSubmitCheckInitial(requestData);
        DemoPipelineProduct finalProduct = managerSubmitNode.execute(initialProduct);
        if (Objects.isNull(finalProduct) || Objects.nonNull(finalProduct.getException())) {
            return DemoResp.buildRes("未知異常");
        }
        return finalProduct.getProductData().getUserResponseData();
    }

    /**
     * 審核-初始化申請的流水線數據
     *
     * @param requestData 入參
     * @return 初始的流水線數據
     */
    private DemoPipelineProduct managerSubmitCheckInitial(DemoReq requestData) {
        // 初始化
        return DemoPipelineProduct.builder()
                .signal(DemoPipelineProduct.DemoSignalEnum.NORMAL)
                .tradeId(UUID.randomUUID().toString())
                .productData(DemoPipelineProduct.DemoProductData.builder().userRequestData(requestData).build())
                .build();
    }
}




總結

本文重點為管道模式的抽象與應用,上述示例僅為個人理解。實際應用中,此案例長於應對各種規則冗雜的業務場景,便於規則編排。
待改進點:

  1. 各個任務其實隱含了執行的先後順序,此項內容可進一步實現;

  2. 針對最後“流水線組裝”這一步,可通過配置描述的方式,進一步抽象,從而將變動控制在每個“管道任務”的描述上,針對規則項做到“可插拔”式處理。

作者:京東保險 侯亞東

來源:京東雲開發者社區 轉載請註明來源


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • tmux教程 功能 分屏:可以在一個開發框里分屏 允許terminal在連接斷開之後可以繼續運行,讓進程不會因為斷開連接而中斷 結構 // 一個tmux可以包含多個session,一個session可以包含多個window,一個window可以包含多個pane。 tmux: session 0: w ...
  • 本文分享自華為雲社區《GaussDB資料庫SQL系列-層次遞歸查詢》,作者: Gauss松鼠會小助手2。 一、前言 層次遞歸查詢是一種常見的SQL查詢方式,特別是在一些層次化的數據存儲結構中經常用到。本文主要以GaussDB資料庫為實驗平臺,為大家講解其使用方法。 二、GuassDB資料庫層次遞歸查 ...
  • 在我們應用中的使用場景來看,簡單來說通常會看中了clickhouse在處理大批量數據的寫入和讀取分析方面的性能,MySQL會主要負責一些基於模型進行指標二次加工的高頻查詢及複雜join的查詢。 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 大家好,今天聊一下在做uniapp多端適配項目,需要用到自定義導航時,如何解決狀態欄塌陷及導航欄安全區域多端適配問題,下文只針對H5、APP、微信小程式三端進行適配,通過封裝一個通用高階組件包裹自定義導航欄內容,主要是通過設置pad ...
  • 求上進的人,不要總想著靠誰,人都是自私的,自己才是最靠得住的人。 React 中生命周期劃時代幾個節點,React 16.2 之前處於老的生命周期,之後提出了新的生命周期。而函數式組件在 React 16.8 之前是沒有狀態和生命周期的,在 React 16.8 版本通過引入 Hooks 使得函數式 ...
  • HTML中的title標簽是非常重要的標簽之一,它用來描述網頁的標題。在搜索引擎優化中,title標簽是非常關鍵的,因為搜索引擎會將title標簽中的文字作為頁面的主要描述,並根據其相關性來判斷網頁內容的質量和權重。 ...
  • 小程式中最常見的功能就是底部導航欄了,今天就來看一下怎麼設置一個好看的導航欄~這裡我們使用的是支付寶官方小程式 IDE 做示範。 ...
  • antd Pro組件ProFormList自定義action ProFormList是ant design pro的結構化數據組件,通常用來實現動態表單。 現在有個需求,除了組件自帶的刪除和複製,還需要增加兩個按鈕來實現每個item位置的上下移動,如圖所示: 查看官方文檔,組件有提供自定義actio ...
一周排行
    -Advertisement-
    Play Games
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...