それはすべての抽象化についての赤ちゃん


アブストラクトイラストArt by Lønfeldt on Unsplash
私は、10年ほど専門的な私の人生の開発者でした.その10年で、私はいつも「仕事を得る」ことができたと感じます、しかし、一旦私が抽象化を受け入れるならば、ものは本当に私のためにクリックし始めました.

抽象化とは何か
からWikipedia :

Abstraction, in general, is a fundamental concept to computer science and software development. The process of abstraction can also be referred to as modeling and is closely related to the concepts of theory and design. Models can also be considered types of abstractions per their generalization of aspects of reality.


うーん、それは本当に私のために多くをクリアしなかった.ラベルは私にとって難しいです、しかし、私は「何が起こる必要があるか」を定義することによって概念モデリングとして抽象化を考えます.
抽象化は概念ではなく、詳細である.人々は抽象的にどのように動作するかについての詳細を理解することなく、毎日抽象化を使用します.これの簡単な例は、あなたの車のガスペダルの抽象的な考えかもしれません.あなたはどのようにエンジンの仕事の内部を知らないかもしれませんが、あなたはそのペダルの背後にあるアイデアは“車を作るVルームvroom”を知っている.抽象化の消費者にとって、抽象化がどのように意図しているかを達成する方法の詳細は、重要ではない.

