八葉の日記

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

めも

Humble Object at XUnitPatterns.com


Example: Humble Dialog

Many development environments let us build the user interface
visually by dragging and dropping various visual objects ("widgets")
onto a canvas.
多くの開発環境では、さまざまなビジュアルオブジェクト(「ウィジェット」)を
キャンバスにドラッグアンドドロップすることで、ユーザーインターフェイスを視覚的に
構築できます。

They let us add behavior to these visual objects by selecting one of
the possible actions or events specific to that visual object and typing
logic into the code window the IDE presents us with.
これらのビジュアルオブジェクトには、ビジュアルオブジェクトに固有の可能なアクション
またはイベントの1つを選択し、IDEが提示するコードウィンドウにロジックを入力することによって、
ビジュアルオブジェクトにビヘイビアを追加できます。

This logic may involve invoking the application behind the user interface or it
may involve modifying the state of this or other visual objects.
このロジックは、ユーザインタフェースの背後でアプリケーションを呼び出すことを伴い得るか、
またはこのまたは他の視覚的オブジェクトの状態を変更することを含み得る。

Visual objects are very hard to test efficiently because they are tightly coupled
to the presentation framework that invokes them.
ビジュアルオブジェクトは、それらを呼び出すプレゼンテーションフレームワークに密接に結合されて
いるため、効率的にテストすることは非常に困難です。

The test would need to simulate that environment to provide the visual object with
all the information and facilities it requires.
テストでは、ビジュアルオブジェクトに必要なすべての情報と機能を提供するために、その環境を
シミュレートする必要があります。

This makes testing very complicated, so much so that many development teams don't
bother testing the presentation logic at all.
これはテストを非常に複雑にするので、多くの開発チームはプレゼンテーションロジックのテストを
全く気にしません。

This leads to Production Bugs caused by untested code and Untested Requirements.
これは、テストされていないコードとテストされていない要件によって引き起こされるProduction Bugsに
つながります。

To create the Humble Dialog, we extract all the logic from the view component into
a non-visual component that is testable via synchronous tests.
ハンブルダイアログを作成するために、ビューコンポーネントからロジックのすべてを非同期テストで
テスト可能な非ビジュアルコンポーネントに抽出します。

If this component needs to update the view object's (the Humble Dialog's) state, the Humble Dialog is passed in as an argument.
このコンポーネントがビューオブジェクト(ハンブルダイアログ)の状態を更新する必要がある場合、ハンブルダイアログが引数として渡されます。

When testing the non-visual component, we typically replace the Humble Dialog with a Mock Object that is configured with the indirect input values and the expected behavior (indirect outputs.)
非ビジュアルコンポーネントをテストするときは、通常、間接入力値と予想される動作(間接出力)で構成されたモックオブジェクトをハンブルダイアログに置き換えます。

In some GUI frameworks, where the Humble Dialog has to register itself with the framework for each event it wishes to see, the non-visual component can register itself instead of the Humble Dialog (as long as that doesn't introduce unmanageable dependencies on the context) and this makes the Humble Dialog even simpler because these events go directly to the non-visual component and require no delegation logic
Humble Dialogが見たい各イベントのフレームワークに自分自身を登録しなければならないGUIフレームワークでは、非ビジュアルコンポーネントは、ハンブルダイアログの代わりに自身を登録することができます。コンテキスト)、これらのイベントは非ビジュアルコンポーネントに直接移動し、委譲ロジックを必要としないため、Humble Dialogをさらに単純化します

The following code sample is from a VB view component (.ctl) with some non-trivial logic.
次のコードサンプルは、VBのビューコンポーネント(.ctl)のロジックです。

It is part of a custom plug-in we built for Mercury Interactive's TestDirector tool.
Mercury InteractiveのTestDirectorツール用に作成したカスタムプラグインの一部です。

///////////////////////////////////////////////////////////////////////////////
' Interface method, TestDirector will call this method
' to display the results.Public Sub ShowResultEx(TestSetKey As TdTestSetKey, _ TSTestKey As TdTestKey, _
ResultKey As TdResultKey)
Dim RpbFiles As OcsRpbFiles
Set RpbFiles = getTestResultFileNames(ResultKey)
ResultsFileName = RpbFiles.ActualResultFileName
ShowFileInBrowser ResultsFileName
End Sub

