C#从工厂返回通用接口

时间:2017-10-29 12:23:20

标签: c# generics polymorphism

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

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

}

任何建议都将不胜感激。

3 个答案:

答案 0 :(得分:5)

只需将服务转换为界面:

return new CarService() as IVehicleService<T>;

知道T是汽车,但编译器没有,它不够聪明,无法遵循方法逻辑,也不是意图;就编译器所知,T可以是任何东西,只要它是Vehicle。你需要告诉编译器,&#34;嘿,我知道我在做什么,TCar实际上是同一类型。&#34;

答案 1 :(得分:2)

@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,因为我对你系统的其余部分了解不多。一般来说,想一想如何在代码的其余部分中引入新类型的Vehicle,只需要进行少量更改。

答案 2 :(得分:2)

这对你有用吗?

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