LoginSignup
79

More than 1 year has passed since last update.

インタフェースの命名パターンから知るインタフェースの役割

Last updated at Posted at 2018-05-20
1 / 19

はじめに

C#における(≒.NETにおける)インタフェースの命名パターンを個人的に整理するとともに、それぞれのインタフェースの役割と存在意義を考えてみます。


標準ライブラリのインタフェース


パターン1 : I + 動詞-able

単一のメソッドを定義するインタフェースはこのように命名されることが多いです。
『〇〇できる』の意味のインタフェースですので、メソッドもそれに応じたものを持ちます。

    • System.IDisposable
    • System.IComparable<T>
    • System.Collections.Generic.IEnumerable<T>

サブバターン1-1

動詞と同名のメソッドを1つだけ持つパターンです。
個人的な見解ですが、このパターンがインタフェースの基礎である、と考えるとインタフェースそのものの理解がしやすいと思います。

// 『破棄できる』インタフェース
public interface IDisposable {
    // 《自分自身を》破棄する
    void Dispose();
}
// 『比較できる』インタフェース
public interface IComparable<in T> {
    // 《自分自身を》T型の他のオブジェクトと『比較する』
    int CompareTo(T other);
}

サブバターン1-2

別のオブジェクトを取得するメソッドを1つだけ持つパターンです。
取得したオブジェクトは後述のサブパターン2-2のインタフェースを持ちます。
動詞で表される操作が複雑な場合に利用されます。

// 『列挙できる』インタフェース
public interface IEnumerable<out T> {
    // 『《自分自身を》「列挙する人」を取得する』
    IEnumerator<T> GetEnumerator();
}
// ややこしいので非ジェネリクスのIEnumerableの継承は省略

パターン2 : I + 動詞-or/動詞-er

『〇〇する者』を意味するインタフェースです。
サブパターンによって性質が少し異なります。

    • System.ICustomFormatter
    • System.Collections.Generic.IEnumerator<T>

サブバターン2-1

動詞と同名のメソッドを1つだけ持つパターンです。
サブパターン1-1に似ていますが、動詞の対象が別オブジェクトであることが大きく違います。

// 『フォーマットする人』インタフェース
public interface ICustomFormatter {
    // 受け取った引数を元に『フォーマットする』
    String Format (String format, Object arg, IFormatProvider formatProvider);
}

サブバターン2-2

メソッドが複数含まれているパターンです。
複数のメソッドを組み合わせて、1つの機能を実現します。
操作対象から予め操作を任されている、とも取れます。

// 『列挙する人』インタフェース
public interface IEnumerator<out T> : IDisposable {
    // 《列挙する対象を》次の要素へ移動する
    bool MoveNext();
    // 《列挙する対象を》現在の要素を取得する
    T Current { get; }
    // 《列挙する対象を》初期状態に戻す
    void Reset();
}
// ややこしいので非ジェネリクスのIEnumeratorの継承は省略

パターン3 : I + 名詞

名詞に対応する標準的なクラスの操作を定義したものです。
このパターンのインタフェースは良く設計されていると強力です。

    • System.Collections.Generic.IList<>
    • System.Collections.Generic.ICollection<>
// 『コレクション』を表すインタフェース
public interface ICollection<T> : IEnumerable<T> {
    // 《自分自身の》持つ要素数
    int Count { get; }
    // 《自分自身が》読み取り専用か否か 
    bool IsReadOnly { get; }
    // 《自分自身に》T型の新しい要素を追加する
    void Add(T item);
    // 《自分自身の》要素を空にする
    void Clear();
    // 《自分自身に》対象の要素が含まれているか判断する
    bool Contains(T item); 
    // 《自分自身の》arrayIndex番目の要素以降をarrayにコピーする                
    void CopyTo(T[] array, int arrayIndex);
    // 《自分自身から》対象の要素を削除する 
    bool Remove(T item);
}
// 『リスト』を表すインタフェース
// 『リスト』は『コレクション』に加えて以下のことが可能
public interface IList<T> : ICollection<T> {
    // 《自分自身の》index番目の要素を取得、設定する
    T this[int index] { get; set; }
    // 《自分自身の》何番目に対象の要素があるか判断する
    int IndexOf(T item);
    // 《自分自身の》index番目に対象の要素を挿入する
    void Insert(int index, T item);
    // 《自分自身の》index番目の要素を削除する
    void RemoveAt(int index);
}

