An example of adaptive change design under multi-class inheritance

Keywords: C# JSON Attribute

In the CITIC Payment Channel Implementation Layer of Payment Center, the design of class diagram is as follows (with Payment Center Program Framework-Hierarchical Structure), such as obtaining dynamic payment code, public number/service window, order query, customs clearance, refund, payment, payment query and so on. The api implementation of each payment interface inherits the same base class.


ClassDiagram

Payment Center Program Framework-Hierarchical Structure

The base class encapsulates a series of links, such as parameter verification, signature, request message generation, request initiation and response message verification, which are necessary for the api of the requesting channel. Such OO design can simplify the logical implementation of each payment interface api class. They only need to construct the request model, then call the base class Communicate method, and then convert it into the corresponding model.

After putting into operation, the volume of online transactions is too large. In the process of system operation and maintenance, it is often necessary to help troubleshoot by checking logs. For example, a request for an order query returns the wrong information "order not paid". Then it is necessary to locate the channel request message log and response message log corresponding to the request.

For each request received by the payment interface of the payment center, I will generate a random string as logflag to unify the logs corresponding to each request processing process (involving each layer of the project, such as webapi layer, transaction service layer, BLL layer, DAL layer). See the following figures: "[OrderQuery_180001914_C72FF]," [OrderQuery_180002492_C6E22],"[JSPay_102157155_D0F3F]," [180002CITIC648].


Transaction log screenshot

The drawback is that the logs recorded by the channel communication layer do not have this logflag, which makes it difficult to correspond with the logs recorded by the webapi layer and other layers. This article is about this perfection. It receives the logflag parameters from outside and marks the logflag in the log. (Later, when I chatted with my colleagues, I learned that the current Name of Thread can be used to easily implement the function of the Unified Markup Transaction Log. This article focuses on the analysis of the refactoring process, so let's not mention these.)

The base class CiticAPIBase is an abstract class:

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("Channel interface processing results:{0}", json);
            return json;
        }
        catch (ResponseErrorException ex)
        {
            throw new ResponseErrorException("[Upstream Channel)" + ex.Message);
        }
    }
}

 

One of the derived classes _61InitJSAPI (the implemented payment interface is the public number/service window), overrides the Invoke method:

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, Our company and CITIC Qingdao match the original ecology. js Payment. So, it's written here.

            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;
        }

        //----Call CITIC Payment Channel
        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;
    }
}

 

One of the derived classes, the _8Reverse code (the payment interface implemented is a single), overrides the Invoke method:

/// <summary>
/// 8 Close the order interface
/// </summary>
public class _8Reverse : CiticAPIBase<ReverseRequestDTO, ResponseDTOBase>
{
    public _8Reverse(string logFlag) : base(logFlag) { }

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

 

Next comes the implementation plan.

I started with the construction method, which added a logFlag parameter. When the caller initializes a specific object, it passes logFlag. The base class becomes as follows:

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)
    {
        ... ...
    }
}

 

Then, each derived class explicitly declares the constructor, plus the logFlag parameter:

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)
    {
        ... ...
    }
}

 

It is troublesome for all the eight derivatives to change this way, which also violates the OCP principle. In addition, from a domain point of view, logFlag parameters have nothing to do with the whole function, only to improve the logging to add such a parameter "stiffly". Therefore, the above implementation scheme is inappropriate. Instead, encapsulate a LogFlag attribute. In this way, only the base class needs to be modified, and the derived class does not need to be changed at all. After instantiating the object, the caller can assign a value (if possible) to the LogFlag attribute.

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)
    {
        ... ...
    }
}

Posted by kishan on Wed, 19 Jun 2019 15:29:37 -0700