Function getTestResultFileNames(ResultKey As Variant) _ As OcsRpbFiles
On Error GoTo Error
Dim Attachments As Collection
Dim thisTest As Run
Dim RpbFiles As New OcsRpbFiles
Call EnsureConnectedToTd
Set Attachments = testManager.GetAllAttachmentsOfRunTest(ResultKey)
Call RpbFiles.LoadFromCollection(Attachments, "RunTest")
Set getTestResultFileNames = RpbFiles
Exit Function
Error: ' do something ...End Function
Example VbViewWithLogic embedded from VB6/HumbleDialog-Not/ResultViewerPane.ctl
///////////////////////////////////////////////////////////////////////////////

=========================================================
Ideally, we would like to test the logic but we are unable to construct the objects passed in as parameters because they don't have public constructors.
理想的には、ロジックをテストしたいのですが、パブリックコンストラクタを持たないため、渡されたオブジェクトをパラメータとして構築することはできません。

Passing in objects of some other type isn't possible either because the types of the function parameters are hard-coded to be specific concrete classes.
関数のパラメータの型が特定の具象クラスにハードコードされているため、他の型のオブジェクトを渡すことはできません。

We can do an Extract Testable Component (page X) refactoring on the executable to create the testable component leaving behind just the Humble Dialog as an empty shell.
テスト可能なコンポーネントを作成して空のシェルとしてHumble Dialogだけを残すように、実行可能ファイルに対してTestable Component(page X)のリファクタリングを行うことができます。

This typically involves doing several Extract Method refactorings (already done in the original example to make the refactoring easier to understand), one for each chunk of logic that we want to move.
これには、通常、リファクタリングを理解しやすくするために元の例で行っていたいくつかの抽出メソッドリファクタリングを実行します。これは、移動するロジックの各チャンクに対して1つです。

We then do an Extract Class refactoring to create our new testable component class.
その後、Extract Classリファクタリングを実行して、新しいテスト可能コンポーネントクラスを作成します。
The Extract Class refactoring may include both Move Method[Fowler] refactorings and Move Field[Fowler] refactoring to move the logic and the data it requires from the Humble Dialog to the new testable component.
エクストラクトクラスリファクタリングには、Moveメソッド[Fowler]リファクタリングとMove Field [Fowler]リファクタリングの両方が含まれており、必要なロジックとデータをハンブルダイアログから新しいテスト可能コンポーネントに移動できます。
Here's the same view converted to a Humble Dialog:
ここでは、同じビューをハンブルダイアログに変換します:

///////////////////////////////////////////////////////////////////////////////
' Interface method, TestDirector will call this method
' to display the results.Public Sub ShowResultEx(TestSetKey As TdTestSetKey, _ TSTestKey As TdTestKey, _
ResultKey As TdResultKey)
Dim RpbFiles As OcsRpbFiles
Call EnsureImplExists
Set RpbFiles = Implementation.getTestResultFileNames(ResultKey)
ResultsFileName = RpbFiles.ActualResultFileName
ShowFileInBrowser ResultsFileName
End Sub

Private Sub EnsureImplExists() If Implementation Is Nothing Then Set Implementation = New OcsScriptViewerImpl
End If
End Sub
Example VbViewAsHumbleDialog embedded from VB6/HumbleDialog/ResultViewerPane.ctl
///////////////////////////////////////////////////////////////////////////////

以下は、ハンブルオブジェクトが呼び出すテスト可能なコンポーネントOcsScriptViewerImplです:
///////////////////////////////////////////////////////////////////////////////
' ResultViewer Implemenation:
Public Function getTestResultFileNames(ResultKey As Variant) _ As OcsRpbFiles
On Error GoTo Error

Dim Attachments As Collection
Dim thisTest As Run
Dim RpbFiles As New OcsRpbFiles
Call EnsureConnectedToTd
Set Attachments = testManager.GetAllAttachmentsOfRunTest(ResultKey)
Call RpbFiles.LoadFromCollection(Attachments, "RunTest")
Set getTestResultFileNames = RpbFiles
Exit Function
Error: ' do something ...End Function
///////////////////////////////////////////////////////////////////////////////

We could now instantiate this OcsScriptViewerImpl class easily and write VbUnit tests for it.
I've omitted the tests for space reasons because they don't really show anything particularly interesting.
このOcsScriptViewerImplクラスを簡単にインスタンス化し、VbUnitテストを作成することができます。私はスペースの理由でテストを省略しました。なぜなら、特に面白いことは何も実際に表示していないからです。

Humble Object

