0

私は現在、各クラスが別の構造の対応するレベルからオブジェクトを返すことができるように、メソッドの戻り値の型がオープンジェネリックとして残されている継承構造を持っています。たとえば、ProduceVehicle TVehicle ができる TVehicle:Vehicle の VehicleFactory があるとします。TVehicle:Car の CarFactory:VehicleFactory もあります。

Car は Vehicle から継承するため、これはすべて有効であり、CarFactory は Car を生成する必要があり、Vehicle ファクトリはあらゆる種類の車両を生成できることを知ることができます。私が遭遇した問題は、VehicleFactory が Ford によって実行されているときは CarFactory としてインスタンス化する方法が必要なことですが、Wave Runner によって実行されているときは BoatFactory としてインスタンス化する方法が必要です。

これは、VehicleFactory の機能に一致するインターフェイスを作成し、IVehicleFactory を返す MakeFactory メソッドを作成することで実現できると考えました (非ジェネリックに車両を返します)。CarFactory は車両である車を返すため、インターフェイスを満たし、すべてが正しいはずです。予期しない問題は、TVehicle が Vehicle でなければならないという事実にもかかわらず、VehicleFactory が TVehicle が Vehicle であるとして閉じられているインターフェイスに適合しないことです。

これがなぜなのか、またはこの制限を回避する別の方法があるのか​​ 誰かが知っていますか? この制限を直接回避する方法がない場合、機能の共有セットを使用して、より具体的なクラスの 2 つ以上のセットの 1 つとして常にインスタンス化されるようにする代替方法はありますか。(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

まあ、それを機能させる方法の解決策はまだありませんが(少なくとも完全ではありません)、問題を理解したと思います。私の最初の仮定の 1 つが間違っていたようです。私は、インターフェイスのあまり具体的でない戻り値の型は、より具体的な型で満たすことができるという印象を受けました。つまり、メソッドの戻り値の型として SomeBaseClass を定義するインターフェイスは、SomeChildClass が SomeBaseClass から継承する SomeChildClass を返すメソッドを実装するクラスによって実装できません。

共変ジェネリックを ( 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 に答える