0

クラスライブラリの設計と、ライブラリを使用する実行可能プログラムで構成される以下のサンプルコードについて考えてみます。

namespace AppLib
{
    /// <summary>
    /// Entry point for library. Stage manages all the actors in the logic.
    /// </summary>
    class StageApp
    {
        /// <summary>
        /// Setting that is looked up by different actors
        /// </summary>
        public int SharedSetting { get; set; }

        /// <summary>
        /// Stage managing actors with app logic
        /// </summary>
        public IEnumerable<Actor> Actors { get { return m_actors.Where(x => x.Execute() > 40).ToArray(); } }

        private List<Actor> m_actors = new List<Actor>();
    }

    /// <summary>
    /// An object on the stage. Refers to stage (shared)settings and execute depending on the settings.
    /// Hence actor should have reference to stage
    /// </summary>
    class Actor
    {
        private StageApp m_StageApp;

        private int m_Property;

        /// <summary>
        /// An actor that needs to refer to stage to know what behavior to execute
        /// </summary>
        /// <param name="stage"></param>
        public Actor(StageApp stage)
        {
            m_StageApp = stage;
            m_Property = new Random().Next();
        }

        /// <summary>
        /// Execute according to stage settings
        /// </summary>
        /// <returns></returns>
        public int Execute()
        {
            return m_StageApp.SharedSetting * m_Property;
        }
    }
}

namespace AppExe
{
    using AppLib;

    class Program
    {
        static void Main(string[] args)
        {
            StageApp app = new StageApp();
            app.SharedSetting = 5;

            // Question: How to add actor to stage?

            foreach (var actor in app.Actors)
                Console.WriteLine(actor.Execute());
        }
    }
}

質問

Stage循環依存がありActor、私には悪いようです。たとえば、俳優をステージに追加するにはどうすればよいですか?

Actor()ユーザーに自分で新しいものを作成させる場合、ユーザーはを提供し続ける必要がありStageます。

Actor()内部コンストラクターを指定してファクトリを作成するStageと、ユーザーが継承されたを作成するための柔軟性が失われますActor

シングルトンを作成Stageする場合、1セットしか持てませんSharedSettingStageユーザーが自分の中に複数を必要とする場合AppExe、それを行うことはできません。

上記の問題を回避するためにアーキテクチャを再設計する方法はありますか?

4

3 に答える 3

1

アクター間でStageApp設定を共有することによって機能が制限されない場合は、他のロジックも使用されます。たとえば、Actorから親StageAppを知る必要がある場合、またはその逆の場合です。私はそれをこのように実装することを好みます:

namespace AppLib
{
    /// <summary> 
    /// Entry point for library. Stage manages all the actors in the logic. 
    /// </summary> 
    class StageApp
    {
        /// <summary> 
        /// Setting that is looked up by different actors 
        /// </summary> 
        public int SharedSetting { get; set; }

        /// <summary> 
        /// Stage managing actors with app logic 
        /// </summary> 
        public IEnumerable<Actor> Actors { get { return m_actors.Where(x => x.Execute() > 40).ToArray(); } }

        private List<Actor> m_actors = new List<Actor>();

        public int TotalActorsCount
        {
            get
            {
                return m_actors.Count;
            }
        }

        public void AddActor(Actor actor)
        {
            if (actor == null)
                throw new ArgumentNullException("actor");

            if (m_actors.Contains(actor))
                return; // or throw an exception

            m_actors.Add(actor);
            if (actor.Stage != this)
            {
                actor.Stage = this;
            }
        }

        // we are hiding this method, to avoid because we can change Stage only to another non null value
        // so calling this method directly is not allowed
        internal void RemoveActor(Actor actor)
        {
            if (actor == null)
                throw new ArgumentNullException("actor");

            if (!m_actors.Contains(actor))
                return; // or throuw exception

            m_actors.Remove(actor);
        }
    }

    /// <summary> 
    /// An object on the stage. Refers to stage (shared)settings and execute depending on the settings. 
    /// Hence actor should have reference to stage 
    /// </summary> 
    class Actor
    {
        private StageApp m_StageApp;

