web-dev-qa-db-ja.com

スイッチまたはIf / Thenのないファクトリパターン

ファクトリクラスを実装する方法の簡単な例を探していますが、SwitchまたはIf-Thenステートメントを使用しないなし。私が見つけることができるすべての例は1つを使用します。たとえば、実際のファクトリがスイッチに依存しないように、この簡単な例(下記)をどのように変更できますか?この例は、オープン/クローズの原則に違反しているようです。ファクトリークラスを変更せずに具象クラス(「マネージャー」、「事務員」、「プログラマー」など)を追加できるようにしたいと思います。

ありがとう!

class Program
{
    abstract class Position
    {
        public abstract string Title { get; }
    }

    class Manager : Position
    {
        public override string Title
        {
            get  { return "Manager"; }
        }
    }

    class Clerk : Position
    {
        public override string Title
        {
            get { return "Clerk"; }
        }
    }

    class Programmer : Position
    {
        public override string Title
        {
            get { return "Programmer"; }
        }
    }

    static class Factory
    {
        public static Position Get(int id)
        {
            switch (id)
            {
                case 0: return new Manager();
                case 1: return new Clerk();
                case 2: return new Programmer();
                default: return new Programmer();
            }
        }
    }

    static void Main(string[] args)
    {
        for (int i = 0; i <= 2; i++)
        {
            var position = Factory.Get(i);
            Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
        }
        Console.ReadLine();
    }
}

更新:

うわー!みんな、ありがとう!私はトンを学びました。すべてのフィードバックを受け取った後、私はいくつかの答えをブレンドしてこれを思いつきました。私はこれを行うためのより良い方法についてのさらなる対話を受け入れるでしょう。

class Program
{

    public interface IPosition
    {
        string Title { get; }
    }

    class Manager : IPosition
    {
        public string Title
        {
            get { return "Manager"; }
        }
    }

    class Clerk : IPosition
    {
        public string Title
        {
            get { return "Clerk"; }
        }
    }

    class Programmer : IPosition
    {
        public string Title
        {
            get { return "Programmer"; }
        }
    }

static class PositionFactory
{
    public static T Create<T>() where T : IPosition, new()
    {
        return new T();
    }
}


static void Main(string[] args)
    {

        IPosition position0 = PositionFactory.Create<Manager>();
        Console.WriteLine("0: " + position0.Title);

        IPosition position1 = PositionFactory.Create<Clerk>();
        Console.WriteLine("1: " + position1.Title);

        IPosition position2 = PositionFactory.Create<Programmer>();
        Console.WriteLine("1: " + position2.Title);

        Console.ReadLine();
    }
}

別の編集:

不明なタイプを使用して、インターフェイスのインスタンスを作成することもできます。

static class PositionFactory
{
   public static IPosition Create(string positionName)
    {       
        Type type = Type.GetType(positionName);
        return (IPosition)Activator.CreateInstance(type);
    }
}

その後、次のように呼び出すことができます。

IPosition position = PositionFactory.Create("Manager");
Console.WriteLine(position.Title);
21
Casey Crookston

これはどうですか(辞書は必要ありません。Create<Position>()を実行しようとすると構文エラーが発生することに注意してください):

