インタフェースの代わりにデリゲートをつかう

C# 3.0デリゲートラムダ式を使った、テンプレートメソッドパターンっぽいコードを書いてみました。
※中の処理には意味はありませんが…

エントリポイント

Program.cs
namespace HogeApp
{
    class Program
    {
        public static void Main(string[] args)
        {
            // 処理実行
            Facade.DoWork();
        }
    }
}

ファサードクラス と クロージャのファクトリクラス

Facade.cs
PostSelectFactory.cs
SelectFactory.cs
PostSelectFactory.cs
using System;
using System.Collections.Generic;

namespace HogeApp
{
    public static class Facade
    {
        public static void DoWork()
        {
            // サービスオブジェクト
            IService service = ServiceFactory();

            // データ用のオブジェクト変数
            IList<IMember> list = ListFactory();

            /////////////////////////////////////////////////////////
            // 振る舞いの設定を行います

            // 前段処理の設定
            service.PreSelect = PreSelectFactory.GetPreSelect(list);

            // メイン処理の設定
            service.Select = SelectFactory.GetSelect(list);

            // 後段処理の設定
            service.PostSelect = PostSelectFactory.GetPostSelect(list);

            // 出力処理の設定
            service.Outputer = OutputerFactory.GetOutputer(list);

            /////////////////////////////////////////////////////////
            // 処理実行

            Console.WriteLine("-- program start --");

            // サービスオブジェクト実行
            service.Execute();

            Console.WriteLine("-- program end --");

            // コンソール一時停止
            Console.ReadLine();
        }

        // サービスオブジェクト生成
        private static IService ServiceFactory()
        {
            IService service = SkeletonService.GetInstance();
            service.PersonsFactory = () => PersonsFactory.GetPersons();
            return service;
        }

        // データ用のリストオブジェクト作成
        private static IList<IMember> ListFactory()
        {
            return new List<IMember>();
        }
    }
}

using System;
using System.Collections.Generic;

namespace HogeApp
{
    public static class PreSelectFactory
    {
        public static Func<IPerson, IList<IMember>> GetPreSelect(IList<IMember> list)
        {
            return (person) =>
            {
                Console.WriteLine("-- is called PreSelect.");

                // 年齢30才以上をデータリストに追加
                if (person.Age > 30)
                {
                    list.Add(new Member(new Person(person.Name, person.Age), "over 30"));
                }
                return list;
            };
        }
    }
}

using System;
using System.Collections.Generic;

namespace HogeApp
{
    public static class SelectFactory
    {
        public static Func<IPerson, IList<IMember>> GetSelect(IList<IMember> list)
        {
            return (person) =>
            {
                Console.WriteLine("-- is called Select.");

                // 名前にaが含まれる人をデータリストに追加
                if (person.Name.IndexOf("a") != -1)
                {
                    list.Add(new Member(new Person(person.Name, person.Age), "include a"));
                }
                return list;
            };
        }
    }
}

using System;
using System.Collections.Generic;

namespace HogeApp
{
    public static class PostSelectFactory
    {
        public static Func<IPerson, IList<IMember>> GetPostSelect(IList<IMember> list)
        {
            return (person) =>
            {
                Console.WriteLine("-- is called PostSelect.");

                // 名前にyが含まれる人をデータリストに追加
                if (person.Name.IndexOf("y") != -1)
                {
                    list.Add(new Member(new Person(person.Name, person.Age), "include y"));
                }
                return list;
            };
        }
    }
}

サービスオブジェクト すべて抽象に対してプログラミングされます

IService.cs
SkeletonService.cs
using System;
using System.Collections.Generic;

namespace HogeApp
{
    public interface IService
    {
        Func<IList<IPerson>> PersonsFactory { set; }

        Action Outputer { set; }

        Func<IPerson, IList<IMember>> PreSelect { set; }

        Func<IPerson, IList<IMember>> Select { set; }

        Func<IPerson, IList<IMember>> PostSelect { set; }

        void Execute();
    }
}

using System;
using System.Collections.Generic;

namespace HogeApp
{
    public class SkeletonService : IService
    {
        // フィールド (※自分のインスタンス変数以外は全て抽象)

        private static SkeletonService instance;

        private Func<IList<IPerson>> personsFactory;

