c# - 转换和界面困境

标签 c# oop

因此,这些天我越来越使用接口。但是这次我碰到一堵砖墙。

仅出于上下文考虑,让我在这里向您展示RESTful WCF合约,该合约旨在向您展示我如何使用IPaymentRequest:

[ServiceContract]
public interface IPaymentService
{
    [WebInvoke(Method = "POST", UriTemplate = "/PreAuthorization")]
    PreAuthorizeResponse SendTransaction(PreAuthorizeRequest request);

    [WebInvoke(Method = "POST", UriTemplate = "/Capture")]
    CaptureResponse SendTransaction(CaptureRequest captureRequest);

    ... and so on
}


例如,服务合同的实现具有一些类似于以下的方法:

public PreAuthorizeResponse SendTransaction(PreAuthorizeRequest request)
{

   .....
   Processor.SetSettings(request);

 }


(有关干净代码主体的注释/免责声明。我对诸如SetSettings()之类的东西有更好的名称,但出于隐私考虑,我为此Stack帖子更简单地命名了诸如“ SetSettings”和“ Process”之类的东西。实际上,我拥有什么类名称中的处理器,因此仅供参考)。

其次,让我知道您有一个Processor类,该类基本上有点像实用程序类,可以执行一些操作,例如将请求字段发送到外部REST API。在本课程中,我要设置的另一种方法的一个示例是SetSettings方法,该方法将根据传入的请求类型设置一些内容。通常,我将从其Transaction中获取所需的内容属性。

public class Processor
{
    private void SetSettings(IPaymentRequest request)
    {
         var someValue = request.Transaction.SomeProperty1;
         ...
    }
}


现在,这是IPaymentRequest的样子:

public interface IPaymentRequest
{
    string Token { get; set; }
    int ClientID { get; set; }
}


现在,这是实现IPaymentRequest的我的域模型(服务合同期望从客户端请求发送的模型)的几个示例:

public class PreAuthorizeRequest : IPaymentRequest
{
    public string Token { get; set; }
    public int ClientID { get; set; }
    public int Amount { get; set; }
    public PreAuthTransaction Transaction { get; set; }
}

public class CaptureRequest : IPaymentRequest
{
    public string Token { get; set; }
    public int ClientID { get; set; }
    public int BankID { get; set; }
    public CaptureTransaction Transaction { get; set; }
}


我在整个WCF服务中使用IPaymentRequest(这是预计将发送到我的付款服务的方法合同中的类型),并在服务的其他位置使用这些接口对这些请求可以通过的方法进行一些良好的重用,例如SendRequest( IPaymentRequest请求),等等。

这是我遇到的难题/问题:

在我想为传入的任何类型的请求重用逻辑的方法中,我最终不得不检查有时在处理器类中传入我的方法的类型。因此,我必须创建一堆凌乱的if语句才能确定并转换传入的ITransaction,以便在此处的实用程序方法中开始使用它。

因此,让我们继续,以便我可以进一步解释我的第一个方法SetSettings()

注意,我需要请求中来自事务对象的值,并且能够使用该类型的请求中的属性。

现在,让我们看一下CaptureTransaction对象,例如CaptureRequest

public class CaptureTransaction : ITransaction
{
    public string Reference { get; set; }
    public decimal Amount { get; set; }
    public string CurrencyCode { get; set; }
    public CreditCard CustomerCreditCard { get; set; }
}


如您所见,对于每个请求类型,我都有一个相关的具体事务类型,该类型实现ITransaction并保存该事务需要发送到外部API的信息。

注意:所有请求都将始终具有事务(ITransaction),因此我认为将ITransaction放入我的IPaymentRequest中是一个好主意,如下所示:

public interface IPaymentRequest
{
    string Token { get; set; }
    int ClientID { get; set; }
    ITransaction Transaction {get; set; }
}


这是ITransaction。进入我们服务的每个请求现在和将来都将需要货币,因此该字段是使用Interface的很好的候选人/理由:

