上海で実演したテストケースの方針(メモ)その2

上海で実演したテストケースの方針(メモ) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2217

の第2弾…のメモです。
先の例では、DAO(Data Access Object)なのでテスト対象は主に SQL になる訳ですが、今度は業務ロジック(business logic)を中心に解説します。

業務ロジックの場合は、基本は以下の 4 箇所がテストの対象になります。

  • メソッドのパラメータ
  • if 文による分岐
  • for 文による繰り返し
  • 内部メソッドの呼び出し

ここで、内部メソッドの呼び出しは既にテスト済みであることが前提です。

サンプル的には次のコードを頭に入れます。

// 引数は, id と name の 2 つ
public int func( int id, string name )
{
	// パラメータで判別
	if ( id == 0 ) {
		return -1;
	}
	// 内部関数の呼び出し
	DataTable dt = this.GetPersons( name );
	// 繰り返し処理
	foreach ( DataRow row in dt.Rows )
	{
		if ( id == (int)row["id"] ) {
			// 正常値
			return (int)row["age"];
		}
	}
	// 異常値
	return -1;
}

■テストコードを書く順番

前回の DAO と同じように、簡単なものから複雑なものへと進めていきます。前のテストコードがコピー&ペーストで使えるようにします。

0) TestZero

動作確認のテストコードを書きます。

1) TestOK

正常系が通るテストコードを書きます。

2) TestNG

戻り値が異常値となるテストコードを書きます。

3) TestParam

パラメータが変化した時の戻り値をチェックします。

4) TestIf

条件分岐に対するテストです。
switch-case による複数分岐もここに入れます(複数分岐の場合は、網羅性を追求せず、サンプリングします)

5) TestFor

繰り返しが1回の場合、複数回の場合、0回の場合をチェックします。

6) TestBorder

パラメータの変更による境界値試験です。

7) TestInner

内部メソッドに渡すパラメータが変化するパターンをチェックします。
内部メソッドについてテスト済みなので、パラメータより引き渡される条件で、2,3 ぐらいチェックすれば十分です。

まぁ、ざっとこんな感じでパターンを頭に入れておけば、OK ということで。

カテゴリー: 開発, xUnit | 上海で実演したテストケースの方針(メモ)その2 はコメントを受け付けていません

上海で実演したテストケースの方針(メモ)

上海出張して、NUnit の作り方を教え/実演してきました。

日本だと google で調べればそれなりに出てくるし「テスト技法」の本を読めば、それなりに分かるんですが、上海チームではそのあたりの資料が不足しております…という訳で、テストを作るときのメモを残しておきます。

■テストコードを書く手順

TDD(テスト駆動)を正確にやるためには、テストコード作成後、実コード(=テスト対象のコード)を書く、という手順になるのですが、時間的な制約やテストコードの記述の慣れ、テストケースの作り方の慣れ≒理解度、もあるので、まぁ、「実コードを書いてからテストコードを書いても構いません」という方針で説明しました。

その中で、ひとつルールとして守ってほしいのは、以下のひとつだけ。

「コードを書いたら、その日のうちにテストコードを書いて確認することッ!!!」

テストコード初心者がやりがちなのは、大量の実コードを書いた後に大量のテストコードを書くことです。全ての実コードを書いた後では、テストコードを書くと、時間が経ってしまっているのでテストが通らないときのデバッグに時間が掛かるし、開発の工数が少ないときにはテストコード自体がばっさり省略されてしまうことがあります。
なので、これを避けるために、その日のうちにテストコードを書いて確認します。
例えば、実コードを15分で書いたらテストコードを1時間という感じ。テスト自体は動作確認を兼ねるので、長めに時間を取ると良いでしょう。慣れれば、実コード 30 分、テストコード 30 分、的な配分も可能です。

■テストコードは省略可能である

