【问题标题】:Interface with two different return types unknown till runtime具有两种不同返回类型的接口,直到运行时才知道
【发布时间】:2020-10-02 08:30:00
【问题描述】:

抱歉,如果之前已经问过这个问题。我是这门语言的新手,真的不知道如何正确表达我的问题。 我目前必须重写一个使用不再维护的 C# 库的应用程序。有一个新的 API 可以做同样的事情,但使用不同的数据结构。 因为我必须同时支持它们,所以我的想法是创建一个通用接口来访问所选 API 的方法。 我正在考虑执行以下操作

public interface IAPIService<T> {
    List<T> GetEntries(...);
    //...
}
public class API1Service: IAPIService<APIType1> {...};
public class API2Service: IAPIService<APIType2> {...};

在我的主类中使用类似

IAPIService<?????> api;
if (API1) { api = new API1Service(); }
else { api = new API2Service(); }

但是,由于我已经输入了它们,并且直到运行时我才知道要使用哪种类型,所以我被卡住了。

【问题讨论】:

  • APIType1APIType2 有什么共同点吗?
  • 您正在向客户端泄露实现细节。如果您想隐藏您实际使用的 API,那么客户端无法告诉您。这也意味着,如果存在特定于各个 API 的数据结构,您也需要对它们进行抽象/规范化。
  • 决定使用哪个 API 的标准是什么?
  • 我猜配置文件中的条目或命令行参数就足够了。
  • 看来您需要为返回类型编写一个包装器。究竟是什么意思:“它们只是在语义上相等”?

标签: c# .net generics interface


【解决方案1】:

如果新旧 API 在其用途之外没有任何共同点,那么泛型不是适合您的工具。您需要隐藏以下事实:来自客户端的两个不同 API。

所以,我的建议是这样的:

假设旧 API 是 APIv1,新 API 是 APIv2

// This is the CLIENT-Facing Facade (=internal API) which _hides_ the actual impl.
public interface IAPIFacade
{
     MyDataStructure1 DoSomething( MyDataStructure2 input ); 
}

public class APIFacadeImplForAPIv1 : IAPIFacade
{
     private readonly IAPIv1 apiClient;

     MyDataStructure1 DoSomething( MyDataStructure2 input )
     {
           // translate specific datastructures somehow
           ApiV1SpecificDS2 apiInput = Convert<ApiV1SpecificDS2>(input)
           var result = apiClient.DoSomething( apiInput );
           // translate to common datastructure from api - specific
           return Convert<MyDataStructure1>(result);
     }
}

APIv2 类似...

public class APIFacadeImplForAPIv2 : IAPIFacade // Notice SAME interface!
{
     private readonly IAPIv2 apiClient;

     MyDataStructure1 DoSomething( MyDataStructure2 input )
     {
           // translate specific datastructures somehow
           ApiV2SpecificDS2 apiInput = Convert<ApiV2SpecificDS2>(input)
           var intermediateResult = apiClient.DoSomethingTheOtherWay( apiInput );
           var result = apiClient.InV2IneedTwoCalls(intermediateResult);
           // translate to common datastructure from api - specific
           return Convert<MyDataStructure1>(result);
     }
}

现在,在客户端中,您可以使用 DI 或工厂或任何您认为适合该任务的东西:

// ONE interface       vv Factory decides which wrapper to inst. based on config
IAPIFacade apiClient = APIFactory.GetClient(config);

有点像 Microsoft Logging API。您可以“插入”许多不同的日志框架。他们所需要的只是 MS API 和他们自己的 API 之间的支持桥梁。但客户最终不在乎。

【讨论】:

    【解决方案2】:

    你可以像这样使用工厂伙伴

    private readonly IDictionary<Type, object> _apiService;
    
    
    public IAPIService<T> GetService<T>() where T : class
            {
                IRepository<T> apiService;
                if (_apiService.Keys.Contains(typeof(T)))
                    apiService= _apiService[typeof(T)] as IAPIService<T>;
                else
                {
                    apiService= new IAPIService<T>();
                    this._apiService.Add(typeof(T), repository);
                }
    
                return apiService;
            }
    

    【讨论】:

    • 这仍然会给你一个强类型的返回值,这意味着客户端需要提前知道将使用哪个 API。这一切的目的都失败了。泛型在这里不是正确的解决方案。
    • 这不是工作单元的实现。这是一个返回不同服务实例的工厂
    • ^^ * ... 不同服务的不同实例。
    猜你喜欢
    • 1970-01-01
    • 2011-08-18
    • 2012-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-21
    • 2019-08-20
    • 1970-01-01
    相关资源
    最近更新 更多