实现通用工厂方法
Implementing a generic factory method
我已经实施了一个车辆服务,负责为汽车和卡车等车辆提供服务:
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
方法。它在理想情况下应该接受 Car
时接受 Vehicle
,因为它知道它只会维修汽车。所以我决定更新此实现以改用泛型:
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
}
}
public class VehicleServiceFactory
{
public IVehicleService<T> GetVehicleService<T>(T vehicle) where T : Vehicle
{
if (vehicle is Car)
{
return new CarService() as IVehicleService<T>;
}
if (vehicle is Truck)
{
return new TruckService() as IVehicleService<T>;
}
throw new NotSupportedException("Vehicle not supported");
}
}
我目前遇到的问题是调用这个工厂时如下:
var factory = new VehicleServiceFactory();
Vehicle vehicle = GetVehicle();
var vehicleService = factory.GetVehicleService(vehicle); // this returns null!
vehicleService.ServiceVehicle(vehicle);
GetVehicleService
returns null
,我猜是因为我将基类型 Vehicle
传递给此方法,所以 T
会求值到 Vehicle
并且无法从 CarService
(实现 IVehicleService<Car>
)转换为有效的 return 类型,即 IVehicleService<Vehicle>
(请纠正我如果我错了)。
希望得到有关如何解决此问题的指导。
我会在 Factory class 中使用类似下面的东西:
public T GetVehicle<T>(T it) {
try {
Type type = it.GetType(); // read incoming Vehicle type
ConstructorInfo ctor = type.GetConstructor(new[] { type }); // get its constructor
object instance = ctor.Invoke(new object[] { it }); // invoke its constructor
return (T)instance; // return proper vehicle type
} catch { return default(T); }
}
对于工厂实现,您可以使用 MEF
这将允许您使用具有唯一名称的 Export 和 Import 属性来实现,并且您不需要 if else / 语句来创建工厂。
class Program
{
private static CompositionContainer _container;
public Program()
{
var aggList = AppDomain.CurrentDomain
.GetAssemblies()
.Select(asm => new AssemblyCatalog(asm))
.Cast<ComposablePartCatalog>()
.ToArray();
var catalog = new AggregateCatalog(aggList);
_container = new CompositionContainer(catalog);
_container.ComposeParts(this);
}
static void Main(string[] args)
{
var prg = new Program();
var car = _container.GetExportedValue<IVehicle>("CAR") as Car;
var carService = _container.GetExportedValue<IVehicleService<Car>>("CARSERVICE") as CarService;
carService.ServiceVehicle(car);
var truck = _container.GetExportedValue<IVehicle>("TRUCK") as Truck;
var truckService = _container.GetExportedValue<IVehicleService<Truck>>("TRUCKSERVICE") as TruckService;
truckService.ServiceVehicle(truck);
Console.ReadLine();
}
}
public interface IVehicleService<in T>
{
void ServiceVehicle(T vehicle);
}
public interface IVehicle
{
}
[Export("CARSERVICE", typeof(IVehicleService<Car>)), PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public class CarService : IVehicleService<Car>
{
public void ServiceVehicle(Car vehicle)
{
}
}
[Export("TRUCKSERVICE", typeof(IVehicleService<Truck>)), PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public class TruckService : IVehicleService<Truck>
{
public void ServiceVehicle(Truck vehicle)
{
}
}
public abstract class Vehicle : IVehicle
{
}
[Export("CAR", typeof(IVehicle)), PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public class Car : Vehicle
{
}
[Export("TRUCK", typeof(IVehicle)), PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public class Truck : Vehicle
{
}
有一种方法可以避免在 IVehicleService
上使用泛型并避免将 Truck 传递到 CarService 的问题,反之亦然。您可以先将 IVehicleService
更改为非通用的或通过以下车辆:
public interface IVehicleService
{
void ServiceVehicle();
}
相反,我们将车辆传递给 CarService/TruckService 的构造函数:
public class CarService : IVehicleService
{
private readonly Car _car;
public CarService(Car car)
{
_car = car;
}
public void ServiceVehicle()
{
Console.WriteLine($"Service Car {_car.Id}");
}
}
并让工厂通过车辆:
public class VehicleServiceFactory
{
public IVehicleService GetVehicleService(Vehicle vehicle)
{
if (vehicle is Car)
{
return new CarService((Car)vehicle);
}
if (vehicle is Truck)
{
return new TruckService((Truck)vehicle);
}
throw new NotSupportedException("Vehicle not supported");
}
}
这就是我要实现的方式
public static void Main(string[] args)
{
var factory = new VehicleServiceFactory();
Vehicle vehicle = GetVehicle();
var vehicleService = factory.GetVehicleService(vehicle);
vehicleService.ServiceVehicle();
Console.ReadLine();
}
public static Vehicle GetVehicle()
{
return new Truck() {Id=1};
//return new Car() { Id = 2 }; ;
}
public interface IVehicleService
{
void ServiceVehicle();
}
public class CarService : IVehicleService
{
private readonly Car _car;
public CarService(Car car)
{
_car = car;
}
public void ServiceVehicle()
{
Console.WriteLine($"Service Car {_car.Id}");
}
}
public class TruckService : IVehicleService
{
private readonly Truck _truck;
public TruckService(Truck truck)
{
_truck = truck;
}
public void ServiceVehicle()
{
Console.WriteLine($"Service Truck {_truck.Id}");
}
}
public class VehicleServiceFactory
{
public IVehicleService GetVehicleService(Vehicle vehicle)
{
if (vehicle is Car)
{
return new CarService((Car)vehicle);
}
if (vehicle is Truck)
{
return new TruckService((Truck)vehicle);
}
throw new NotSupportedException("Vehicle not supported");
}
}
public abstract class Vehicle
{
public int Id;
}
public class Car : Vehicle
{
}
public class Truck : Vehicle
{
}
您要求编译时类型安全。但是您使用的代码在编译时类型未知。在这个例子中....
var factory = new VehicleServiceFactory();
Vehicle vehicle = GetVehicle(); //Could return any kind of vehicle
var vehicleService = factory.GetVehicleService(vehicle);
vehicleService.ServiceVehicle(vehicle);
...编译代码时根本不知道 vehicle
的类型。
即使你能成功,你也不能对 returned 的 class 做任何事情,因为同样,你在编译时不知道类型:
CarService s = new CarSevice();
Vehicle v = new Car();
s.ServiceVehicle(v); //Compilation error
如果要编译时检查,需要在编译时声明类型。所以只需将其更改为:
var factory = new VehicleServiceFactory();
Car vehicle = GetCar(); //<-- specific type
var vehicleService = factory.GetVehicleService(vehicle);
vehicleService.ServiceVehicle(vehicle);
或者如果你坚持用Vehicle
类型的变量持有车辆,你可以使用
var factory = new VehicleServiceFactory();
Vehicle vehicle = GetCar();
var vehicleService = factory.GetVehicleService<Car>(vehicle); //Explicit type
vehicleService.ServiceVehicle(vehicle);
并且工厂会return相应的服务class。
要么,要么坚持运行时检查,这在您的第一个示例中实现。
问题
您遇到的问题与 C# 推导的泛型类型有关。
Vehicle vehicle = GetVehicle();
这一行会给你带来麻烦,因为你传入的 vehicle
变量类型
var vehicleService = factory.GetVehicleService(vehicle); // this returns null!
是 Vehicle
类型, 不是 类型 Car
(或 Truck
)。因此,您的工厂方法 GetVehicleService<T>
推导出的类型 (T) 是 Vehicle
。但是,在您的 GetVehicleService 方法中,如果给定的类型无法按照您的意愿进行转换,您将进行安全转换 (as
) returns null
。
如果改成直接投
return (IVehicleService<T>) new CarService();
您会看到,调试器将在该行捕获 InvalidCastException。这是因为您的 CarService
实现了 IVehicleService<Car>
但程序实际上试图将其强制转换为 IVehicleService<Vehicle>
而您的 CarService
未实现,因此抛出异常。
如果您完全删除演员表
return new CarService();
你甚至会在编译时得到一个错误,告诉你这些类型不能相互转换。
一个解决方案
遗憾的是,我不知道可以由 C# 处理的简洁解决方案。但是,您可以为您的服务创建一个抽象基础 class,实现一个非通用接口:
public interface IVehicleService
{
void ServiceVehicle(Vehicle vehicle);
}
public abstract class VehicleService<T> : IVehicleService where T : Vehicle
{
public void ServiceVehicle(Vehicle vehicle)
{
if (vehicle is T actual)
ServiceVehicle(actual);
else
throw new InvalidEnumArgumentException("Wrong type");
}
public abstract void ServiceVehicle(T vehicle);
}
public class CarService : VehicleService<Car>
{
public override void ServiceVehicle(Car vehicle)
{
Console.WriteLine("Service Car");
}
}
public class TruckService : VehicleService<Truck>
{
public override void ServiceVehicle(Truck vehicle)
{
Console.WriteLine("Service Truck");
}
}
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");
}
}
如您所见,工厂现在是非通用的,接口也是如此(就像您之前拥有的一样)。然而,服务的抽象基 class 现在可以处理类型并在类型不匹配时抛出异常(不幸的是仅在运行时)。
一个(可能)有用的补充
如果您的工厂有很多不同的类型并且您想要保存几十个 if
语句,您可以使用属性做一些变通。
首先,创建一个ServiceAttribute class:
[AttributeUsage(AttributeTargets.Class)]
public class ServiceAttribute : Attribute
{
public Type Service { get; }
public ServiceAttribute(Type service)
{
Service = service;
}
}
然后将此属性附加到您的车辆 classes:
[Service(typeof(TruckService))]
public class Truck : Vehicle
// ...
并像这样更改您的工厂:
public class VehicleServiceFactory
{
public IVehicleService GetVehicleService(Vehicle vehicle)
{
var attributes = vehicle.GetType().GetCustomAttributes(typeof(ServiceAttribute), false);
if (attributes.Length == 0)
throw new NotSupportedException("Vehicle not supported");
return (IVehicleService) Activator.CreateInstance(((ServiceAttribute)attributes[0]).Service);
}
}
此方法不使用反射,因此与 if 语句相比应该不会那么慢。
我已经实施了一个车辆服务,负责为汽车和卡车等车辆提供服务:
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
方法。它在理想情况下应该接受 Car
时接受 Vehicle
,因为它知道它只会维修汽车。所以我决定更新此实现以改用泛型:
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
}
}
public class VehicleServiceFactory
{
public IVehicleService<T> GetVehicleService<T>(T vehicle) where T : Vehicle
{
if (vehicle is Car)
{
return new CarService() as IVehicleService<T>;
}
if (vehicle is Truck)
{
return new TruckService() as IVehicleService<T>;
}
throw new NotSupportedException("Vehicle not supported");
}
}
我目前遇到的问题是调用这个工厂时如下:
var factory = new VehicleServiceFactory();
Vehicle vehicle = GetVehicle();
var vehicleService = factory.GetVehicleService(vehicle); // this returns null!
vehicleService.ServiceVehicle(vehicle);
GetVehicleService
returns null
,我猜是因为我将基类型 Vehicle
传递给此方法,所以 T
会求值到 Vehicle
并且无法从 CarService
(实现 IVehicleService<Car>
)转换为有效的 return 类型,即 IVehicleService<Vehicle>
(请纠正我如果我错了)。
希望得到有关如何解决此问题的指导。
我会在 Factory class 中使用类似下面的东西:
public T GetVehicle<T>(T it) {
try {
Type type = it.GetType(); // read incoming Vehicle type
ConstructorInfo ctor = type.GetConstructor(new[] { type }); // get its constructor
object instance = ctor.Invoke(new object[] { it }); // invoke its constructor
return (T)instance; // return proper vehicle type
} catch { return default(T); }
}
对于工厂实现,您可以使用 MEF
这将允许您使用具有唯一名称的 Export 和 Import 属性来实现,并且您不需要 if else / 语句来创建工厂。
class Program
{
private static CompositionContainer _container;
public Program()
{
var aggList = AppDomain.CurrentDomain
.GetAssemblies()
.Select(asm => new AssemblyCatalog(asm))
.Cast<ComposablePartCatalog>()
.ToArray();
var catalog = new AggregateCatalog(aggList);
_container = new CompositionContainer(catalog);
_container.ComposeParts(this);
}
static void Main(string[] args)
{
var prg = new Program();
var car = _container.GetExportedValue<IVehicle>("CAR") as Car;
var carService = _container.GetExportedValue<IVehicleService<Car>>("CARSERVICE") as CarService;
carService.ServiceVehicle(car);
var truck = _container.GetExportedValue<IVehicle>("TRUCK") as Truck;
var truckService = _container.GetExportedValue<IVehicleService<Truck>>("TRUCKSERVICE") as TruckService;
truckService.ServiceVehicle(truck);
Console.ReadLine();
}
}
public interface IVehicleService<in T>
{
void ServiceVehicle(T vehicle);
}
public interface IVehicle
{
}
[Export("CARSERVICE", typeof(IVehicleService<Car>)), PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public class CarService : IVehicleService<Car>
{
public void ServiceVehicle(Car vehicle)
{
}
}
[Export("TRUCKSERVICE", typeof(IVehicleService<Truck>)), PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public class TruckService : IVehicleService<Truck>
{
public void ServiceVehicle(Truck vehicle)
{
}
}
public abstract class Vehicle : IVehicle
{
}
[Export("CAR", typeof(IVehicle)), PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public class Car : Vehicle
{
}
[Export("TRUCK", typeof(IVehicle)), PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public class Truck : Vehicle
{
}
有一种方法可以避免在 IVehicleService
上使用泛型并避免将 Truck 传递到 CarService 的问题,反之亦然。您可以先将 IVehicleService
更改为非通用的或通过以下车辆:
public interface IVehicleService
{
void ServiceVehicle();
}
相反,我们将车辆传递给 CarService/TruckService 的构造函数:
public class CarService : IVehicleService
{
private readonly Car _car;
public CarService(Car car)
{
_car = car;
}
public void ServiceVehicle()
{
Console.WriteLine($"Service Car {_car.Id}");
}
}
并让工厂通过车辆:
public class VehicleServiceFactory
{
public IVehicleService GetVehicleService(Vehicle vehicle)
{
if (vehicle is Car)
{
return new CarService((Car)vehicle);
}
if (vehicle is Truck)
{
return new TruckService((Truck)vehicle);
}
throw new NotSupportedException("Vehicle not supported");
}
}
这就是我要实现的方式
public static void Main(string[] args)
{
var factory = new VehicleServiceFactory();
Vehicle vehicle = GetVehicle();
var vehicleService = factory.GetVehicleService(vehicle);
vehicleService.ServiceVehicle();
Console.ReadLine();
}
public static Vehicle GetVehicle()
{
return new Truck() {Id=1};
//return new Car() { Id = 2 }; ;
}
public interface IVehicleService
{
void ServiceVehicle();
}
public class CarService : IVehicleService
{
private readonly Car _car;
public CarService(Car car)
{
_car = car;
}
public void ServiceVehicle()
{
Console.WriteLine($"Service Car {_car.Id}");
}
}
public class TruckService : IVehicleService
{
private readonly Truck _truck;
public TruckService(Truck truck)
{
_truck = truck;
}
public void ServiceVehicle()
{
Console.WriteLine($"Service Truck {_truck.Id}");
}
}
public class VehicleServiceFactory
{
public IVehicleService GetVehicleService(Vehicle vehicle)
{
if (vehicle is Car)
{
return new CarService((Car)vehicle);
}
if (vehicle is Truck)
{
return new TruckService((Truck)vehicle);
}
throw new NotSupportedException("Vehicle not supported");
}
}
public abstract class Vehicle
{
public int Id;
}
public class Car : Vehicle
{
}
public class Truck : Vehicle
{
}
您要求编译时类型安全。但是您使用的代码在编译时类型未知。在这个例子中....
var factory = new VehicleServiceFactory();
Vehicle vehicle = GetVehicle(); //Could return any kind of vehicle
var vehicleService = factory.GetVehicleService(vehicle);
vehicleService.ServiceVehicle(vehicle);
...编译代码时根本不知道 vehicle
的类型。
即使你能成功,你也不能对 returned 的 class 做任何事情,因为同样,你在编译时不知道类型:
CarService s = new CarSevice();
Vehicle v = new Car();
s.ServiceVehicle(v); //Compilation error
如果要编译时检查,需要在编译时声明类型。所以只需将其更改为:
var factory = new VehicleServiceFactory();
Car vehicle = GetCar(); //<-- specific type
var vehicleService = factory.GetVehicleService(vehicle);
vehicleService.ServiceVehicle(vehicle);
或者如果你坚持用Vehicle
类型的变量持有车辆,你可以使用
var factory = new VehicleServiceFactory();
Vehicle vehicle = GetCar();
var vehicleService = factory.GetVehicleService<Car>(vehicle); //Explicit type
vehicleService.ServiceVehicle(vehicle);
并且工厂会return相应的服务class。
要么,要么坚持运行时检查,这在您的第一个示例中实现。
问题
您遇到的问题与 C# 推导的泛型类型有关。
Vehicle vehicle = GetVehicle();
这一行会给你带来麻烦,因为你传入的 vehicle
变量类型
var vehicleService = factory.GetVehicleService(vehicle); // this returns null!
是 Vehicle
类型, 不是 类型 Car
(或 Truck
)。因此,您的工厂方法 GetVehicleService<T>
推导出的类型 (T) 是 Vehicle
。但是,在您的 GetVehicleService 方法中,如果给定的类型无法按照您的意愿进行转换,您将进行安全转换 (as
) returns null
。
如果改成直接投
return (IVehicleService<T>) new CarService();
您会看到,调试器将在该行捕获 InvalidCastException。这是因为您的 CarService
实现了 IVehicleService<Car>
但程序实际上试图将其强制转换为 IVehicleService<Vehicle>
而您的 CarService
未实现,因此抛出异常。
如果您完全删除演员表
return new CarService();
你甚至会在编译时得到一个错误,告诉你这些类型不能相互转换。
一个解决方案
遗憾的是,我不知道可以由 C# 处理的简洁解决方案。但是,您可以为您的服务创建一个抽象基础 class,实现一个非通用接口:
public interface IVehicleService
{
void ServiceVehicle(Vehicle vehicle);
}
public abstract class VehicleService<T> : IVehicleService where T : Vehicle
{
public void ServiceVehicle(Vehicle vehicle)
{
if (vehicle is T actual)
ServiceVehicle(actual);
else
throw new InvalidEnumArgumentException("Wrong type");
}
public abstract void ServiceVehicle(T vehicle);
}
public class CarService : VehicleService<Car>
{
public override void ServiceVehicle(Car vehicle)
{
Console.WriteLine("Service Car");
}
}
public class TruckService : VehicleService<Truck>
{
public override void ServiceVehicle(Truck vehicle)
{
Console.WriteLine("Service Truck");
}
}
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");
}
}
如您所见,工厂现在是非通用的,接口也是如此(就像您之前拥有的一样)。然而,服务的抽象基 class 现在可以处理类型并在类型不匹配时抛出异常(不幸的是仅在运行时)。
一个(可能)有用的补充
如果您的工厂有很多不同的类型并且您想要保存几十个 if
语句,您可以使用属性做一些变通。
首先,创建一个ServiceAttribute class:
[AttributeUsage(AttributeTargets.Class)]
public class ServiceAttribute : Attribute
{
public Type Service { get; }
public ServiceAttribute(Type service)
{
Service = service;
}
}
然后将此属性附加到您的车辆 classes:
[Service(typeof(TruckService))]
public class Truck : Vehicle
// ...
并像这样更改您的工厂:
public class VehicleServiceFactory
{
public IVehicleService GetVehicleService(Vehicle vehicle)
{
var attributes = vehicle.GetType().GetCustomAttributes(typeof(ServiceAttribute), false);
if (attributes.Length == 0)
throw new NotSupportedException("Vehicle not supported");
return (IVehicleService) Activator.CreateInstance(((ServiceAttribute)attributes[0]).Service);
}
}
此方法不使用反射,因此与 if 语句相比应该不会那么慢。