我已经实施了一项汽车服务,负责维修汽车和卡车等车辆:
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");
}
}
任何建议都将不胜感激。
答案 0 :(得分:5)
只需将服务转换为界面:
return new CarService() as IVehicleService<T>;
你知道T
是汽车,但编译器没有,它不够聪明,无法遵循方法逻辑,也不是意图;就编译器所知,T
可以是任何东西,只要它是Vehicle
。你需要告诉编译器,&#34;嘿,我知道我在做什么,T
和Car
实际上是同一类型。&#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();
}
}