多類繼承情況下適應變化設計一例

来源:http://www.cnblogs.com/buguge/archive/2017/06/23/7071763.html
-Advertisement-
Play Games

8個派生都這麼改還是挺麻煩的,也違背了OCP原則。另外,從領域的角度來說,logFlag參數與整個功能並無關係,只是為了完善記錄日誌才“生硬地”加這麼一個參數。所以,上面的實現方案不妥。改為封裝一個LogFlag屬性。這樣,只需修改基類,派生類無需任何改動。調用方在實例化對象後,可以為LogFlag... ...


在支付中心的中信支付渠道實現層里,關於每個支付介面的對接實現,類圖設計方式如下(後附支付中心程式框架-分層結構),諸如獲取動態支付碼、公眾號/服務窗、訂單查詢、關單、退款、代付、代付查詢等每種支付介面的api實現均繼承了同一個基類。


ClassDiagram
支付中心程式框架-分層結構

基類封裝了請求渠道方api所必須的參數驗證、簽名、生成請求報文、發起請求、驗證響應報文這一系列環節。這樣的OO設計,可以簡化每個支付介面api類的邏輯實現,它們只需構造請求模型,然後調用基類Communicate方法,然後轉換成相應模型即可。

投產後,線上交易量太大。系統運維過程中往往要通過查日誌來協助排障,比如某次訂單查詢的請求返回了錯誤的信息“訂單未支付”,那麼就要定位該次請求所對應的渠道請求報文日誌和響應報文日誌。

對於支付中心支付介面收到的每一次請求,我會生成一個隨機字元串作為logflag,來統一標識每一次請求的處理過程(涉及到項目的每一層,如webapi層、交易服務層、BLL層、DAL層)中所對應的日誌。 見下圖中的“[OrderQuery_180001914_C72FF]”、“[OrderQuery_180002492_C6E22]”、“[JSPay_102157155_D0F3F]”、“[180002CITIC648]”。


交易日誌截圖

美中不足的是,渠道通訊層所記錄的日誌沒有這個logflag,導致很難與webapi等層的日誌對應起來。 本文要說的就是這一完善,接收外部傳來的logflag參數,在記日誌的時候打上這個logflag標識。(後來與同事閑聊時,得知可以用當前Thread的Name來很容易的實現這個統一標記交易日誌的功能,本文重點是分析本次重構過程,所以姑且不講這些)

基類CiticAPIBase是抽象類:

public abstract class CiticAPIBase<TRequestModel, TResponseModel>
    where TRequestModel : RequestDTOBase
    where TResponseModel : ResponseDTOBase
{

    readonly string LOG_FLAG = string.Format("[{0}CITIC{1}]", DateTime.Now.ToString("HHmmss"), new Random().Next(9999));
    protected LogHelperUtil _LogHelperUtil;

    public CiticAPIBase()
    {
        _LogHelperUtil = new LogHelperUtil(LOG_FLAG);
    }

    public abstract TResponseModel Invoke(TRequestModel reqModel);

    public string Communicate(RequestModelCommon citicReqModel)
    {
        try
        {
            CiticCommon citicCommon = new CiticCommon(LOG_FLAG);
            var json = citicCommon.Invoke(_reqModel);
            //_LogHelperUtil.WriteLog("渠道介面處理結果:{0}", json);
            return json;
        }
        catch (ResponseErrorException ex)
        {
            throw new ResponseErrorException("【上游通道】" + ex.Message);
        }
    }
}

 

派生類之一_61InitJSAPI(實現的支付介面是公眾號/服務窗),重寫Invoke方法:

public class _61InitJSAPI : CiticAPIBase<JSPayRequestDTO, JSPayResponseDTO>
{    
    public override JSPayResponseDTO Invoke(JSPayRequestDTO reqDto)
    {
        var citicReqDto = new _61InitJSAPIRequestModel()
        {
            out_trade_no = reqDto.order_no,
            body = reqDto.goods_name,
            total_fee = reqDto.pay_money,
            mch_create_ip = ReadIp.Ip_GetIPAddress(),
            notify_url = PartnerConfig.PayNotifyUrl,
            callback_url = reqDto.return_url,
            is_raw = "1", //reqModel1.is_raw, 我司與青島中信配的是原生態js支付。所以,這裡寫死。

            sub_openid = reqDto.user_client_name,//TODO:
            mch_id = reqDto.merchant_id,
        };
        if (citicReqDto.mch_id == PartnerConfig.MCH_ID_TEST)
        {
            citicReqDto.sub_openid = string.Empty;
        }

        //----調用中信支付通道
        var json = base.Communicate(citicReqDto);
        var respModel = JsonConvert.DeserializeObject<_61InitJSAPIResponseModel>(json);
        string pay_url = string.Format("https://pay.swiftpass.cn/pay/jspay?token_id={0}&showwxtitle=1", model.token_id);
        var returnDto = new JSPayResponseDTO()
        {
            StatusIsSuccess = true,
            ReturnCodeIsSuccess = true,
            pay_info = respModel.pay_info,
            pay_url = pay_url,
        };
        return returnDto;
    }
}

 

派生類之一_8Reverse代碼(實現的支付介面是關單),重寫Invoke方法:

/// <summary>
/// 8 關閉訂單介面
/// </summary>
public class _8Reverse : CiticAPIBase<ReverseRequestDTO, ResponseDTOBase>
{
    public _8Reverse(string logFlag) : base(logFlag) { }