QA(品質システム)としては、網羅性テストをしたほうが良いのですが、プロジェクトの状況によっては、網羅性よりも実品質を優先させます。いわゆる、QCD の Delivery を優先させるために、テストの Quality を落とします。
ええ、勿論、製品自体の Quality も落ちるので、そのバランスは大切です。

■テストコードを書く順番

これらを踏まえて、実コードに対してテストコードを書いていきます。

以下は、私の経験を踏まえた順番です(私自身の実コードの癖もあるので、それぞれ自分にあった形で進めてください)。

今回は、SQL 文を内部に持つ DAO(Data Access Object)のテストを例にとります。

0) TestZero

動作確認用のコードを書きます。

1) TestOK

正常動作ができるコードをひとつだけ書きます。

2) TestNG

TestOK とは反対の、エラーになるコードをひとつだけ書きます。

3) TestMulti

TestOK を利用して、複数行呼び出しのテストコードを書きます。

4) TestSelect

TestMulti を利用して、複数行から1行だけ取り出す選択のコードを書きます。

5) TestOrderBy

TestMulti を利用して、insert する順序を逆にしても正常に取れかテストします。

6) TestWhere

TestSelect を利用して、選択時のパラメータを変更させます。

7) TestJoin

TestMulti を利用して LEFT OUTER JOIN が意図通りに動いているかをチェックします。

8) TestMultiCall

TestOK, TestMulti を利用して、実コードを2回以上呼び出しても、問題ないことを確認します。

大体、こんな流れで作ると、単純なパターンから複雑なパターンにテストコードを書き進めることができます。
単純なコードがうまく動いていないと、複雑なパターンは動きませんからね。

実コードの複雑度によって、適度に省略しても構いません。

ちなみにテスト技法的な名前をつけると以下になります。

0) 動作確認
1) 正常系(単数)
2) 異常系(単数)
3) 正常系(複数)
4) 選択性(境界値)
5) 順序性
6) 条件選択(パラメータ指定)
7) 連結、他データとの関連
8) 複数回呼び出し(単一性)

# 正確な名前かどうかはさておき、こんな感じで網羅ができます。

■テストデータの作り方

テストするときのデータは、できるだけわかりやすいものにします。
例えば、業務に即したデータ(ID を “MSD02AX001” のようにするとか)を使う必要はありません。
むしろ、テストの邪魔になります。

正常系のID: “001” or 1
異常系のID: “999” or 999
正常系の名前: “masuda”
異常系の名前: “XXXXXX”

のように違っているとことが分かるように付けます。

// 正常系
public void TestOK() {
	obj.SetData("...")
	string id = obj.GetUserID();
	Assert.AreEqual( "masuda", id );
}

// 異常系
public void TestNG() {
	obj.SetData("...")
	string id = obj.GetUserID();
	Assert.AreEqual( "XXXXXX", id );
}

あと、テストデータを参照させるときは、【できる限り変数を使わないようにします】

// 即値を使う
public void TestOK() {
	obj.SetData("masuda")
	string id = obj.GetUserID();
	Assert.AreEqual( "masuda", id );
}

下記のように変数をつかってはいけません。

// 変数に入れてはいけない。
public void TestOK() {
    string TESTID = "masuda";
	obj.SetData( TESTID )
	string id = obj.GetUserID();
	Assert.AreEqual( TESTID , id );
}

変数に入れると、どれが正しいデータなのかが分かりづらくなります。
また、基本的にテストデータはコピー&ペーストで作るので、コピーした後にテストデータを手軽に変更することができなくなります。

という訳で、もろもろのメモを up しておきます。
整理するのは後ほど(時間があれば)。

カテゴリー: 開発, xUnit | 上海で実演したテストケースの方針(メモ) はコメントを受け付けていません

string へのキャストと ToString の動作は異なる

いつまでも危ない(?)PDFを晒しておくわけには行かないのでw、小ネタでブログを進めます。

時々(特にVBプログラマの方?)では、文字列へのキャストを ToString で行っている箇所を見掛けるのですが、これは危ういです、という話ですね。

