はじめに
こんちわす。はじめまして、Souと申します。
遅かれながら初めて記事を書くわけですが、悩んだ挙句、自分がPGする上で一番とっつきにくかった
interfaceについて書こうと思います。そもそも明確な利点がわからなかった経験があり、同じ悩みを抱えたエンジニアはいると思います。。
では実際にコードを見ながらアプローチしていきましょう。言語はc#で書きますが、スキル的に読むのは難しくないと思いますので一緒に頑張りましょう!
利点 其の① メソッド名を強要出来る
よく聞くやつですね。最初は「それだけ??」と思うかもしれません。僕も最初はそうでした。
具体的には、**"あらゆる変更に強くなる"**という風に置き換えれます。
例えば商品情報をデータベースから取得するという想定で考えましょう。
商品を識別できるユニークな値「id」を引数にし、それを条件に商品情報を取得するといったモノを想定してください。
インターフェース
public interface ISearchItemQuary
{
Item Execute(int id);
}
具象クラス
public class SearchItemQuaryFromDataBase : ISearchItemQuary
{
public Item Execute(int id)
{
"データベースから商品情報取得処理;"
}
}
実行クラス※使用するフレームワークであったりアーキテクチャによって実行クラスの名前は様々だと思うので今回は「Main」クラスにさせてください。
public class Main
{
private ISearchItemQuary _searchItemQuary;
public Main (ISearchItemQuary searchItemQuary)
{
_searchItemQuary= searchItemQuary;
}
//実行処理
static void Main(string[] args)
{
//商品IDが1の商品情報を取得
_searchItemQuary.Execute(1);
}
}
とします。
だが、ここでパフォーマンスなどの問題があってデータベースからではなくキャッシュサーバーから取得するように変更が必要になりました。
リファクタリングを行う上で大事なのは既存の処理を出来るだけ変えないことです。変更に強くなくてはなりません。
では、さっそくキャッシュから取得するクラスを作りましょう。
public class SearchItemQuaryFromCache : ISearchItemQuary
{
public Item Execute(int id)
{
"キャッシュサーバーから商品情報取得処理;"
}
}
SearchItemQuaryFromDataBaseクラスと同じインターフェースISearchItemQuaryを使用します。
これによりExecuteメソッドを強要できます。
この時点でDBとCacheで同じメソッド名を持っていることを念頭に置いてください。
これでリファクタリングは終わりです。同じインターフェースを噛ませているので、実行クラスで変える箇所はありません。
※Mainメソッドのコンストラクタの具象クラス(DBかCache)はDIコンテナで結びつけてると想定してください。
簡単に言うとインターフェースと具象クラスをアプリ起動時紐付けれるやつ。初学者の方はあまり気にしなくていいです。
利点 其の② インスタンスを柔軟に選択できる
もう既に①の例で証明できてるのですが、もう一度確認してみましょう。
public Main (ISearchItemQuary searchItemQuary)
{
_searchItemQuary= searchItemQuary;
}
①でDBとCacheの2つのクラスを作成しましたが、前回も述べたようにクラスが増えたからといってMainクラスは何の変更もしていません。
理由はコンストラクタの引数がインターフェースで定義しているためです。
ISearchItemQuary を使用しているクラスであればこのMainクラスは誰でも使用することが出来ます。
まさに柔軟性が垣間見えた瞬間です!
利点 其の③ テストの見通しをよく出来る
TDD(テスト駆動開発)を行う上で、interfaceを作成してテストを実施してから、具象クラスを作成するのが定石だと思います。
モックを使用した例で見てみましょう。
↓先程のinterfaceです。
public interface ISearchItemQuary
{
Item Execute(int id);
}
↓テストクラス
private readonly int _id = 1
public Item TestShouldGetItem()
{
//ISearchItemQuaryのモックを作成
var mockContext = new Mock<ISearchItemQuary>();
mockContext.Setup(m => m.Execute(_id)).Returns("idに紐づく商品情報");
var item = mockContext.Object;
//id「1」に紐づいた商品情報を取得
return item.Execute(_id)
}
この時、ISearchItemQuaryの具象クラスは未作成だと考えてください。DBにもキャッシュサーバーにも接続する処理がないという状況です。
モックの概念は個別で調べてください。まあ簡単に言うとDBなどがなくても値をあたかも取得したかのように振舞えます。
このようにinterfaceを使用することで実際の処理がなくてもモックに振舞いを行わさせることで、テストを容易にし、品質を担保できるのです。
まとめ
まだまだ、factory method、SOLID原則など
参考になるモノはあるので、初学者の方はこの辺からアプローチしてみてはいかがでしょうか?