我如何将具有相同目的但不同输入和输出的 类 统一到一个接口

How can i unify classes that have same purpose but different input and output to one interface

我需要统一一些具有相同目的但使用相同方法但参数和结果不同的 classes。

这对你来说可能听起来很奇怪,所以让我解释一下。

我有多个 class 连接到多个 rest/web 服务以从中获取数据。对于这个例子,假设所有这些都是天气预报服务。每个人都做同样的事情。 Return 对某些地区进行预测,但他们都在按照自己的方式进行。对于每个服务,我有 class 实现从它收集数据并映射到我的对象:

public class AForecast
{
    public AForecastResult GetForecast (AForecastRequest request)
    {
         // Grab Forecast
    }
}

public class BForecast
{
    public BForecastResult GetForecast (BForecastRequest request)
    {
         // Grab Forecast
    }
}

我正在调用那些 classes,获取预测数据,然后将其映射到我自己的对象中,这很好。我的问题是现在我有 13 个预报服务。他们中的许多人都使用类似的方法将预测结果映射到我自己的对象中。下一个问题也是,现在可能只有我知道如何将新的预测添加到系统中。我想统一它以便能够将一些接口添加到 Forecast 服务实现中,以及一些基本预测映射器。

我创建了界面,但因为我不知道预测结果和请求看起来如何,所以它非常通用:

public interface IForecast<out TResult, in TRequest>
{
    TResult GetForecast(TReqiest request)
}

对于每个预测,我创建了单独的接口来实现 IForecast

public interface IAForecast : IForecast<AForecastResult, AForecastRequest>
{
}

我的 AForecast 实施开始时如下所示:

public class AForecast : IAForecast 
{
    public AForecastResult GetForecast (AForecastRequest request)
    {
         // Grab Forecast
    }
}

多亏了它,我才有了拥有自己的接口和通用基础接口的 Forecast 服务。

问题是当我想在基础 class 中使用它时,它将能够调用每个预报服务和地图对象:

public abstract ForecastBase
{
    private readonly ?ForecastService _service;
    protected ForecastBase(?ForecastService service)
    {
        _service = service;
    }

    public MapedObject GetForecast(DateTime date, string zip)
    {
        var request = GetRequest(date,zip);
        var forecastServiceResponse = _service.GetForecast(request); 
        return Map(forecastServiceResponse);
    } 

    protected abstract MapedObject Map(?Response response);
    protected abstract ?Request GetRequest(DateTime date, string zip);
}

哦,这很长。现在最后一个问题是如何实现 ForeCast base class ?根据我的体系结构,我如何知道 ?ForecastService?Request?Response 的类型。我希望我能像那样制作映射器:

public class AMap : ForecastBase 

并且知道在那种情况下 ?ForecastService 将是 IAForecast?Request 将是 AForecastRequest 并且 ?Response 将是 AForecastResponse

如果您需要更多解释,请随时询问。

我认为您需要的是关键字where(不仅如此,而且它在这里将发挥非常重要的作用)。

我这样定义请求和响应的接口:

public interface IForecastRequest
{

}

public interface IForecastResult
{

}

然后我们可以继续为预测本身定义接口:

public interface IForecast<out TForecastResult, in TForecastRequest> 
    where TForecastResult : IForecastResult where TForecastRequest : IForecastRequest
{
    TForecastResult GetForecast(TForecastRequest request);
}

这里发生的事情如下。除了您的解决方案之外,我们还限制了通用类型来实现我们的请求和响应接口。然后我们可以添加通用方法来获取 ForecastResult。顺便说一下,GetForecast 似乎也不是一个好名字。最好是 GetResultGetForecastResult,否则您会期待一个不同的 return-type。

现在我们可以实现 BaseForecast 了。我希望我正确地理解了这应该做什么。因为您将其称为 ForecastBase,所以我不确定这是否应该与 IForecast 做同样的事情。也许一个更好的名字在这里是个好主意。我是这样实现的:

public abstract class BaseForecast<TForecastResult, TForecastRequest> 
    where TForecastResult : IForecastResult where TForecastRequest : IForecastRequest
{
    private readonly IForecast<TForecastResult, TForecastRequest> _service;
    protected BaseForecast(IForecast<TForecastResult, TForecastRequest> service)
    {
        _service = service;
    }

    public MappedObject GetForecast(DateTime date, string zip)
    {
        TForecastRequest request = GetRequest(date, zip);
        TForecastResult forecastServiceResponse = _service.GetForecast(request);
        return Map(forecastServiceResponse);
    }

    protected abstract MappedObject Map(TForecastResult response);
    protected abstract TForecastRequest GetRequest(DateTime date, string zip);
}

这里我们还必须添加 where,因为我们必须使用相同的约束调用 IForecast 接口的通用方法。您可以看到,在构造函数中,我们提供了 IForecast 实现的实例,这将是我们的服务。该服务使用我们在该基类的定义中也有的两种通用类型。 GetForecast-method 现在可以使用 IForecast 的通用方法和自己的通用方法 (GetRequest) 来获取 MappedObject(我不知道它是什么)。所有类型都对齐,您可以获得方便的智能感知和 IForecastRequest 和 IForecastResult 的 compiletime-typechecking。

请告诉我是否正确理解了 ForecastBase 的用途。
我希望这有帮助。我很乐意尝试回答您可能对此提出的任何问题。

编辑:

I wish i can make mapper like that..

您在此处显示的方式未使用任何 generic-type 参数。据我了解您的意图,如果不指定要使用的结果和请求,您将无法执行此操作。
我为此创建实现的方式如下:

// implement request
public class SomeForecastRequest : IForecastRequest
{

}

// implement result
public class SomeForecastResult : IForecastResult
{

}

// implement forecast itself
public class SomeForecast : IForecast<SomeForecastResult, SomeForecastRequest>
{
    public SomeForecastResult GetForecast(SomeForecastRequest request)
    {
        // return the result you got from wherever
    }
}

public class SomeMapper : BaseForecast<SomeForecastResult, SomeForecastRequest>
{
    public SomeMapper(IForecast<SomeForecastResult, SomeForecastRequest> service) : base(service)
    {

    }

    protected override SomeForecastRequest GetRequest(DateTime date, string zip)
    {
        // return a request from wherever
    }

    protected override MappedObject Map(SomeForecastResult response)
    {
        // map the response and return it
    }
}

编辑 2: 我刚刚阅读了您的评论,内容如下:

.. They are not related to each other. And those object don't have own interfaces, and I don't wrapped it in own interface

如果你想保持这种方式,你无法为你的映射器创建一个 base-class。映射器需要知道 AForecast 有一个名为 GetForecast 的方法。如果没有,它将如何调用它。如果你尝试这个,你将进入 compiler-error 土地。你需要告诉编译器 "this class can handle any class if it has the GetForecast-method" 否则它会拒绝尝试调用 GetForecast。告诉编译器的方法是说 "dude look I got this awesome interface which has the GetForecast-method. I'm only going to allow the caller to use a class which implements this very interface so you can be sure the GetForecast-method exists. Alright?"。这就是您使用 where 关键字的原因,我感谢您提出问题,因为这是一个关于如何使用它的非常好的(但不太容易)示例。

编辑 3:
顺便说一下,没有什么可以阻止您使用 类 而不是 IForecastRequestIForecastResult 的接口(当然您会更改名称,但其他一切都可以保持不变)。我不知道你的请求和响应对象应该是什么 do/store 所以我不知道在接口上使用 类 是否适合你。
我只想说这也是可能的。

我很想听听您的一些反馈:)