例えば、データベースから読み込みをした時に VB だと、こういう風に書いているのです。

Dim da as new SqlDataAdapter("SELECT * ...", cn )
Dim dt as new DataTable
da.Fill(dt)
' 文字列を取り出す
dim name as String = ""
if dt.Rows.Count > 0 then
  name = dt.Rows(0)("name").ToString()
end if

こんな風に、ToString メソッドを使って object 型を文字列に変換しています。
ただし、本来はここはキャストを使うべきです。後述しますが、キャストと ToString メソッドの【意味】が異なるので、必ずしも同じ動作をするとは限らないのためです。

dim name as string = CType( dt.Rows(0)("name"), String )

C# の場合は、

string name = (string)dt.Rows[0]["name"];

# 詳細に言えば、dynamic cast を使うんでしょうが、ここでは普通のキャストで。

さて、ToString でも用途は足りるので、これでも良いような気がしますが、何故キャストを使わないといけないかというと、以下の理由があります。

dim name As String = obj.ToString()

とした時に、name には必ず期待する【文字列】が入るかというと、実は異なるのです。これは、ListBox を扱うと分かるのですが、ToString メソッドはオーバーライド可能なので、単純なキャストとは異なる値を入れることができるのです。

動作が分かるように極端な例を示すと、

Public Class AClass
    Public Shadows Function ToString() As String
        Return "ToString AClass"
    End Function

    Public Shared Narrowing Operator CType(ByVal b As AClass) As String
        Return "Cast AClass"
    End Operator
End Class

ToString をオーバーライドしたものと、キャストを再定義したものを用意します。
すると次のコードでは実行結果が違ってきます。

        Dim a As New AClass
        ' ToString の場合
        Debug.Print(a.ToString())
        ' Cast の場合
        Debug.Print(CType(a, String))

▼実行結果

ToString AClass
Cast AClass

こんな風に、ToString が定義されている時は、思ったとおりには動かないのです。まぁ、こういう時は、String 型へのキャスト自体も危ういところなのですが、ひとまず ToString とキャストは違う動作をする、ってことを覚えてコーディングして欲しいなぁ、と。

余談を言えば、キャストの場合は string 型にキャストできない場合は例外を発生させるけど、ToString 型は例外にはならない(多分ならない)ですよね。このあたり、意図して ToString メソッド(ToIntegerメソッドとかと同列の意味で)を使う分には ok ってことなのです。

カテゴリー: 開発 | string へのキャストと ToString の動作は異なる はコメントを受け付けていません

デスマーチを脱するための5つの方法(プレゼン)

デスマーチを脱するための5つの方法(メモ) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2184

国家的なデスマーチの問題と、ITプロジェクトのデスマーチの問題をいっしょに論じることはできない…とは思っていませんがw、まぁ、貼り付けてある画像は特に悪意がある訳でもなく、特別な意味がある訳でもありません。

PDF 版は、こちらからダウンロードしてください。







な感じで、頭の隅に入れておくとよいです。

カテゴリー: プロジェクト管理, 雑談 | 1件のコメント

メソッドチェーンではテスト項目数は減らない

コードを短くすると単体テストが楽になる、の検証編 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2167

の続きで、実は適切なコードの書き方をしないとテスト項目は減りません。

# マーチン・ファウラー氏の言う「流れるようにコーディングをする」ことと
# コード自体の品質とは別なのです。
# 当然、コーディングがしやすい、というのも品質を上げる要因のひとつ
# であることは変わりありませんが。

これを検証してみます。
まずは、以下の3つのパターンを見てください。

A.通常パターン

Window win = new Window(800,600);
win.SetMessage("hello world.");
win.Show(0,0);

B.プロパティで設定するパターン

Window win = new Window();
win.Width = 800;
win.Height = 600 ;
win.Message = "hello world.";
win.X = 0;
win.Y = 0;
win.Show();

C.メソッドチェーンのパターン