ちなみに、LinkedListICollection<T> は実装するけれど、 IList<T>は実装していません。このあたりも掘り下げてみると面白そうですね。


自作する場合

さて、各パターンを使った自作インタフェースの例を挙げることで、想像の幅を広げてみましょう。


1-1

サブパターン1-1 はシンプルですね。この例のほかにも、動物を例にしたIFlyable(飛べる)インタフェースなんかも例えに向いています。

// 『印刷できる』
interface IPrintable {
    // 《自分自身を》印刷する
    void Print();
}

// 例えば、印刷できるドキュメント型
class Document : IPrintable { /*...*/ }

2-1

サブパターン2-1 もシンプルです。メソッドが引数を持つのが特徴ですね。

// 『カッター(切るもの)』
interface ICutter {
    // 《他のものを》切る
    Paper[] Cut(Paper paper);
}

// 例えば、他の物を切ることができるハサミ型
class Scissor : ICutter { /*...*/ }

1-2 と 2-2

サブパターン1-2,2-2 はイメージを膨らませるために、合わせて作ってみました。
「テレビ」と「リモコン」みたいなイメージが個人的にはしっくりきます。
一つのテレビに対して複数のリモコンを作れるのは、少し感覚が違うかもしれませんね。より良い例えを思いついたら更新します(^^;

// 『リモコンで操作できる』
interface IRemoteControllable {
    // 《自分自身を》操作するためのリモコンを取得する
    IRemoteController GetController();
}
// 『リモコン(遠隔操作するもの)』
interface IRemoteController {
    // 『《遠隔操作する対象の》電源を入切する』
    void Power(bool turningOn);
    // 『《遠隔操作する対象の》チャネルを変更する』
    void ChangeChannel(int channel);
}

// リモコン操作可能なテレビ型
class Television : IRemoteControllable {

    bool _powerOn;
    int _channel;

    // 自分自身を対象とするリモコンを返却
    public IRemoteController GetController() {
        return new RemoteController(this);
    }

    // リモコン型
    class RemoteController : IRemoteController {

        Television _television;

        // コンストラクタで対象のテレビを受け取る
        public RemoteController(Television television) {
            _television = television;
        }

        public void ChangeChannel(int channel) {
            _television._channel = channel;
        }

        public void Power(bool turningOn) {
            _television._powerOn = turningOn;
        }
    }
}

3

パターン3 はメソッドが多いパターンで、教科書的な説明ではこのパターン3が使われることも多いです《IList<int> list = new List<int>();のようなパターン》
一方で、このパターンで説明すると、インタフェースの意義は分かりにくかったりします。

もしあなたがオブジェクト指向言語の教育係になったなら、このパターンではなく、サブパターン1-1のようなインタフェースを例として挙げることをお勧めします。

ちょっと否定的な書き方をしてしまいましたが、それでもDIなどで出番が多いのがこのパターンです。上手く付き合ってインタフェースのスペシャリストになりましょう。

以下、良い例とは言い難いのですが、例を示しておきます。

// 『車』
interface ICar {
    // 『《自分自身の》エンジンを開始する』
    void StartEngine();
    // 『《自分自身を》加速する』
    void Accel();
    // 『《自分自身を》減速する』
    void Break();
}

// 様々な車
class Car : ICar { /* ・・・*/ }
class Bus : ICar { /* ・・・*/ }

まとめ

No パターン 特徴 Method
1-1 -able 動詞と同名のメソッド
1-2 -able 複雑な操作をする別オブジェクト
(パターン2-2)を生成する
2-1 -or / -er 他のオブジェクトを操作するメソッド
2-2 -or / -er 状態を管理するメソッド
3 名詞 一揃いのメソッド群

上記以外の例もあるとは思いますが、大雑把な分類としては有効でしょう。


今回まとめられてないパターン

今回は省略したパターン
今後何かで記事を書くかもしれません…

  • System.ComponentModel.INotifyPropertyChanged
    • Notify なので、動詞句型の命名
    • PropertyChanged というeventを持つ

おわりに

命名パターンごとに、少しずつ性格が違うインタフェースであることが分かります。標準のクラスライブラリを学習する場合にも、命名に着目することで理解がより容易になると思います。
また、自分でインタフェースを設計するときには、命名を考えることで設計の手助けになるはずです。


  • 偉そうに書いていますが、色々と突っ込みを頂けると嬉しいです!
  • 誤字脱字から高度な内容まで、なにかあればコメントお願いします。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
79