【问题标题】:C# Returning a generic interface from a factoryC# 从工厂返回通用接口
【发布时间】:2017-10-29 12:23:20
【问题描述】:

我已经实现了一个 Vehicle 服务,负责维修汽车和卡车等车辆:

public interface IVehicleService
{
    void ServiceVehicle(Vehicle vehicle);   
}

public class CarService : IVehicleService
{
    void ServiceVehicle(Vehicle vehicle)
    {
        if(!(vehicle is Car))
            throw new Exception("This service only services cars")

       //logic to service the car goes here
    }
}

我还有一个车辆服务工厂,负责根据传入工厂方法的车辆类型创建车辆服务:

public class VehicleServiceFactory 
{
    public IVehicleService GetVehicleService(Vehicle vehicle)
    {
        if(vehicle is Car)
        {
            return new CarService();
        }

        if(vehicle is Truck)
        {
            return new TruckService();
        }

        throw new NotSupportedException("Vehicle not supported");
    }

}

我遇到的主要问题是CarService.ServiceVehicle 方法。它接受Vehicle,而理想情况下它应该接受Car,因为它知道它只会为汽车服务。所以我决定更新这个实现以使用泛型:

public interface IVehicleService<T> where T : Vehicle
{
    void ServiceVehicle(T vehicle); 
}

public class CarService : IVehicleService<Car>
{
    void ServiceVehicle(Car vehicle)
    {
        //this is better as we no longer need to check if vehicle is a car

        //logic to service the car goes here 
    }
}

我遇到的问题是如何更新 VehicleServiceFactory 以返回车辆服务的通用版本。我尝试了以下方法,但它导致编译错误,因为它无法将 CarService 转换为通用返回类型 IVehicleService:

public class VehicleServiceFactory 
{
    public IVehicleService<T> GetVehicleService<T>(T vehicle) where T : Vehicle
    {
        if(vehicle is Car)
        {
            return new CarService();
        }

        if(vehicle is Truck)
        {
            return new TruckService();
        }

        throw new NotSupportedException("Vehicle not supported");
    }

}

任何建议将不胜感激。

【问题讨论】:

    标签: c# generics polymorphism


    【解决方案1】:

    只需将服务强制转换为接口:

    return new CarService() as IVehicleService<T>;
    

    知道T是car,但编译器不知道,它不够聪明,无法遵循方法逻辑,也不是它的本意;据编译器所知,T 可以是任何东西,只要它是Vehicle。你需要告诉编译器,“嘿,我知道我在做什么,TCar 实际上是同一个类型。”

    【讨论】:

    • 这也是一个正确的答案,但问题仍然存在:你打算如何处理IVehicleService&lt;T&gt; 类型的返回值?对于每种类型的服务,您都需要一个不同类型的变量。声明这样的接口可能会更有用:public interface IVehicleService&lt;T&gt; : IVehicleService。 IE。您从非泛型接口派生出泛型接口。这使您能够以中立的方式使用车辆服务。您可以以通用方式实现,但以非通用方式使用。你甚至可以创建一个List&lt;IVehicleService&gt; 包含不同种类的服务。
    【解决方案2】:

    这对你有用吗?

    public class VehicleServiceFactory
    {
        public S GetVehicleService<T, S>(T vehicle)
            where T : Vehicle
            where S : IVehicleService<T>, new()
        {
            return new S();
        }
    }
    

    【讨论】:

    • 拥有这样的工厂有什么意义?如果工厂的客户必须写 factory.GetVehicleService() 那么为什么不写 new CarService() 呢?抽象工厂设计模式的重点是将服务实现与服务客户端分离。
    • @felix-b - OP 没有明确表示他们试图混淆返回类型的创建。他们只是问如何编写这个接口。除此之外,客户端还可以指定类型和工厂来决定将对象返回给客户端所需的自定义步骤。这也可以用于跟踪返回的关联引用 - 调用 new CarService() 不允许这样做。
    【解决方案3】:

    @InBetween 建议的解决方案是最直接的解决方案。但是,如果继承者的数量超过 2 或 3 个,或者预计会增加,我会主张采用不同的解决方案。

    如果我们编写if (vehicle is Car)if (vehicle is Truck),我们可能在代码的多个位置都遵循这种模式。当我们需要引入另一种车辆时会发生什么?我们必须更改代码中检查具体实现的每个地方。这不是 OOP 的使用方式。

    设计目标是在引入抽象的新实现时避免对现有代码进行多次更改

    public class VehicleServiceFactory
    {
        // the code of this method doesn't change when new Vehicle type is introduced
    
        public IVehicleService<T> GetVehicleService<T>(T vehicle) where T : Vehicle
        {
            Func<object> concreteFactory;
    
            if (_factoryByType.TryGetValue(typeof(T), out concreteFactory))
            {
                var serviceInstance = (IVehicleService<T>)concreteFactory();
                return serviceInstance;
            }
    
            throw new NotSupportedException("Vehicle not supported");
        }
    
        // the simplest service locator below is just an example
        // of decoupling clients of an abstraction from its implementations
    
        // instead of hard-coded initialization, the dictionary can be built 
        // from configuration or a database, for example
    
        private static readonly Dictionary<Type, Func<object>> _factoryByType = 
            new Dictionary<Type, Func<object>> {
                { typeof(Car),   () => new CarService() },
                { typeof(Truck), () => new TruckService() }
                // .... the rest of Vehicle types
            };
    
    }
    

    所以在这里我只是在工厂内部使用 Dictionary,因为我对您系统的其余部分了解不多。一般来说,想想你是如何在其余代码中进行最小更改的情况下引入新类型的车辆的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-24
      • 2023-03-27
      相关资源
      最近更新 更多