实现通用工厂方法

时间:2017-10-30 17:12:12

标签: c# generics interface 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方法。它接受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 
    }
}

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返回null,我猜是因为我将基本类型Vehicle传入此方法,因此T将评估为Vehicle并且无法从CarService(实现IVehicleService<Car>)转换为有效的返回类型IVehicleService<Vehicle>(如果我错了,请纠正我)。

会对如何解决这个问题提供一些指导。

5 个答案:

答案 0 :(得分:2)

您要求编译时类型安全。但是您正在使用编译时未知类型的代码。在这个例子....

var controller = new MyWebApiController();
controller.InvokeAction("/myAction?param1=2");

...编译代码时,var factory = new VehicleServiceFactory(); Vehicle vehicle = GetVehicle(); //Could return any kind of vehicle var vehicleService = factory.GetVehicleService(vehicle); vehicleService.ServiceVehicle(vehicle); 的类型根本就不知道。

即使您可以将其删除,也无法对返回的类执行任何操作,因为在编译时您不知道类型:

vehicle

如果要进行编译时检查,则需要在编译时声明类型。所以只需将其更改为:

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

工厂将返回相应的服务类。

要么是这样,要么坚持运行时检查,这是在你的第一个例子中实现的。

答案 1 :(得分:2)

问题

你遇到的问题与泛型C#deduces有关。

Vehicle vehicle = GetVehicle();

此行会导致您遇到麻烦,因为您传递的vehicle变量类型

var vehicleService = factory.GetVehicleService(vehicle);  // this returns null!

类型为Vehicle不是,类型为Car(或Truck)。因此,工厂方法GetVehicleService<T>推导出的类型(T)为Vehicle。但是,在GetVehicleService方法中,如果无法按照您的意愿投射给定类型,则执行安全转换(as)并返回null。 如果你把它改成直接演员

return (IVehicleService<T>) new CarService();

您将看到,调试器将在此行捕获InvalidCastException。这是因为您的CarService实现了IVehicleService<Car>,但该程序实际上会尝试将其强制转换为IVehicleService<Vehicle>,而CarService并未执行此操作,因此会抛出异常。

如果你完全删除演员阵容

return new CarService();

你甚至会在编译时遇到错误,告诉你这些类型不能互相强制转换。

解决方案

不幸的是,我不知道C#可以处理的整洁解决方案。但是,您可以为服务创建抽象基类,实现非泛型接口:

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

正如您所看到的,工厂现在是非通用的,以及界面(就像之前一样)。但是,如果类型不匹配,服务的abstratc基类现在可以处理类型并抛出异常(不幸的是仅在运行时)。

A(可能)有用的补充

如果您的工厂有很多不同类型,并且您希望保存数十个if语句,则可以使用属性进行一些解决方法。

首先,创建一个ServiceAttribute类:

[AttributeUsage(AttributeTargets.Class)]
public class ServiceAttribute : Attribute
{
    public Type Service { get; }

    public ServiceAttribute(Type service)
    {
        Service = service;
    }
}

然后将此属性附加到您的车辆类:

[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语句相比不应该那么慢。

答案 2 :(得分:1)

我在Factory类中使用如下内容:

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

答案 3 :(得分:1)

有一种方法可以避免在IVehicleService上使用泛型并避免将卡车传递到CarService的问题,反之亦然。您可以先将IVehicleService更改为不通用或通过vechicle:

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
    {

    }

答案 4 :(得分:0)

对于工厂实施,您可以使用MEF

这将允许您使用具有唯一名称的Export和Import属性实现,并且您不需要if else / witch语句来创建工厂。

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
{

}