読者です 読者をやめる 読者になる 読者になる

No more Death March

あるSEのチラシの裏 C# WPF

インターフェースを考える(12)ActionとActor

IActionインターフェースについて思いついたことがあるので書いてみます。

まずIActionインターフェース

namespace Nmdm.Actions
{
    public interface IAction<TContext>
    {
        void Do(TContext context);
    }
}

機能面だけで見ると一つの引数を受け取って戻り値の無いメソッドを実行するインターフェースですが。
これを少し見方を変えて「TContextが行う動作」とすると、以下の拡張メソッドでIActionをTContextのインスタンスメソッドのように呼び出せる。

namespace Nmdm.Actions
{
    public static class IActionExtension
    {
        public static void Do<T>(this T actor, IAction<T> action)
        {
            action.Do(actor);
        }
    }
}

これのクライアントコードの例は以下の通り

            // IActionのパラメータになるクラス
            var actor = new object();

            // IActionのNullオブジェクト
            var action = new NullAction<object>();

            // IActionにパラメータを渡して実行する場合。
            action.Do(actor);

            // actorのインスタンスメソッドに見せかけて実行する場合。
            actor.Do(action);

メリット(?)を考える。

・あたかもactorが主体となって処理を実行するので視覚的にオブジェクト指向チックに見える。
・actorの振る舞いを別クラスで実装出来るのでactor自身の肥大化が防げる。
(ただし、actorの状態自体を書き換えるのであればactor自身にSetterのようなものが必要)
・IAction自体は組み換えしやすい。特に複雑な動作が求められる場合に有利?

うーん・・・処理とふるまいが一体化するっていうところからかなり逸脱してるけども・・・
とにかくクラスを細分化するっていうところだけ突き詰めていくとこういう書き方もありなのか・・・?

これをもう少し手を入れて「流暢なインターフェース」にするのであれば、
拡張メソッドにactor自身を返すメソッドDoFluentを追加する。

namespace Nmdm.Actions
{
    public static class IActionExtension
    {
        public static void Do<T>(this T actor, IAction<T> action)
        {
            action.Do(actor);
        }

        public static T DoFluent<T>(this T actor, IAction<T> action)
        {
            actor.Do(action);
            return actor;
        }
    }
}

このDoFluentの呼び出し例は以下の通り。

            // 流暢な記述
            actor
                .DoFluent(action)
                .DoFluent(action)
                .DoFluent(action);