        private int m_Property;

        public StageApp Stage
        {
            get
            {
                return m_StageApp;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                if (m_StageApp != value)
                {
                    if (m_StageApp != null) // not a call from ctor
                    {
                        m_StageApp.RemoveActor(this);
                    }
                    m_StageApp = value;
                    m_StageApp.AddActor(this);
                }
            }
        }

        /// <summary> 
        /// An actor that needs to refer to stage to know what behavior to execute 
        /// </summary> 
        /// <param name="stage"></param> 
        public Actor(StageApp stage)
        {
            Stage = stage;
            m_Property = new Random().Next();
        }

        /// <summary> 
        /// Execute according to stage settings 
        /// </summary> 
        /// <returns></returns> 
        public int Execute()
        {
            return m_StageApp.SharedSetting * m_Property;
        }
    }
}

namespace AppExe
{
    using AppLib;

    class Program
    {
        static void Main(string[] args)
        {
            StageApp app = new StageApp();
            app.SharedSetting = 5;

            StageApp anotherApp = new StageApp();
            anotherApp.SharedSetting = 6;

            // actor is added to the stage automatically after instantiation
            Actor a1 = new Actor(app);
            Actor a2 = new Actor(app);
            Actor a3 = new Actor(anotherApp);

            Console.WriteLine("Actors in anotherApp before moving actor:");
            Console.WriteLine(anotherApp.TotalActorsCount);

            // or by calling method from StageApp class
            anotherApp.AddActor(a1);

            Console.WriteLine("Actors in anotherApp after calling method (should be 2):");
            Console.WriteLine(anotherApp.TotalActorsCount);

            // or by setting Stage through property
            a2.Stage = anotherApp;

            Console.WriteLine("Actors in anotherApp after setting property of Actor instance (should be 3):");
            Console.WriteLine(anotherApp.TotalActorsCount);

            Console.WriteLine("Actors count in app (should be empty):");
            Console.WriteLine(app.TotalActorsCount);
        }
    }
}

オブジェクトの関係を透過的に操作できますが、実装するには少しmorコードが必要です。

于 2012-08-22T16:04:09.443 に答える
0

各ステージでのアクターの動作を定義する新しいクラス「ActorRole」を追加してみませんか。ActorとStageを相互に分離できるため、両方を個別に(たとえば、ファクトリを介して)インスタンス化し、それらを組み合わせて、ステージを構成するActorRoleオブジェクトを作成できます。この組み合わせは、必要に応じてBuilderパターンを使用して行うことができます。

アクターの動作を動的に変更する必要がある場合は、ActorRoleクラスに基づくストラテジーパターンを使用できるため、ステージに応じて、アクターにその動作のさまざまな具体的な実装を割り当てることができます。

于 2012-08-22T14:04:12.210 に答える
0

Funcに渡す代わりにを使用して解決しStageますActor。このような:

namespace AppExe
{
    using AppLib;
class Program
    {
        static void Main(string[] args)
        {
            StageApp app = new StageApp();
            app.CreateActor();
            app.SharedSetting = 5;
            foreach (var actor in app.Actors)
                Console.WriteLine(actor.Execute());
        }
    }
}

namespace AppLib
{
    class StageApp
    {
        public int SharedSetting { get; set; }
        public IEnumerable<Actor> Actors { get { return m_actors.Where(x => x.Execute() > 40).ToArray(); } }
        private List<Actor> m_actors = new List<Actor>();
        public void CreateActor()
        {
            m_actors.Add(new Actor(Executed));
        }
        private int Executed(int arg)
        {
            return SharedSetting * arg;
        }
    }

    class Actor
    {
        private int m_Property;
        private Func<int, int> m_executed;

        public Actor(Func<int, int> executed)
        {
            m_executed = executed;
            m_Property = new Random().Next();
        }

        public int Execute()
        {
            return m_executed(m_Property);
        }
    }
}

循環参照は面白くないということに完全に同意します:)。

イベントを使用してこれを解決することもできますが、コールバックなどの関数を渡すのが好きです。

于 2012-08-22T14:34:12.303 に答える