[〜#〜] edit [〜#〜]-明示的に実装されたIPositionインターフェイスを使用するように更新されました。 IPositionのインスタンスのみがメンバー関数にアクセスできます(例:<implementation of Manager>.Titleはコンパイルされません)。

EDIT#2 Factory.Createは、インターフェイスを適切に使用すると、TではなくIPositionを返す必要があります。

using System;
using System.Collections.Generic;

class Program
{
    interface IPosition
    {
        string Title { get; }
        bool RequestVacation();
    }

    class Manager : IPosition
    {
         string IPosition.Title
        {
            get { return "Manager"; }
        }

        bool IPosition.RequestVacation()
        {
            return true;
        }
    }

    class Clerk : IPosition
    {
        int m_VacationDaysRemaining = 1;

        string IPosition.Title
        {
            get { return "Clerk"; }
        }

        bool IPosition.RequestVacation()
        {
            if (m_VacationDaysRemaining <= 0)
            {
                return false;
            }
            else
            {
                m_VacationDaysRemaining--;
                return true;
            }
        }
    }

    class Programmer : IPosition
    {
        string IPosition.Title
        {
            get { return "Programmer"; }
        }

        bool IPosition.RequestVacation()
        {
            return false;
        }
    }

    static class Factory
    {
        public static IPosition Create<T>() where T : IPosition, new ()
        {
            return new T();
        }
    }

    static void Main(string[] args)
    {
        List<IPosition> positions = new List<IPosition>(3);
        positions.Add(Factory.Create<Manager>());
        positions.Add(Factory.Create<Clerk>());
        positions.Add(Factory.Create<Programmer>());

        foreach (IPosition p in positions) { Console.WriteLine(p.Title);  }
        Console.WriteLine();

        Random rnd = new Random(0);
        for (int i = 0; i < 10; i++)
        {
            int index = rnd.Next(3);
            Console.WriteLine("Title: {0}, Request Granted: {1}", positions[index].Title, positions[index].RequestVacation());
        }

        Console.ReadLine();
    }
}
10
Clay Ver Valen

カスタム属性とリフレクションを利用できます。

[PositionType(1)]
class Manager : Position
{
    public override string Title
    {
        get
        { return "Manager"; }
    }
}

[PositionType(2)]
class Clerk : Position
{
    public override string Title
    {
        get
        { return "Clerk"; }
    }
}

次に、ファクトリでPositionから継承するすべてのクラスを取得し、PositionType属性が正しい値を持つクラスを見つけることができます。

static class Factory
{
    public static Position Get(int id)
    {
        var types = typeof(Position).Assembly.GetTypes()
            .Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(Position)))
            .ToList();

        Position position = null;
        foreach(var type in types)
        {
           type.GetCustomAttributes<PositionTypeAttribute>();

           if(type.PositionId == id)
           {
               position = Activator.CreateInstance(type) as Position;
               break;
           }
        }

        if(position == null)
        {
            var message = $"Could not find a Position to create for id {id}.";
            throw new NotSupportedException(message);
        }

        return position;
    }
}
6
Craig W.
public class PositionFactory
{
    private Dictionary<int, Type> _positions;

    public PositionFactory()
    {
        _positions = new Dictionary<int, Type>();
    }

    public void RegisterPosition<PositionType>(int id) where PositionType : Position
    {
        _positions.Add(id, typeof(PositionType));
    }

    public Position Get(int id)
    {
        return (Position) Activator.CreateInstance(_positions[id]);
    }
}

このように使用されます:

            var factory = new PositionFactory();
            factory.RegisterPosition<Manager>(0);
            factory.RegisterPosition<Clerk>(1);

            Position p = factory.Get(0); //Returns a new Manager instance
2
Bradley Uffner

なぜ複雑すぎるのですか?簡単な解決策の1つを次に示します。

using System;
using System.Collections.Generic;

class Program
{
    interface IPosition
    {
        string Title { get; }
    }

    class Manager : IPosition
    {
        public string Title
        {
            get { return "Manager"; }
        }
    }

    class Clerk : IPosition
    {
        public string Title
        {
            get { return "Clerk"; }
        }
    }

    class Programmer : IPosition
    {
        public string Title
        {
            get { return "Programmer"; }
        }
    }

    class Factory
    {
        private List<IPosition> positions = new List<IPosition>();
        public Factory()
        {
            positions.Add(new Manager());
            positions.Add(new Clerk());
            positions.Add(new Programmer());
            positions.Add(new Programmer());
        }

        public IPosition GetPositions(int id)
        {
            return positions[id];
        }
    }

    static void Main(string[] args)
    {
        Factory factory = new Factory();

        for (int i = 0; i <= 2; i++)
        {
            var position = factory.GetPositions(i);
            Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
        }
        Console.ReadLine();
    }
}

これは、ファクトリクラスをまったく使用せずにこれを行う方法です。

using System;
using System.Collections.Generic;

class Program
{
    interface IPosition
    {
        string Title { get; }
    }

    class Manager : IPosition
    {
        public string Title
        {
            get { return "Manager"; }
        }
    }

    class Clerk : IPosition
    {
        public string Title
        {
            get { return "Clerk"; }
        }
    }

    class Programmer : IPosition
    {
        public string Title
        {
            get { return "Programmer"; }
        }
    }

    static void Main(string[] args)
    {
        List<IPosition> positions = new List<IPosition> { new Manager(), new Clerk(), new Programmer(), new Programmer() };

        for (int i = 0; i <= 2; i++)
        {
            var position = positions[i];
            Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
        }
        Console.ReadLine();
    }
}
1
Tanuki