systembankスタッフのブログ

システムバンクスタッフのブログです。

インターフェイス その1

 インターフェイスってそもそも

マイクロソフトC#プログラミングガイドに書いてある定義を読むと何故かよくわからない気持ちになります。

クラスまたは構造体で実装できる関連機能のグループの定義が含まれますって書いてあるんですがちょっと何言ってるかわからないのが正直なところ。(機械翻訳じゃなかったら訳した方、上から目線な物言い済みません)

原文を読んでみると、「別々のクラスや構造体にある関連する機能をまとめてグループにしたもの、このグループをインタフェースっていうんですよ」って感じだろうか、これでもよくわかんないか。

同じように機能をまとめるC#のプログラミング機構に抽象クラスってのもありますが、どうやって使い分ければよいのか、話してみようと思います。

インターフェイスの書き方

    interface IReader
    {
        string Execute();
    }

 クラスの代わりにinterfaceと書いて頭にIつけて宣言する。これでインタフェースになります。インターフェイス内のメソッド、プロパティは全てpublicなのでpubilcとかprivateとかは書かないです。インターフェイス自身を外部に公開する場合はpublicをつけて宣言します。

使い方の簡単な実装例

これをどうやって使うかというと

    class DbReader : IReader
    {
        public string Execute()
        {
            var factory = DbProviderFactories.GetFactory("Npgsql");
            using (DbConnection connection = factory.CreateConnection())
            {
                connection.ConnectionString 
                    = Properties.Settings.Default.ConnectionString;
                connection.Open();

                using (DbCommand command = connection.CreateCommand())
                {
                    command.CommandText = "SELECT STRINGVALUE FROM TABLE1";
                    using (DbDataReader dataReader = command.ExecuteReader())
                    {
                        if (!dataReader.HasRows) return string.Empty;
                        
                        string returnValue = string.Empty;
                        while(dataReader.Read())
                        {
                            returnValue 
                                += string.Format("{0}\r\n", dataReader["STRINGVALUE"]);
                        }
                        return returnValue;
                    }
                }
            }
        }
    }

冗長な書き方になってしまいましたが、IReaderのExecuteメソッド実装したDbReaderクラスです。インターフェイスを利用するクラスが1つだけだと前段で話した「別々のクラス云々」ではないのでもう一つクラスを実装します

    class FileReader : IReader
    {
        public string Execute()
        {
            using (StreamReader streamReader 
                = new StreamReader(Properties.Settings.Default.FileName))
            {
                string returnValue = string.Empty;

                while(!streamReader.EndOfStream)
                {
                    returnValue += string.Format("{0}\r\n", streamReader.ReadLine());
                }

                return returnValue;
            }
        }
    }

IReaderインターフェイスを持つDbReaderクラス、FileReaderクラスを作成しました。 2つのクラスはIReaderインターフェイスを持っていること以外は関連性がありません。 これが別々のクラスにある関連する機能をグループにまとめる行為です。 IReaderを使う側が存在しないと何がどうインターフェイスなのかわからないのでその部分も実装します。

    static class ReaderFactory
    {
        public static IReader CreateReader(string readerName)
        {
            switch (readerName)
            {
                case "DbReader": return new DbReader();
                case "FileReader": return new FileReader();
                default: throw new InvalidOperationException("readerNameが無効です。");
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length > 0)
            {
                IReader reader = ReaderFactory.CreateReader(args[0]);
                Console.WriteLine(reader.Execute());
            }
            else
            {
                Console.WriteLine("Readerをコマンドライン引数に書いてください");
            }

        }
    }    

IReaderインターフェイスの何れかを生成するファクトリクラスを作って、IReaderインターフェイスを使うプログラム側ではファクトリクラスを使ってIReaderを使う形にするのが常套です。

こうすることでインターフェイスを使う側のプログラムはどんな具体的なクラスが実体化したかを意識する必要がなくなります、言い方をかえると同じインターフェイスを共有してればどんなクラスであっても使う側のプログラムは影響を受けないことになります、クラス自体が何かは問うことはありません。

次回

今回はここまでにします。

この例だと抽象クラス、具体クラスを用いて同じことを実装できるのですがなぜ抽象クラスではなくインターフェイスなのかを次回の書こうと思います。