public interface ITransaction
{
    string CurrencyCode { get; set; }
}


因此,现在将其添加到我的IPaymentRequest中需要我将“自定义类型”中的“属性名称”更改为“交易”,例如:

public class CaptureRequest : IPaymentRequest
{
    public string Token { get; set; }
    public int ClientID { get; set; }
    public int BankID { get; set; }
    public ITransaction Transaction { get; set; }
}


我还可以。

但是现在,如果我尝试在我的实用程序方法中使用事务,因为它是一个接口变量,所以不知道它是什么类型的事务。因此,最终我不得不在使用它之前强制转换它:

private void SetSettings(IPaymentRequest request)
{
     ITransaction transaction;

     if (request is CaptureRequest)
         transaction = request.Transaction as CaptureTransaction;
     if (request is PreAuthorizeRequest)
         transaction = request.Transaction as PreAuthorizeTransaction;

... etc.

     var someValue = request.Transaction.Some1;
     ...carry on and use SomeProperty1elsewhere in this method for whatever reason
}


IMO感觉就像强烈的代码气味。所以很明显,我没有做正确的事,或者我对接口应该了解的东西一无所知……这使我可以在这里更好地使用它们,而无需太多的转换。从性能角度来看,过多地投射IMO是不好的。

也许这是在我要创建的方法中使用泛型而不是接口参数以在不同类型的具体请求类型(Capture,PreAuth,Void yada yada)之间重用的好例子?

这里的重点是我希望能够以某些方法指定接口参数以使其干燥(不要重复自己)/可重用...然后使用通过多态性引入的具体类型并与请求实例一起使用。

最佳答案

如果每个请求都有一个事务,那么这是正确的方法:

interface IPaymentRequest
{
    string Token { get; set; }
    int ClientID { get; set; }
    ITransaction Transaction { get; set; }
}


显然,要处理自定义请求,您将需要一个自定义处理器:

class Processor
{
    protected virtual void OnSetSettings(IPaymentRequest request)
    {
    }

    private void SetSettings(IPaymentRequest request)
    {
        // do the common stuff
        // ...
        // set custom settings
        OnSetSettings(request);
    }
}

class PreAuthorizeRequestProcessor : Processor
{
    protected override void OnSetSettings(IPaymentRequest request)
    {
        base.OnSetSettings(request);

        // set custom settings here
        var customRequest = (PreAuthorizeRequest)request;
    }
}


如您所见,这需要一点类型转换。可以避免使用泛型进行转换,但这在类型声明中带来了复杂性:

interface IPaymentRequest<TTransaction>
    where TTransaction : ITransaction
{
    string Token { get; set; }
    int ClientID { get; set; }
    TTransaction Transaction { get; set; }
}

class Processor<TRequest, TTransaction>
    where TRequest : IPaymentRequest<TTransaction>
    where TTransaction : ITransaction
{
    protected virtual void OnSetSettings(TRequest request)
    {
    }

    private void SetSettings(TRequest request)
    {
        // do the common stuff
        // ...
        // set custom settings
        OnSetSettings(request);
    }
}

class PreAuthorizeRequestProcessor : Processor<PreAuthorizeRequest, PreAuthTransaction>
{
    protected override void OnSetSettings(PreAuthorizeRequest request)
    {
        base.OnSetSettings(request);

        // set custom settings here
    }
}

关于c# - 转换和界面困境,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21474398/

相关文章:

javascript - JavaScript 中动态类型运算符的双重调度

Java,通过arraylist调用对象方法

Javascript - 调用父函数和扩展方法

python - “self”未定义 - 使用计时器进行线程处理

c# - 如何迭代数据表

c# - 读取后无法写入文件

c# - 多行文本框 - 在回发开始时添加空格

c# - 在类声明中初始化对象与构造函数的区别

C# - Datagridview 不显示来自 MySQL 的数据

C#:抽象策略基类作为策略对象的抽象工厂