简体   繁体   English

C#从工厂返回通用接口

[英]C# Returning a generic interface from a factory

I've implemented a Vehicle service that is responsible for servicing vehicles such as cars and trucks: 我已经实施了一项车辆服务,负责维修汽车和卡车等车辆:

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
    }
}

I also have a vehicle service factory that is responsible for creating a vehicle service according to the type of vehicle passed in to the factory method: 我还有一个车辆服务工厂,负责根据传入工厂方法的车辆类型创建车辆服务:

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

}

The main issue I have is specifically with CarService.ServiceVehicle method. 我遇到的主要问题是CarService.ServiceVehicle方法。 It's accepting a Vehicle when ideally it should accept a Car instead, as it knows that it will only service cars. 它接受一辆Vehicle ,理想情况下它应该接受一辆Car ,因为它知道它只会服务汽车。 So I decided to update this implementation to use generics instead: 所以我决定更新此实现以使用泛型:

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 
    }
}

The issue I'm having is how to update VehicleServiceFactory to return the generic version of the vehicle service. 我遇到的问题是如何更新VehicleServiceFactory以返回车辆服务的通用版本。 I've tried the following but it results in a compilation error as it's unable to cast CarService to the generic return type IVehicleService: 我尝试了以下但它导致编译错误,因为它无法将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");
    }

}

Any suggestions would be appreciated. 任何建议,将不胜感激。

Simply cast the service to the interface: 只需将服务转换为界面:

return new CarService() as IVehicleService<T>;

You know T is car but the compiler doesn't, its not smart enough to follow the method logic nor is it meant to be; 知道T是汽车,但是编译器没有,它不够聪明,无法遵循方法逻辑,也不是意味着; as far as the compiler knows, T can be anything as long as its a Vehicle . 就编译器所知, T可以是任何东西,只要它是一辆Vehicle You need to tell the compiler, "Hey, I know what I'm doing, T and Car are in fact the same type." 你需要告诉编译器,“嘿,我知道我在做什么, TCar实际上是同一类型。”

Solution that @InBetween suggests is the most straightforward one. @InBetween建议的解决方案是最直接的解决方案。 Yet, if number of inheritors is more than 2 or 3, or if it's expected to grow, I would advocate for a different solution. 然而,如果继承人的数量超过2或3,或者如果它预计会增长,我会主张采用不同的解决方案。

If we write if (vehicle is Car) and if (vehicle is Truck) , we probably follow this pattern in multiple places in the code. 如果我们写if (vehicle is Car)if (vehicle is Truck) ,我们可能会在代码中的多个位置遵循这种模式。 What happens when we need to introduce another kind of Vehicle? 当我们需要引入另一种车辆时会发生什么? We'll have to change every place in the code where we check for concrete implementations. 我们必须更改代码中检查具体实现的每个位置。 This is not how OOP is intended to be used. 这不是OOP的使用方式。

The design goal is avoiding multiple changes in existing code when introducing a new implementation of an abstraction . 设计目标是在引入抽象的新实现时避免现有代码中的多个更改

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

}

So here I just use Dictionary inside the factory, since I don't know much about the rest of your system. 所以这里我只是在工厂里面使用Dictionary,因为我对你系统的其余部分了解不多。 In general, think how you introduce new types of Vehicle with minimal changes in the rest of your code. 一般来说,想一想如何在代码的其余部分中引入新类型的Vehicle,只需要进行少量更改。

Does this work for you? 这对你有用吗?

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM