八葉の日記

日々、感じたことをまとめる場として利用する

DI依存性注入

依存性の注入(Wikipediaから抜粋すると)
依存性の注入(いそんせいのちゅうにゅう、英: Dependency injection)とは、コンポーネント間の依存関係をプログラムのソースコードから排除し、
外部の設定ファイルなどで注入できるようにするソフトウェアパターンである。英語の頭文字からDIと略される。

コード中に依存関係を定義するコードがあると、ユニットテストがやりにくいという問題がある。
例えば、スクリーンに何かを描画するMainScreenが実際にディスプレイドライバーを触っているとする。

class MainScreen
{
        void Disp()
        {
            DisplayDriver* drv = new DisplayDriver980() ;
            bool ret = drv.Setup();
            if (FALSE = ret)
            {
                  drv.Teardown();
                  return FALSE;
            }

            return TRUE;
        }
};

上のコードを単体試験するときは、本物ドライバに依存しているため、ドライバーがインストールしている環境でないと動かないし、
ドライバーは大抵外部から提供されものであるため、自由に中を見れずエラーケースの試験を行うことが難しくなる。

こういうときは、MainScreenに対して依存するオブジェクトをMainScreenに依存する人が渡す方法がある。
これが依存性の注入(Dependency injection)。

class MainScreen
{
        Driver& m_drv;
  MainScreen(Driver& drv) : m_drv(drv){}
        void Disp()
        {
            bool ret = m_drv.Setup();
            if (FALSE = ret)
            {
                  m_drv.Teardown();
                  return FALSE;
            }

            return TRUE;
        }
};

利用者側が依存性を解決してあげる必要があるが(*)、これで特定のオブジェクトに対する依存性はなくなり、
依存するオブジェクトが特定のシグネチャに沿っていればよくなりました。
テンプレートを使うとよくわかるかと思います。下記コードでは渡すオブジェクトには継承関係がなくても
API仕様が同じであれば一切問題がないです。

template< class T >
class MainScreen
{
        T& m_depend;
  MainScreen(class T& depend) : m_depend(depend){}
        void Disp()
        {
            bool ret = m_depend.Setup();
            if (FALSE = ret)
            {
                  m_depend.Teardown();
                  return FALSE;
            }

            return TRUE;
        }
};

(*)
依存関係を解決するのを利用者に渡しているため、利用者が必要な依存を判断する必要があります。
なので、、、
利用者に対して依存関係を知られたくない場合はこの方法は使えません。
例えば、外部の顧客に対して提供するAPIに対して、DIを利用するのは難しいかもしれません。
これは、依存するクラスをセットしてというより、モード切替といったAPIを用意する。もしくは
自分で動作環境を判断して内部で切り替えるといった仕組みを用意することが多いからかなっと思います。

ソフトウェアテスト(統合テストとユニットテスト) 覚書

■統合テスト
複数の層を1つに繋げるテストを指す。

 できること
 - UIテストで見つけにくい下位の不具合、最下層のユニットテストでは見つからない不具合を検出できる
  → 複数の部品がちゃんとつながっていることを確認できる

ユニットテスト
※はじめにUIテストのデメリット
※ - 時間がかかる。一つあたりが数秒かかる場合を考えると数がおおくなればなるほど時間がかかる
※ - 問題あることの検知は容易だが、どこに問題があるか検知するのが難しい

 できること
 - 高速に実行できる、手軽に作成できる、迅速にフィードバックできる
 - コードが正しく動くことを保証する。
 - テストを書くことでコードに意図を伝えることができる
※いつまでやるか
  →コードに自信がもてるまで。考えるべきことはすべて考えたかということがテストで明らかになる
 


ユニットテストのデメリットとしていかが起こりうる。
モックの泥沼:ユニットテストを頑張って書いた人なら一度はかんじたことがある。
       「テストコード中の事前準備(モックの設定)が嫌におおい」→テストコードを書くのに時間がかかる。
       原因は、テスト対象コード自体が保守しにくい構造になっているため、テストコードとテスト対象が密接に結びついているため。

解決策
1.リファクタリング:試験対象を直して、試験コードもきれいにする。
2.入出力にのみ注目する:テスト対象レイヤのインプットとアウトプットのみに注目して試験を行う。

両者ともに試験対象と試験コード間の結合度を下げる。2については、ポートとアダプタというアーキテクチャを利用できる。

ユーザーストーリマッピング覚書 2018/01/02

ユーザーストーリマッピングを読んでいるので覚書

■ユーザーストーリーって何?
・ユーザーストーリを作るのは何のためか
 プロジェクトに関わる人間が作るものに対して共通理解を保つため
 → 実際のプロジェクトはできの悪い伝言ゲームのように皆が違うことを考えている。←防止する

 本当に作らないといけないものを見極めるため
 → 実際のプロジェクトでは必要のないものも作ってしまっている。←防止する

 

・念頭に置くこと
 誰の為に作るのか、何で必要なのかを常に考えよう
 自分たちの作ったものがどう使われて、誰が喜ぶのかを考えよう
 より多くの成果物を作るのではなく、より大きなインパクトを与えられるか考えよう

■どうすれば描ける?
・考えを整理する
 - 自分の考え(アイデアや提供する機能)を表に出して整理する。
  ホワイトボードやポストイットに書き出す。ポストイットの方が並び替えしやすい
・抜け漏れがないか確認する
 - アイデアを利用する人の1日を声に出して言ってみる
  表に出したアイデアで足りてないことがないか確認し、抜けていたら足す

→アイデアのマップを作る!


 ストーリを洗い出す時は、細かいところを初めから見ないようにする。大きな枠で大筋を掴み。まず整理してから
 細かい部分を洗い出す。

■洗練していくには?
・大きいマップを見やすくする
 - 複数のアイデアをまとめる。←より上位もしくは抽象化された概念にまとめるとマップがスッキリしませんか?
・優先順位をつけましょう
 - 依存関係から順番を見つけましょう
 - 利用者に対してインパクトの大きいアイデアから作りましょう
・要らないものは削っていきましょう
 - 優先度の低いものを作成する時間は削る。必要ないのであれば作らないとしましょう。
 - MVPリリース:望まれる成果を実現できる最小の製品リリース
  を見つけよう


補足:今は以下の書籍を勉強中です
ユーザーストーリーマッピング

SOLID原則:4つ目 インターフェース分離の原則(ISP:Interface Segregation Principle)

〇インターフェース分離の原則(ISP:Interface Segregation Principle)
クライアントにクライアント利用しないメソッドへの依存を強制してはならない

あなたがある機能を実現するモジュールを作成しているとして、そのモジュールが外部に公開しているIFを見るとメソッドが100個以上あって、複数クライアントから利用されているといったことはよくあると思います。
各クライアントはすべてのインターフェースを利用しているわけでなく、自分がほしい機能に関係するインターフェースを利用しているだけなので、機能ごとにインターフェースをグループ化して分けることで、クライアント利用するインターフェースを分離することができます。

こうすることで、クライアントは自分に関係あるインターフェースに対する変更した影響しなくなります。

SOLID原則:5つ目 依存関係逆転の原則(DIP:The Dependency Inversion Principle)

〇依存関係逆転の原則(DIP:The Dependency Inversion Principle)
上位のモジュールは下位のモジュールに依存してはならない。どちらのモジュールも「抽象」に依存すべきである
「抽象」は実装の詳細に依存してはならない。実装の詳細が「抽象」に依存すべきである。

構造化分析に従うと、システムをトップダウンで分析していき、主要機能を実現するのに必要なサブ機能を見つけていきます。これをそのままSW設計と実装に適用するとサブ機能の組み合わせで上位ができるため、上位は下位に依存する構成になります。

余談

こうなった時のデメリットとしては、些細な実装の変更が上位のモジュールに伝わってしまうため、すぐ変更が必要になってしまうことです。自分が作っている範囲ならいいのですが、上位と下位モジュールを別組織で作成していたりすると、変更自体が難しくなります。
例えば、下位担当が楽な変更を考えたとしても、上位への影響が大きいとか言われて、下位担当は楽じゃない変更をすることが起こりえます。また、上位からすると下位がちょっと変えてくれればいいのに他の上位モジュールにも影響があるから下位は変更できないとなります。
つまり、最終的に望むのは上位と下位が直接依存しないことになります。

閑話休題

以下の図では、Policy(方針)がMechanism、MechanismがUtilityに依存している。ここまでダイレクトな依存関係だとUtilityを変更することPolicyへ影響することになってしまいます。(そして、余談にかいてあるようなやりとりが発生します。f:id:konboi_kun:20171203140947p:plain)

f:id:konboi_kun:20171203135609p:plain
参考書籍にあった例:認識の甘いレイヤ構造

上位は自信が期待する振る舞いを定義したInterfaceを用意し、下位はInterfaceに沿った実装をする(下位が上位へ依存する)。この結果、上位はInterfaceに、下位はInterfaceに依存する構造のため、Interfaceで決めた範囲の変更では、余談に書いたようなことは起こりません。

アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技

アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技

GenICam:ノードについて

GenICam規格のノード説明のメモ
http://www.emva.org/wp-content/uploads/GenICam_Standard_v2_0.pdf

■ノードについて


Something vendor specific

The amplification of the camera
A more elborated description
Gain
Expert
12fc
SomeNode1
SomeNode2
SomeNode3
NodeIndicatingAnError
RO
SomeNode4



それぞれのノードにはName属性があり、 名前はカメラデバイス内で一意でなければならない。あと、NameSpace属性で識別される名前空間内に名前属性が存在する。
例:
名前空間はCustomかStandardの2つの名前を持つことができる。名前はカメラデバイス内で一意な値である必要はあるが、名前空間がStandardのときはSFNCにも従う必要がある。

拡張領域として用意できる。無視される。

ノードの簡単な説明を記載
A more elborated description
ノードのより詳細な説明を記載
Gain
機能の名前。
Expert
Beginner, Expert, GuruとInvisibleと可視性を制御できるパラメータ。
12fc
非同期イベント通信で使用するIDです。

SOLID原則:3つ目 リスコフの置換原則(LSP:Liskov-Substitution Principle)

〇リスコフの置換原則(LSP:Liskov-Substitution Principle)
派生型は基本型と置換可能でなければならない

上の意味だけ聞いてもなんのこっちゃとなるとおもうのですが、
リスコフの置換原則に反したケースを皆さん見たことがあると思います。

class MainScreen : public Screen
{
        virtual void Disp();
	~省略~
};

class SubScreen : public Screen
{
         virtual void Disp();
	~省略~
};

void Display(Screen* p)
{
	if (typeid(p) == typeid(MainScreen))※RTTIはイメージのために使っているので実際にこのコードを入れても動作しないと思います。
        {
               static_cast<MainScreen* >(p)->Disp();
        }
        else if (typeid(p) == typeid(SubScreen ))
        {
               static_cast<SubScreen * >(p)->Disp();
        }
}

クラスの型を見て処理を変える、コードをみたことはあるんじゃないでしょうか。。。。
ここでは、MainScreen とSubScreen がScreenの代わりに使えていません。こういうときにリスコフの置換原則に反しているといえます。

ここでDisp関数はvirtual関数なんだからif文はいらないじゃないかという方は正しいです。
上の例のコードにリアリティを付けるとこんな感じになると思います。

void Display(Screen* p)
{
	if (typeid(p) == typeid(MainScreen))
        {
     PreFuncMain(); //当初は予定していませんでしたが、度重なる仕様変更でMainの前にはDispに関係ない処理がどうしても必要です。
           //この処理はMainScreenクラスで実行するのはムリです。もしくは変更にコストがかかります。
               static_cast<MainScreen* >(p)->Disp();
        }
        else if (typeid(p) == typeid(SubScreen ))
        {
               static_cast<SubScreen * >(p)->Disp();
        }
}

■リスコフの置換原則を守るためには
リスコフの置換原則を守るためには、契約による設計を導入するということが
挙げられています。
契約による設計とは、クラスに不変条件、メソッドに事前条件と事後条件を決めることです。
もうこれを破れば継承関係を持たせるべきでないかもしれないという尺度になると思います。

アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技

アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技