0

我目前有一个继承结构,其中方法的返回类型保留为开放泛型,以便每个类都可以从另一个结构的相应级别返回一个对象。例如,假设我们有一个 VehicleFactory,其中 TVehicle:Vehicle 可以 ProduceVehicle TVehicle。我也有 CarFactory:VehicleFactory 其中 TVehicle:Car。

Car 继承自 Vehicle,因此所有这些都是有效的,并且让我知道我的 CarFactory 必须生产 Cars,而我的 Vehicle 工厂可以生产任何类型的车辆。我遇到的问题是,我需要一种方法在 Ford 运行时将 VehicleFactory 实例化为 CarFactory,而在 Wave Runner 运行时将其实例化为 BoatFactory。

我想我可以通过创建一个与 VehicleFactory 的功能相匹配的接口并编写一个返回 IVehicleFactory 的 MakeFactory 方法来做到这一点(它将非通用返回车辆)。由于 CarFactory 返回作为车辆的汽车,它实现了接口,并且在世界上一切都应该是正确的。出乎意料的问题是,尽管 TVehicle 必须是 Vehicle,但 VehicleFactory 无法满足作为 TVehicle 是 Vehicle 而关闭的接口。

有谁知道这是为什么,或者是否有其他方法可以解决这个限制?如果无法直接绕过此限制,是否有任何替代方法可以让一组共享功能确保它始终被实例化为两组或更多组更具体的类之一。(其中 Vehicle 是共享层,Car 和 Boat 是上下文特定层。)

class Vehicle
{
}

class Car : Vehicle
{
}

interface IVehicleFactory
{
     Vehicle ProduceVehicle();
}

class VehicleFactory<TVehicle> : IVehicleFactory
    where TVehicle:Vehicle
{
    public virtual TVehicle ProduceVehicle()
    {
    }
}

class CarFactory<TVehicle> : VehicleFactory<TVehicle>
    where TVehicle : Car
{
    public override TVehicle ProduceVehicle()
    {
    }
}

以及使用它的代码

static IVehicleFactory CreateVehicleFactory()
{
    if(somecondition)
    {
         Return new CarFactory<Car>();
    }
    else
    {
         Return new BoatFactory<Boat>();
    }
}

添加更多细节以澄清问题。

术语工厂的使用并不意味着工厂模式。它实际上是一个检索“车辆”的存储库。车辆类型是具有公共共享基本代码的库的应用程序特定版本。每个应用程序 Car 和 Boat 的存储库对于相同的检索可能具有不同的逻辑,并且可能依赖于其对象的 Car 或 Boat 变体中的字段。我需要一种方法来构建一个工厂,它可以查看我所在的应用程序的上下文并返回适当的存储库(BoatFactory 或 CarFactory),以便车辆级别的共享代码将正确使用任何应用程序特定的覆盖功能。

4

4 回答 4

2

希望这有帮助:

interface IVehicleFactory<out T> where T : Vehicle
{
    T Create();
}

class CarFactory : IVehicleFactory<Car>
{
    public Car Create()
    {
        return new Car();
    }
}

class VehicleFactoryProvider
{
    public IVehicleFactory<T> GetFactoryFor<T>() where T : Vehicle
    {
        throw new NotImplementedException();
    }
}

class Program
{
    public static void Main(string[] args)
    {
        VehicleFactoryProvider provider = new VehicleFactoryProvider();
        var factory = provider.GetFactoryFor<Car>();
        var car = factory.Create();
    }
}
于 2012-11-05T18:47:23.367 回答
1

你的接口VehicleFactory<TVehicle>不满足。IVehicleFactory为了满足它,它必须有一个带有签名的方法Vehicle ProduceVehicle()。您显然没有具有该签名的方法(但返回派生类型),因此它不起作用。

处理此问题的最简单方法是显式实现接口并返回调用其他方法的结果。界面将得到满足,您仍在生产正确类型的车辆。这类似于您在实现IEnumerable<T>接口时通常会做的事情(这需要实现IEnumerable接口)。