Window win = new Window(800,600).Message("hello world.").Show(0,0);

非常に極端に書いていますが、典型的なコーディングの3つのパターンです。

A の通常パターンは、コンストラクタでサイズを指定して、その後でメッセージを設定しています。
表示をするときには、Show メソッドの引数で指定する、という関数呼び出し風なパターンですね。

この関数呼び出し風のパターンは、一見わかりやすそうですが、ぱっと見たときに引数が何を意味するのか分からないという欠点があります。例えば、次のように XY 座標をコンストラクタで設定するコードを書いたときは、

Window win = new Window(0,0,800,600);
win.SetMessage("hello world.");
win.Show();

なのか、

Window win = new Window(800,600,0,0);
win.SetMessage("hello world.");
win.Show();

なのかは、わかりませんよね。経験上、X,Y,Width,Height)の順と知っていても、それはマニュアルなど読まないと本当は分からないわけで、引数の意味はコード上には出て来ないのが普通です。

# よく MSDN で引数が混在しているコンストラクタやメソッド(Drawメソッド系とか)がありますが、この現象に陥っています。

さて、B のようにプロパティで指定する方法が、VB2.0(.NETにあらず)ありました。VBA でもこんな書き方をするし、tcl/tk もこんな書き方です。この書き方の欠点は、縦に長くなる(行数が多くなる)ということですね。大抵の場合、1 行にひとつの処理を書くことが多いので、A のコードよりも長くなるのが欠点です。
ただし、VB の場合は「:」を使ったり、C# の場合は「;」で複数行をまとめることができるので、

Window win = new Window();
win.Width = 800; win.Height = 600 ;
win.Message = "hello world.";
win.X = 0; win.Y = 0;
win.Show();

このように書いて、行数を短くすることができるのですが、A と B のパターンは決定的に違うところがあります。
A の場合は、Width と Height を同時に指定することができるのですが、B は、ひとつずつしか指定できません。なので、画面を表示したままのときに、Width, Height の順番に設定すると、横幅が変わった後に、縦幅が変わる、という現象が起こるのですね(実際、VB2.0 の頃には、そのような現象が発生していました)。なので、Width と Height の順番が重要で、実は変な感じなのです。

次は、最近流行(?)のメソッドチェーンの C パターンを見ていくと、あたかも 1 行で書けるために A パターンよりもすっきりしているように見えます。
コンストラクタで画面サイズを指定して、メッセージを追加して、画面の左上に表示するということがわかります。

ただし、これもメソッドの作り方が悪い、先の B のように順序が関係してきます。

Window win = new Window().Width(800).Height(600).Message("hello world.").X(0).Y(0).Show();

何をやっているのか、分かるような分からないような微妙なコードになります。C# の場合は、改行を入れることで少し読み易くできます。

Window win = new Window()
 .Width(800)
 .Height(600)
 .Message("hello world.")
 .X(0)
 .Y(0)
 .Show();

あれ?これって、B のパターンと同じですよね。
また VB.NET の場合は、With を使うと、B のプロパティのパターンを C のメソッドチェーンのように書けます。

Dim win = new Window();
With win
 .Width = 800
 .Height = 600
 .Message = "hello world."
 .X = 0
 .Y = 0
 .Show()
End With

ということはですね。実は、B のパターンで書いても、C のパターンで書いても、同じ数のテストをこなさないといけないのです。自由度が変わらないですからね。

じゃあ、A の通常パターンで書いた時には一番テストの数が少ないかというと、そうでもありません。

Window win = new Window(800,600);
win.SetMessage("hello world.");
win.Show(0,0);

1. コンストラクタの Window の引数は Width,Height なのか? Height,Width なのか?
2. Show メソッドの引数は、X,Y なのか? Y,X なのか?

という自由度が増えています。
実は、Window(0,0,800,600) としたときには、自由度が倍増します。実際コーディングをいくと実感できると思いますが、

func( int x, int y );
func( int x, string s );