Humble Object at XUnitPatterns.com
非同期プログラミング - 非同期コードの単体テスト: テストを容易にする 3 つの解決策


Humble Object はインスタンス化することが難しいオブジェクトを、効率的にテストするための考え方です。(詳細は上記リンクを参照デス)
例えば、
・ビジュアルコンポーネントウィジェット、ダイアログなど)があるフレームワーク上にある場合です。
 この時はUnitTestのためには、フレームワークで依存している全オブジェクトをUnitTest環境で構築する必要があります
・非同期に実行されるためテストが難しいアクティブオブジェクトです。
 ※アクティブオブジェクト:スレッド、プロセス、ウェブサーバなどのタスクの基点(エントリーポイント)となる関数を所有するクラスのオブジェクト

使い方
①すべてのロジックをテスト対象のコンポーネントから同期テストを介してテスト可能なコンポーネントに抽出します。
②①のコンポーネントは、テストが難しいコンポーネントのロジックを、動機かつパブリックで呼び出しできるサービスインターフェイスを実装します。
③その結果、Humble Object トコンポーネントは非常に薄いアダプタレイヤーになり、コードはほとんど含まれません。
④ハンブルオブジェクトがフレームワークによって呼び出されるたびに、それはテスト可能なコンポーネントに委譲されます。

例えば、アクティブオブジェクトRequestHandlerThread が初期化されることをテストしようとする場合、
下記をターゲットにして、runして数秒後にinitializedSuccessfullyがtrueを返すことを確認するようなコードを書くのですが、
processOneRequest内部に複雑な処理があって、initializedSuccessfullyの結果が設定される場合は安定したUnitTestを書くことができない。
(実際に数秒まったり、フレームワークの状態によってはエラーとなる)

public class RequestHandlerThread extends Thread {
   private boolean _initializationCompleted = false;
   private int _numberOfRequests = 0;
  
   public void run()  {
      initializeThread();
      processRequestsForever();
   }
  
   public boolean initializedSuccessfully() {
      return _initializationCompleted;
   }
  
   void processRequestsForever() {
      Request request = nextMessage();
      do {
         Response response = processOneRequest(request);
         if (response != null) {
            putMsgOntoOutputQueue(response);
         }
         request = nextMessage();
      } while (request != null);
   }
}

この例では簡単であるが、ストラテジーパターンを使用して、requestHandlerに処理を移譲している。
こうすると、requestHandlerを同期で試験する、スレッドに対しては処理が移譲できていることを確認すればOKとなる。