class VehicleFactory<TVehicle> : IVehicleFactory
    where TVehicle:Vehicle
{
    public virtual TVehicle ProduceVehicle()
    {
        throw new NotImplementedException();
    }

    // explicitly implement the interface by
    // returning the value of our actual method
    Vehicle IVehicleFactory.ProduceVehicle()
    {
        return ProduceVehicle();
    }
}
于 2012-11-06T16:27:18.117 回答
0

我在想,这里可能需要对架构进行一次小的重构;从语义上讲,您似乎只需要:

public class VehicleFactory
{
    protected Dictionary<Type, ISpecificTransportFactory> _mapping = // initialize

    public Vehicle CreateVehicle<TVehicle>() // any params
    {
        if(_mapping.ContainsKey(typeof(TVehicle))
          return _mapping[typeof(TVehicle)].CreateVehicle(); // any params that need to be passed

        throw new ConfigurationInvalidException("No transportFactory defined for : " + typeof(TVehicle).Name);
    }
}

然后加上一点:

public interface ISpecificTransportFactory
{
     Vehicle CreateVehicle();
}

public abstract class FactoryBase<TVehicleType> : ISpecificTransportFactory
    where TVehicleType : Vehicle
{
    public Vehicle CreateVehicle()
    {
        return CreateInstance();
    }

    public abstract TVehicleType CreateInstance();
}

public class CarFactory : FactoryBase<Car>
{
     public override Car CreateInstance()
     {
          return new Car();
     }
}

从理论上讲,您可以按以下方式使用它:

public static void Main(string[] args)
{
     var vehicleFactory = new VehicleFactory();

     var vehicle = vehicleFactory.CreateVehicle<Car>();
}
于 2012-11-05T19:02:30.863 回答
0

好吧,虽然我还没有解决方案来让它工作,(至少不是完全),但我想我现在明白了这个问题。我最初的假设之一似乎是不正确的。我的印象是,接口上不太具体的返回类型可以由更具体的类型来实现。即,将 SomeBaseClass 定义为方法的返回类型的接口不能由实现返回 SomeChildClass 的方法的类实现,其中 SomeChildClass 继承自 SomeBaseClass。

协变泛型的使用(通过 out 关键字)确实提供了某种方式来规避这种情况,方法是允许对接口进行泛型定义,但是就我的目的而言,当我遇到需要基于 IList 的结果时(这需要一个不变的类型参数)。

最终,似乎可以做的最好的事情是我可以做到这一点,以便 CarFactory 只知道它正在与车辆一起工作(通过删除 TVehicle 通用参数上的额外 where 条件),需要在 CarFactory 中进行强制转换,但是当曾经 CarCompany 实例化 CarFactory,它仍然可以使其成为 CarFactory,并且客户端代码本身不必担心强制转换。

这并不理想,但我认为这可能是最好的,因为 C# 缺乏协变返回类型。

interface IVehicleFactory<TVehicle> where TVehicle:Vehicle
{
    TVehicle SomeMethod();
}

VehicleFactory<TVehicle> : IVehilceFactory<TVehicle> where TVehicle:Vehicle
{
    TVehicle SomeMethod()
    {
    }
}

CarFactory<TVehicle> : IVehicleFactory<TVehicle> where TVehicle:Vehicle
{
    TVehicle SomeMethod()
    {
       ~~Always returns car and uses cast from Vehicle when necessary.
    }
}

class VehicleFactory<TVehicle>
{
   static IVehicleFactory<TVehicle> CreateContextSpecificFactory()
   {
       if(someCondition)
       {
            return new CarFactory<TVehicle>;
       }
   }
}

class CarCompany
{
    CarFactory<Car> carFactory = new CarFactory<Car>;
    Car car = carFactory.SomeMethod();
}

class VehicleCompany
{
    VehicleFactory<Vehicle> vehicleFactory = VehicleFactory<Vehicle>.CreateContextSpecificFactory();
}
于 2012-11-06T15:24:45.573 回答