なぜ抽象化は重要か?
抽象化は、組織の手段であり、組織はクリーン、簡単に従うコードが必要です!抽象化を達成する伝統的な方法は、以下のように利用します.
  • インターフェース
  • 抽象的なクラス
  • 単純なモデルのクラス(理想的に振る舞いのないプロパティ)
  • オブジェクト
  • 振舞いのあるクラス(デザインの観点からではないが、少なくとも抽象的な「良い」型)
  • はい、上記のリストはかなりほとんどの言語機能を含んでいますが、弾丸のポイントのいくつかは、高レベル抽象化の観点から他のものよりも役に立つことができます.
    私は、インターフェイスとモデルのクラスは、概念をモデリングするための最も便利なツールとして、彼らはあなたを許可し、さらに小さなチャンクでの問題について考えることを強制して下さい.インターフェースはすべて、以前に述べたようにメソッドシグネチャを定義することです — “何”“を達成するために必要な”方法ではない.
    例を挙げる
  • 私はいくつかの基準に基づいてMyObjectのシリーズが必要です.
  • 抽象化のレベルでは、次のようなクラスを作成できます.
    public class MyObjectDbRetriever
    {
     public IEnumerable<MyObject> GetMyObjects(MyObjectCriteria criteria)
     {
      // Connect to a database
      // Do some sql-ey stuff that limits the result set based on criteria
    
      return results;
     }
    }
    
    上記のものはちょうどうまくいきます、そして、我々の「終わり結果」の一部で終わることになりますが、それは呼び出し側の展望からの最大のコードでありません.
    どのように/呼び出し元はこのコードを消費できますか?
    public class MyObjectController
    {
     public IEnumerable<MyObject> GetMyObjects()
     {
      // make up some criteria
      var myObjectCriteria = new MyObjectCriteria()
      {
       // some criteria
      }
    
      // Get the data
      var myObjects = new MyObjectDbRetriever()
       .GetMyObjects(myObjectCriteria);
     }
    }
    
    上記のクラスでは、呼び出し元はMyObjectDbRetriever . なぜこれが悪いのか.いくつかの理由
  • コントローラをテストするのは難しいですMyObjectDbRetriever を直接参照する.これは、データベース論理なしでコントローラ論理をテストしないということを意味します — ユニットテストの可能性はほとんどない
  • 上記に関連していますが、それでも私はそれを指摘すべきだと思いました.コントローラは「何」よりむしろ「方法」に縛られます.「私はMyObjects データベースからMyObjectCriteria 私は必要としないMyObjects MyObjectCriteriaを使う

  • なぜ適切な抽象化は、より良いコードを書くのに役立ちますか?
    上記からの作業MyObject 例を少し変えましょう.現在の「方法」ではなく、私たちの抽象化に「何」を紹介しましょう.
    public interface IMyObjectRetriever
    {
     IEnumerable<MyObject> GetMyObjects(MyObjectCriteria criteria);
    }
    
    今、我々は契約、アイデア、“何”抽象化している.我々は現在、元のMyObjectDbRetriever このインターフェイスを利用するには、次の手順に従います
    public class MyObjectDbRetriever : IMyObjectRetriever
    {
     public IEnumerable<MyObject> GetMyObjects(MyObjectCriteria criteria)
     {
      // Connect to a database
      // Do some sql-ey stuff
    
      return results;
     }
    }
    
    これは以前に使用したのと全く同じクラスですIMyObjectRetriever . どのように、これはコントローラでものを変えますか?非常に興味深い方法で!今、我々はインターフェイスにではなく、具体的には、依存の注入は、私たちのためのオプションになるプログラミングです.
    依存性注入は、ソリッドデザイン原則の「D」を達成する1つの方法です — 依存性反転.このような基本的な考え方は、高レベルモジュール(コントローラ)が低レベルモジュールに依存しないことですMyObjectDbRetriever ). この原則は明らかに最初の例で違反されていましたが、今それを防ぐために何が変わったのでしょうか?
    オリジナルをもう一度見ましょうMyObjectController
    public class MyObjectController
    {
     public IEnumerable<MyObject> GetMyObjects()
     {
      // make up some criteria
      var myObjectCriteria = new MyObjectCriteria()
      {
       //
      }
    
      // Get the data
      var myObjects = new MyObjectDbRetriever()
       .GetMyObjects(myObjectCriteria);
     }
    }
    
    上記において、「高レベルモジュール」コントローラは、以下の「低レベルモジュール」に非常に依存しているMyObjectDbRetriever . 私たちの新しいインターフェイスとコンストラクター依存注入を利用して、我々はそれを変更することができます!
    public class MyObjectController
    {
     private readonly IMyObjectRetriever _myObjectRetriever;
    
    public MyObjectController(IMyObjectRetriever myObjectRetriever)
     {
      _myObjectRetriever = myObjectRetriever;
     }
    
     public IEnumerable<MyObject> GetMyObjects()
     {
      // make up some criteria
      var myObjectCriteria = new MyObjectCriteria()
      {
       //
      }
    
      // Get the data
      var myObjects = _myObjectRetriever.GetMyObjects(myObjectCriteria);
     }
    }
    
    上記の実装では、彼らは非常に重要な変更をしていますが、いくつかのことが変更されている!今、私たちには、実装を取るコンストラクタがありますIMyObjectRetriever . 機能GetMyObjects のインターフェイスメソッドを呼び出しますGetObjects(myObjectCriteria) , 具体的なDBメソッドではなく.コントローラクラスは、MyObjectDbRetriever またはデータベース!今、コントローラクラスは単にデータを取得できるインターフェイスのアイデアに依存しています — 緩い結合!
    どのようなものは、我々のコントローラを呼び出すものは、データの性質に応じて異なる動作を返しますか?上記の変更は、我々がより容易に今できることを意味しますtest モック、フェイク、シムズを使用してコントローラ.以前は、MyObjectDbRetriever , 我々のデータベースが特定のデータ要件を返すことを確実にしなければならない.今の例として、インターフェイスを実装する他のクラスをスローし、テストの要件に基づいてデータを返します.
    public class MyEmptyFakeObjectRetriever : IMyObjectRetriever
    {
     public IEnumerable<MyObject> GetMyObjects(MyObjectCriteria criteria)
     {
      return new List<MyObject>();
     }
    }
    
    public class MyNullFakeObjectRetriever : IMyObjectRetriever
    {
     public IEnumerable<MyObject> GetMyObjects(MyObjectCriteria criteria)
     {
      return null;
     }
    }
    
    public class MyKritnerFakeObjectRetriever : IMyObjectRetriever
    {
     public IEnumerable<MyObject> GetMyObjects(MyObjectCriteria criteria)
     {
      return new List<MyObject>()
      {
       new MyObject("kritner")
      };
     }
    }
    
    上記のクラスはすべて実装されているのでIMyObjectRetriever それは単に私たちの特定のシナリオをテストするための偽クラスのインスタンスを通過する問題です.私は一般的にこのようなシナリオで“”を使用しますが、これらの偽物は十分に証明するために簡単です.
    私は、これは抽象の表面をひっかかせるだけであるように感じます、しかし、うまくいけば、これは他の人がその「瞬間」瞬間を持つのを援助します!
    関連項目:
  • Getting started with unit testing and Moq
  • What is the business value of unit testing?