public class HumbleRequestHandlerThread extends Thread
implements Runnable {
   public RequestHandler requestHandler;
  
   public HumbleRequestHandlerThread() {
      super();
      requestHandler = new RequestHandlerImpl();
   }
  
   public void run() {
      requestHandler.initializeThread();
      processRequestsForever();
   }
  
   public boolean initializedSuccessfully() {
      return requestHandler.initializedSuccessfully();
   }
  
   public void processRequestsForever() {
      Request request = nextMessage();
      do {
         Response response = requestHandler.processOneRequest(request);
         if (response != null) {
            putMsgOntoOutputQueue(response);
         }
         request = nextMessage();
      } while (request != null);
   }



コードは参考ページから持ってきています。
英語自信ないため解釈誤りありましたらごめんなさい^^

負の継承への対応(2)

負の継承への対応を具体的にまとめていく
まず、共通性を侵すことなく可変性を扱う場合は、とくに問題なくあつかえるという前提です。
また、負の可変性は正の可変性と対応した技術で実現します。例えば、正の可変性をテンプレートで
実現した場合は、負の可変性はテンプレートの特殊化になります。

負の可変性を扱うメカニズム
・テンプレートの特殊化
共通の形:型によらず共通

template < class  T>
class Stack{
    StackRep<T> *rep;
}

template < class  T>
class StackRep{
    StackRep<T> *next;
    T* rep
}

上記では、ポインタを持った構造体で管理しているが、整数型に対しては性能要件から動的配列はとれず、
素数も決まっている場合があった場合は、テンプレートの特殊化で以下のようにできる。

template < >
class Stack<int>{
    static int rep[10000];
}

・デフォルト引数
アルゴリズムに強い共通性がある場合で、値に対して弱いバリエーションがあった場合、
(言い換えると、十中八九はこの値だけど、ごくまれにコレというようなケース)

・データのキャンセル

//テキストバッファを扱うクラス
template <class CharSet >
class TextBuffer{
public:
    Line<CharSet> getLine(const LineNumber&);

private:
   
   LineNumber firstLine, lastLine;
   int currentLine;
   //はじめと最後の行、現在の行を扱えるとメンバ変数がいる
}

1行だけ扱うテキストバッファが必要で、テキストバッファに対する機能を有している上記を継承する場合は、
複数行扱うメンバーは邪魔になる。

//ページ単位でテキストバッファを扱う
template <class CharSet >
class PageBuffer : TextBuffer<CharSet>{
public:
} 

//1行単位でテキストバッファを扱う
template <class CharSet >
class LineBuffer : TextBuffer<CharSet>{
public:
} 

派生クラスで、基底クラスのメンバをキャンセルすることはできないため、可変性を外側にくくりだす。
ここでは、ページ単位でテキストバッファを扱うクラスと1行単位で扱うテキストバッファを以下のように定義する。

//まず、それぞれの実装を定義
template <class CharSet >
class PageBufferRep : public TextBufferRep<CharSet>{

   LineNumber firstLine, lastLine;
   int currentLine;
} 

template <class CharSet >
class LineBufferRep  : public TextBufferRep<CharSet> {
public:
} 

上記の実装に対して、クライアントへ提供するハンドルを以下のように定義する。

template <class CharSet >
class TextBuffer{
public:
    Line<CharSet> getLine(const LineNumber&);

protected:
   TexstBufferRep<CharSet>* rep;//最初の例と比較すると、所持していたデータメンバが抽象化されている。
   TextBuffer(TexstBufferRep<CharSet>*); //コンストラクタでRepを割り当てる
}

最終的にはこうする。

template <class CharSet >
class PageBufferBuffer : public TextBuffer<CharSet>{
public:
    PageBufferBuffer () 
     : TextBuffer<CharSet>(new PagedTextBufferRep){}
}
template <class CharSet >
class LineBufferBuffer : public TextBuffer<CharSet>{
public:
    LineBufferBuffer () 
     : TextBuffer<CharSet>(new LineBufferRep  ){}
}

図でいうと、
f:id:konboi_kun:20180225194822j:plain
以下になってます
f:id:konboi_kun:20180225200250j:plain


つかれたので、あとで整理しよう

負の継承への対応

■前置き
オブジェクト指向言語では、継承がサポートされているが、継承には2種類存在する。
・正の継承:Liskovの置換原則を守る継承
・負の継承:上記を守れないような継承
      親クラスで定義されたモノは、継承関係で共通の領域とみなされる。これを侵害するようなことを指す。

負の継承の例

struct strrecvfd {
#ifdef KERNEL
    union {
        struct file*fp;
        int fd;
    }
#else
   int fd;
#endif
   
   unsigned short uid;
   unsigned short gid;
   char util[8];
}

KERNELのシンボルの指定が基本である場合、KERNEL以外の場合には、負の可変性が生じている。
生じているのは、"fpが存在するという共通性が不要をされていること"です。
KERNELのシンボルの指定が基本である場合でない場合でも、負の可変性が生じている。
生じているのは、"fdの性質減っている"ことです。KERNELがない場合はfdのみで十分でしたが、KERNELの場合は、fd+fpで、もともとの
fdと同じ役割を果たしますので、fdの役割が一部減っています。

解決策を順にあげていく。
①負の継承に沿ってドメインサブドメインに切り分ける。
②テクニックを用いる

①についてですが、上記の例では、

struct strrecvFP {
    union {
        struct file*fp;
        int fd;
    }
   unsigned short uid;
   unsigned short gid;
   char util[8];
}

struct strrecvFD {
    int fd;
   unsigned short uid;
   unsigned short gid;
   char util[8];
}

②のテクニックについては、別途まとめます。
親子で明確に意味が異なるような場合は、テクニックでなんとかすのではなく、クラスをわけるというのが
正解かもしれない。
多分、②でなんとかできるのは"コードの再利用"の観点から継承を使用しているケースに限られるのではないかなと思う。


新装版 マルチパラダイムデザイン

新装版 マルチパラダイムデザイン

  • 作者: ジェームス・O・コプリン,James O. Coplien,平鍋健児,金沢典子,羽生田栄一
  • 出版社/メーカー: ピアソン桐原
  • 発売日: 2009/12/01
  • メディア: 単行本
  • 購入: 3人 クリック: 41回
  • この商品を含むブログ (6件) を見る

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リリース:望まれる成果を実現できる最小の製品リリース
  を見つけよう


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