    public override ResponseDTOBase Invoke(ReverseRequestDTO reqDto)
    {
        ... ...
    }
}

 

接下來說實現方案。

我是從構造方法入手的,構造方法加了個logFlag參數。調用方在初始化具體的對象時,傳遞logFlag。基類變成瞭如下的樣子:

public abstract class CiticAPIBase<TRequestModel, TResponseModel>
    where TRequestModel : RequestDTOBase
    where TResponseModel : ResponseDTOBase
{

    readonly string LOG_FLAG = string.Format("[{0}CITIC{1}]", DateTime.Now.ToString("HHmmss"), new Random().Next(9999));
    protected LogHelperUtil _LogHelperUtil;

    public CiticAPIBase(string logFlag)
    {
        LOG_FLAG = logFlag + LOG_FLAG;
        _LogHelperUtil = new LogHelperUtil(LOG_FLAG);
    }

    public abstract TResponseModel Invoke(TRequestModel reqModel);

    public string Communicate(RequestModelCommon citicReqModel)
    {
        ... ...
    }
}

 

然後,每個派生類都要顯式聲明構造方法,加上logFlag參數:

public class _61InitJSAPI : CiticAPIBase<JSPayRequestDTO, JSPayResponseDTO>
{
    public _61InitJSAPI(string logFlag) : base(logFlag) { }

    public override JSPayResponseDTO Invoke(JSPayRequestDTO reqDto)
    {
        ... ....
    }
}

public class _8Reverse : CiticAPIBase<ReverseRequestDTO, ResponseDTOBase>
{
    public _8Reverse(string logFlag) : base(logFlag) { }

    public override ResponseDTOBase Invoke(ReverseRequestDTO reqDto)
    {
        ... ...
    }
}

 

8個派生都這麼改還是挺麻煩的,也違背了OCP原則。另外,從領域的角度來說,logFlag參數與整個功能並無關係,只是為了完善記錄日誌才“生硬地”加這麼一個參數。所以,上面的實現方案不妥。改為封裝一個LogFlag屬性。這樣,只需修改基類,派生類無需任何改動。調用方在實例化對象後,可以為LogFlag屬性賦值(if possible)。

public abstract class CiticAPIBase<TRequestModel, TResponseModel>
    where TRequestModel : RequestDTOBase
    where TResponseModel : ResponseDTOBase
{
    public string LogFlag { set { _LOG_FLAG = value + _LOG_FLAG; } }

    string _LOG_FLAG = "";
    protected LogHelperUtil _LogHelperUtil;

    public CiticAPIBase()
    {
        _LOG_FLAG = string.Format("[{0}CITIC{1}]", DateTime.Now.ToString("HHmmss"), new Random().Next(9999));
        _LogHelperUtil = new LogHelperUtil(_LOG_FLAG);
    }

    public abstract TResponseModel Invoke(TRequestModel reqModel);

    public string Communicate(RequestModelCommon citicReqModel)
    {
        ... ...
    }
}

 


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

-Advertisement-
Play Games
更多相關文章
  • 1、下載及安裝 請到官網下載:www.treesoft.cn,要最新的版本treeNMS, window系統下載直接解壓,就可以用了,免安裝,免佈署。 2、登錄及連接參數配置 登錄後,要配置連接參數信息,並測試連接是否成功!連接成功保存,刷新頁面一下。 3、狀態監控 treeNMS有狀態實時監控,及 ...
  • 這是我的SQL Server 2012 酸爽的安裝體驗,包括完全卸載SQL Server以及安裝過程中出現的錯誤的解決方案,分享出來,希望大家能少走彎路。 ...
  • [20170623]利用傳輸表空間恢復部分數據.txt--//昨天我測試使用傳輸表空間+dblink,上午補充測試發現表空間設置只讀才能執行impdp導入原數據,這個也很好理解.--//這樣的操作模式僅僅減少expdp生成原數據的過程.--//我想一下,rman也支持建立傳輸表空間的命令.我仔細看了 ...
  • 前言: 本文是對博客https://www.mssqltips.com/sqlservertip/2827/troubleshooting-sql-server-resourcesemaphore-waittype-memory-issues/的翻譯,本文基本直譯,部分地方讀起來有點不自然。 如有翻... ...
  • 構建本地光碟源 1、將本地光碟掛載/mnt下, mount /dev/cdrom /mnt 2、在/etc/yum.repos.d/目錄下創建自己的源文件,文件名稱自定義,必須以repo結尾, 3、此時把/etc/yum.repos.d/下原來存在的文件備份到別目錄下,即可使用本地光碟源 構建HTT ...
  • 轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/72929800 本文出自 "【趙彥軍的博客】" Fiddler抓包可以完成我們移動開發者的調試測試需求。但是多餘的網頁請求和手機的其他鏈接影響我們手機開發的需求。所以我們需要排除其他無 ...
  • 本篇文章介紹redhat7使用國內阿裡雲yum安裝源的方法,源地址使用的https://mirrors.aliyun.com/對應使用Centos7下的安裝源。文中是自己在安裝阿裡雲yum包和更新yum源地址所做的操作,本人也是新手,如有不足之處請指正。 ...
  • 一、ASP.NET 概述 1. .NET Framework 與 ASP.NET .NET Framework包含兩個重要組件:.NET Framework類庫和公共語言進行時。編寫ASP.NET 頁面需要用到.NET Framework的框架類庫和公共語言進行時 2. ASP.NET MVC簡介 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...