ような 2 つの関数があったときには、int, int よりも、int, string のほうが間違いが減ります。
というのも、int, int の場合は、引数の順序を間違える可能性がありますが、int, string の場合はコンパイル時にはじかれてしまいますよね。これは、string, int のように逆のメソッドを作ることもできることを意味します(勿論、int,string と string,int の 2 つのメソッドが共存する場合は、どちらがどちらの意味なのかという別の自由度が発生しますが)。

というわけで、結論を言えば、A も B も C もテスト項目は同じぐらいになります。ゆえに、複雑度が同じという推測が立ちます。
推測を証明するために、先のようにメソッドと変数の数を勘定すると、

A パターン: 10
B パターン: 11
C パターン: 9

という数になります(引数が2つある場合は、それぞれの引数と交換を含めて自由度は3で勘定します)。
メソッドチェーンを使った場合は、若干小さくなりますが、思ったほど楽にならない、というのが検証できると思います。
まぁ、それでもインテリセンスが利いている最近の統合開発環境では、ピリオドを打った後にメソッドやプロパティ名が自動で出てくるのはいいですよね。メソッドの多重化が過ぎて 10 以上のメソッドから選ばないといけない、というアンチパターンよりいいと思います。

↓これとか、しんどくて…

Graphics.DrawImage メソッド (System.Drawing)
http://msdn.microsoft.com/ja-jp/library/9f3f6hy1.aspx

カテゴリー: 開発 | メソッドチェーンではテスト項目数は減らない はコメントを受け付けていません

必ず成功するための100の開発手順(メモ)

新規の開発手順を作成中です。まだ50手順弱なので100手順にしようかなと。

>> 要件定義
 >> 機能要件
 >> 顧客要望まとめ
  >> 業務分析
 >> 機能抽出
 >> システム概要設計
 >> リスク抽出
 >> リスク対策
>> 見積
 >> 顧客スケジュール
 >> 期間見積
 >> 配員計画
 >> 金額見積
>> 交渉
>> 設計工程
 >> システム設計
 >> ネットワーク設計
 >> オブジェクト指向設計
 >> データ構造設計
 >> ユーザーインターフェース設計
  >> 画面確認
>> 実装工程
 >> 実装計画
 >> タスク抽出
 >> 内部設計、詳細設計
 >> 実装
 >> 単体試験
 >> 結合試験
>> 試験工程
 >> 試験計画
 >> 機能試験
 >> 負荷試験
 >> ユーザー機能試験
 >> 修正
 >> 不具合表
>> 導入
 >> 新規導入
 >> 移行計画
 >> 運用マニュアル
 >> 運用試験
 >> 試験運用
 >> 運用開始
>> 保守
 >> ユーザーサポート
 >> 初期不良対処
 >> 変更要件
カテゴリー: プロジェクト管理, Plan Language | 5件のコメント

業務コードで使う例外処理のたった1つの方法

いわゆる「○つの方法」という釣りタイトルですが、ワタクシ的には例外処理に関しては一択です。
例外処理については、方法論的にも色々あるのは分かっているのですが、「業務」という範囲で考えると、という選択です。

UIがある場合とない場合で、ちょっとだけ書き方が分かれます。

// UIのある場合
int func(...) {
 try {
  // 全ての処理
 } catch ( Exception ex ) {
  // エラーメッセージを表示
  MessageBox.Show( ... );
 }
}
// UIのない場合
int func(...) {
 try {
  // 全ての処理
 } catch ( Exception ex ) {
  // そのまま例外を返す
  throw ;
 }
}

こんな風に、関数/メソッド内の処理を【全て】try catch で囲ってしまいます。
乱暴ですよね…ですが、この方法って VB3.0 の頃からの鉄板な方法だったりします。

sub func(...) 
ON ERROR GOTO L_ERROR

 ' 内部のすべての処理

 exit sub
L_ERROR:
 ' エラーメッセージを表示
 MsgBox(...)
end sub