        private Action outputer;

        private Func<IPerson, IList<IMember>> preSelect;

        private Func<IPerson, IList<IMember>> select;

        private Func<IPerson, IList<IMember>> postSelect;

        // プロパティ (※外部から具体的な振る舞いを設定してもらう)

        public Func<IList<IPerson>> PersonsFactory
        {
            set { this.personsFactory = value; }
        }

        public Action Outputer
        {
            set { this.outputer = value; }
        }

        public Func<IPerson, IList<IMember>> PreSelect
        {
            set { this.preSelect = value; }
        }

        public Func<IPerson, IList<IMember>> Select
        {
            set { this.select = value; }
        }

        public Func<IPerson, IList<IMember>> PostSelect
        {
            set { this.postSelect = value; }
        }

        // コンストラクタは封印
        private SkeletonService() { }

        // スタティックファクトリメソッド
        public static SkeletonService GetInstance()
        {
            if (instance == null)
            {
                instance = new SkeletonService();

                // デリゲートの初期化
                instance.PersonsFactory = () => null;
                instance.Outputer = () => { };
                instance.PreSelect = (person) => null;
                instance.Select = (person) => null;
                instance.PostSelect = (person) => null;
            }
            return instance;
        }

        // 唯一の実行メソッド
        public void Execute()
        {
            Console.WriteLine("-- is called SkeletonService#Execute.");

            // オブジェクトを生成する振る舞いの実行
            IList<IPerson> persons = personsFactory();

            // リスト用のオブジェクト変数
            IList<IMember> list = null;

            // 振る舞い毎に個別のループで処理を実行する
            // リストが返される部分とか気持ち悪い…
            // 抽象に対して処理の構造を決めている
            foreach (IPerson person in persons)
            {
                list = preSelect(person);
            }
            foreach (IPerson person in persons)
            {
                list = select(person);
            }
            foreach (IPerson person in persons)
            {
                list = postSelect(person);
            }

            Console.WriteLine("リストカウントは : " + list.Count);

            // 出力という振る舞いのみが操作出来ればいい
            outputer();
        }
    }
}

エンティティっぽいの

IPerson.cs
Person.cs
using System;

namespace HogeApp
{
    public interface IPerson
    {
        String Name { get; }

        int Age { get; }
    }
}

using System;

namespace HogeApp
{
    public class Person : IPerson
    {
        String name;

        int age;

        public Person(String name, int age)
        {
            this.name = name;
            this.age = age;
        }

        public String Name
        {
            get { return name; }
        }

        public int Age
        {
            get { return age; }
        }
    }
}

DTOっぽいの

IMember.cs
Member.cs
using System;

namespace HogeApp
{
    public interface IMember
    {
        String Text { get; }

        String Name { get; }
    }
}

using System;

namespace HogeApp
{
    public class Member : IMember
    {
        String text;

        IPerson person;

        public Member(IPerson person, String text)
        {
            this.person = person;
            this.text = text;
        }

        public String Text
        {
            get { return text; }
        }

        public String Name
        {
            get { return person.Name; }
        }
    }
}

関数オブジェクトとか

PersonsFactory.cs
MembersOutputer.cs
using System.Collections.Generic;

namespace HogeApp
{
    public static class PersonsFactory
    {
        public static IList<IPerson> GetPersons()
        {
            return new List<IPerson> {
                new Person("hoge", 28),
                new Person("piyo", 32),
                new Person("yazz", 46)
            };
        }
    }
}

using System;
using System.Collections.Generic;

namespace HogeApp
{
    public static class MembersOutputer
    {
        public static void Execute(IList<IMember> list)
        {
            Console.WriteLine("選択された人達");
            foreach (Member m in list)
            {
                Console.WriteLine(m.Name + " : なぜ? " + m.Text);
            }
        }
    }
}

思ったこと

  • デリゲートによって、メソッドという概念が完全に吹っ飛んでしまった!
  • 状態と振る舞いを一緒に扱うのがオブジェクト指向ならこのスタイルは何だろう?
  • 振る舞いがカプセル化されているというのが、気持ち悪くて面白い。
  • デリゲート型の変数にクロージャを返すファクトリクラスを作成しました。
  • 状態振る舞い抽象APIを介してバラバラに分割したスタイルです。