のように、VB の頃(.NETではない)は、ON ERROR GOTO を使っていました。
さて、この頃 C++ はどうだったかというと…実は C++ は余り流行ってはいなくて、いまだ C 言語が主流でした。なので C 言語の場合は、

void func( ... ) 
{
	int err ;
	
	err = subfunc( ... );
	if ( err != 0 ) {
		// エラー処理
	}
}

な風に、戻り値でエラーを判別するしかなかったのです。

この鉄板の例外処理でログイン処理を書いてみると、

class Principal {
  string _username ;
  
  public bool Logon( string username, string password ) {
    try {
      if ( username == "" ) 
        throw new Exception("ユーザー名が空白");
      if ( password == "" ) 
        throw new Exception("パスワードが空白");
      if ( username == "root" ) 
        throw new Exception("rootユーザーはログインできない");
        
      SqlConnection cn = new SqlConnection(...);
      SqlCommand cmd = new SqlCommand( cn, 
        @"SELECT count(*) from usres 
          where @username = username and @password = password" );
      cmd.Parameters.Add(new SqlParameter("@username", username ));
      cmd.Parameters.Add(new SqlParameter("@password2, password ));
      cn.Open();
      int cnt =(int)cmd.ExecuteScaler();
      cn.Close();
      if ( cnt == 0 ) 
        throw new Exception("ログインできない");
        
      // ユーザー名を保持して正常終了
      this._username = username ;
      return true;
    } catch ( Exception ex )
      throw ;
    }
  }
} 

なにやら妖しげなコードになりますが(苦笑)、業務コードの場合はこっちのほうがすんなり通るし頑健なのです。

実は、このコード例外の throw が大量に書いてあるので安全なコードとは程遠いような気もしますが、ライブラリとしての使い方ではなくて業務コードをこのスタイルにしていまうと、exception の catch が必須になるので、これはこれで安全なのです。

フェイルセーフなコードを書くには? | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2173

さて、この業務コードを利用する側も try catch を多用します。

/// ログインボタンのクリック
protected void Login_OnClick(...)
{
	try {
		Principal user = new Principal();
		string username = TextUserName.Text ;
		string password = TextPassword.Text ;
		// username, password のチェックは一切しない。
		user.Logon( username, password );
		// 成功時にフォームにユーザー権限を保持する
		this.User = user ;
	} catch {
		// 例外発生は、一律「ログインできない」とする。
		MessageBox.Show("ログインできませんでした");
	}
}

こういう書き方を一律すると「正常系」を一気に書くことができます。
この発想のベースは、VB3.0 の頃からある ON ERROR GOTO なんですけどね。

こんな風な例外処理を取る場合、いくつかの前提条件があります。

・データベースアクセスが基本、正常に行われること。
・処理の流れが基本、正常系で流れること。
・プログラムのバグは、基本皆無であること(実行時にデバッグログ等を取らない)。
・ユーザーへは基本、アプリケーションエラーを知らせないこと。

ということです。このプログラムの書き方は、プログラムの潜在バグを隠してしまうというデメリットがあるのです。ですが、ワタクシ的には「リリースしたプログラムは、ユーザーの目の前で落ちないことが最優先ではなかろうか?」と考える訳でで、突如として落ちないことを最優先にする(それが潜在バグを隠すものであったとしても)と、こういう例外処理の使い方もありかなぁと考えています。

ちなみに、そこそこの業務以外では、こういう書き方をしません。アリだとは思うのですが、業務コードを書くときに限ってという話です。手元のツールを作るときなんかは DEBUG 定義などを利用します。

カテゴリー: 開発 | 2件のコメント

SQL Server 2000 では文字列を数値に勝手に変換するよ

後で、SQL Server 2008 とかの最新バージョンを調べますが、落とし穴になりかねないのでメモ。

create table test1 (
 id int,
 name varchar(10),
 num int 
);

として test1 テーブルを作成しておきます。
ここに次のようなデータを入れます。

delete from test1 ;
insert into test1 values(1,'masuda',10);
insert into test1 values(2,'tomoaki',20);
insert into test1 values(3,'100',30);

このデータを検索します。

select * from test1 
 where num = 10 ;

この結果は予想通り、1行目が取得できます。

1	masuda    	10

さて、num に文字列の’10’を指定したときはどうなるでしょうか?

select * from test1 
 where num = '10' ;

マッチングしないと思いきや、自動的に文字列型から数値型に変換されて、1行取得されます。
# Oracle の場合は変換エラーになったと思うのだけど、うろ覚えです。

1	masuda    	10

逆に文字列型(varchar)に対して数値で比較してみると、

select * from test1 
 where name = 100 ;

これはエラーになります。

サーバー : メッセージ 245、レベル 16、状態 1、行 1
構文エラー。varchar 値 'masuda    ' から int データ型に変換できませんでした。

1行目の name 列の値「masuda」が int 型に変換できないためにエラーになっていますね。
となると、name 列の値が全て数値型になるようにしたら、どうなるのでしょうか?

delete from test1 ;
insert into test1 values(1,'100',10);
insert into test1 values(2,'200',20);
insert into test1 values(3,'300',30);

というデータを入れた場合に、次の SQL を動かすと、

select * from test1 
 where name = 100 ;

実は、結果が取れるのですッ!!!

1	100	10

そんな訳で、SQL Server 2005 の SQL を書くときに、自動変換をあてにしたり、自動変換がされるような書き方をすると、はまる可能性があるよという話でした。
これは、.NET から扱うときに、SqlParamter オブジェクトの使い方が問題になってくるのですよね。これは別の記事に。

カテゴリー: 開発 | 2件のコメント

純粋オブジェクト指向言語を思考実験してみるテスト

関数言語風なところ(LINQ)や、手続き型の関数の羅列を取り除いて、オブジェクト指向オンリーでプログラミングをする言語を妄想(笑)してみます。個人的には、純粋オブジェクト指向言語は Java がそれに近いのですが、もうちょっと何とかするいう形で。

まず、オブジェクト指向言語の重要な要素を挙げると、

1)カプセル化(隠蔽化)できる。
2)ポリフォリズム(多態化)できる。
3)継承関係がある。

なところです。

ここで、焦点を当てるのは「カプセル化」です。
もともと、オブジェクト指向の最初は、オブジェクト同士のやりとは「メッセージ」を使ってやり取りをしています。

objectA -> message -> objectB

のような繋がりになります。このメッセージ(message)は、objectA のメソッドなのか、objectB のメソッド(リスナー?)なのかは、定義されません(とか思っています)。

また、メソッド(関数)の呼び出しは、C言語やC++の表記上、複数の引数とひとつの戻り値、という決まりがありますが、実は戻り値は複数でも構わないのです(F# のタプルと同じですね)。

objectA.Method
{
  (retA, retB, ... ) = objectB.Method(paramA, paramB, ... )
}

な形です。

さて、手続き型の流れの場合は、if 文を使ってフロー制御をしますが、純粋オブジェクト指向の場合は if 文の扱いは、ちょっと慎重になります。

if ( a != null ) {
  if ( b != null ) {
    objB.Method( a, b );
  }
}

のように呼び出し前にチェックを入れますが、純粋オブジェクト指向では呼び出し時のチェックをしません。
これは、パラメータの整合性のチェックは、呼び出されたオブジェクト側の責務であり、呼び出し側では判断がつかないためです。もう少し詳しく言うと、隠蔽化を進めるとどのパラメータが正常値であるのかは、呼び出されたオブジェクトしか分かりません。

なので、責務を明確にするために、

// 呼び出し側では、パラメータチェックをしない
objB.Methd( a, b )

// objB の中でパラメータチェックをする
void objB.Method( string a, string b ) {
  if ( a == null ) return ;
  if ( b == null ) return ;
  // メソッド内の処理をする
}

この場合、objA から objB への問い合わせが非常に多く発生するため、パフォーマンス的には良くない(と想像される)のですが、オブジェクト指向の視点から言えば、これが正しいのです。なお、オブジェクト指向でのメソッド呼び出しのコストは「0」となっています。なので、呼び出しが多くても少なくてもスピードは関係ありません(ということになっています)。

この部分、責務を明確にする、ということで、objA で判別するべきか、objB で判別するべきか、というので見解が分かれそうですよね。例えば、次のコードを見てください。

if ( a == "masuda") {
  objB.Method( a, b );
}
// objB の中でパラメータチェックをする
void objB.Method( string a, string b ) {
  if ( a == null ) return ;
  if ( b == null ) return ;
  // メソッド内の処理をする
}

こうなっていた場合、a == “masuda” は何を意味するのでしょうか?ここだけ見ると、変数 a の内容が “masuda” の時に、objB.Method を呼び出す、という条件にしかならないので、これがオブジェクトの内部を考慮してのものなのか、オブジェクトを呼び出すときの条件なのかが判断がつきません。なので、他のところで、

if ( a == "tomoaki" ) {
  objB.Method( a, b );
}

と違う値で呼び出していることを確認した時に、オブジェクトの呼び出し側がつけている条件(呼び出されているクラスの前提条件ではない)ということが判断できます。
なので、書き方として、

・呼び出し側で判断する制御文
・隠蔽化を強化するための制御文

の2つを区別しないといけません。

objB.Method( string a, string b ) {
	// 前提条件
	if ( a != null ) {
	if ( b != null ) {
	// 処理内容
	if ( a == "masuda" ) {
		objC.Method( a );	
	}
	}}
	return ;
}

のように書き分ける必要があるのですね。
たしか、アスペクト指向の書き方として、そんなのがあった覚えがあります。


obj.Method.Before( string a, string b ) {
	if ( a == null ) return ;
	if ( b == null ) return ;
}
obj.Method( string a, string b ) {
	// 処理内容
	if ( a == "masuda" ) {
		objC.Method(a);
	}
	return ;
}
obj.Method.After( string a, string b ) {
	...
}

Method.Before では、本体 method の前処理を担当して、主にパラメータをチェックします。
Method.After では、後処理を担当します。

…と書いたところで、力尽きました orz もうちょっと大雑把に議論したほうがいいみたい。

カテゴリー: 開発 | 純粋オブジェクト指向言語を思考実験してみるテスト はコメントを受け付けていません

デスマーチを脱するための5つの方法(メモ)

後で、パワーポイントに直しますが、メモ的に。
プロジェクトが「デスマーチ」状態に陥ったときには、どうするのか?という5つの方法です。
# 「5」という数にはさして意味がありませんが、まぁ、ちょっとだけは意味があるかも。

思考の元ネタとしては、制約の理論(TOC)と、自己組織化、逃走論、デマルコ著スラック、ピープルウェアあたりです、

・トップマネージャを変える。
→ トップの思惑だけで動いている場合は、トップを変える。
→ あるは、トップの行動/行為がボトルネックになっている。
→ トップがマイナス生産するとき。

・権限を分散する。
→ 権限の抱え込みより、ボトルネックになっているとき。
→ 実行できる権限を分散することで、ボトルネックにならないようにする。
→ 権限を委譲する。

・外部の実行部隊を利用する。
→ ルールそのものがネックになっている場合は、ルールを変える。
→ 内部的な要因であれば、外部のものを使う

・自己組織化する。
→ トップに依存せずに、現場レベルで自己組織化する。
→ ただし、全体最適化は期待できない。

・逃げる
→ 崩壊する場合は、現場の人間としては逃げることが可能。
→ プロジェクトに命を掛けるほどなのかを再考する。

カテゴリー: プロジェクト管理 | デスマーチを脱するための5つの方法(メモ) はコメントを受け付けていません