モデラーはいつも抽象的な現実しか語らない。
概念なんだからしょーがねーだろ
っと言われるかもしれないが、
実際問題使う人たちが理解に苦しむのであれば
提供側はもちっと考えるべきである。
ユーザを苦しめるのがベンダの仕事か?ってことにYesYesYesと答えるのは
ユーザをないがしろにしている事実にほかならない。


ポリモルフィズムを語る際に、必ずコードが単純になるよってことしか言わない。
再利用性が高まることを示すにはサンプルコードはあまりにも少ないのだ。
理解を促すために必要なのは前提と結論と過程の3つであり、
巷にあふれる説明には過程がカケラしかない。
ふっとぐぐったらこんなんみつかったのだが、中身はひでぇもんだ。*1


コードに落とし込む時、メソッドの役割は2種類必要になる。
 ・実処理
 ・実処理を呼び出すだけの処理

*1:世で見る普遍的な言葉という意味では「平均的」なのだが、 世の中の平均が正しいかと言われるとこの業界の現在においてはそんなことはないと言える。

サンプルをつくるぜ 第1弾

昨日のエントリで書いたこんなことを踏まえてサンプルを作ってみる事にしましょ。

<メソッド編>
 (1)1つに付き1種類しか役割を持たないメソッド
 (2) (1)をいくつか呼び出すだけのメソッド

車とかテレビとかに例えられる「オブジェクト指向とは」というメタファは全くわからんと言いたくなるほど想像つきにくいので、
おいらは「SQLを投げてデータベースからレコードを取得する」というのを例にコードを作ってみるとします。
まず。
メソッドの処理内容設計から。
Select文を投げてからデータを取得するまでにはどんな手順が必要か羅列します。

 1. SQL文を作る
 2. SQL文を投げる
 3. RecordSetを取得する
 4.(RecordSetの結果を加工する)

これを元に(2)の呼び出しだけメソッドを作ります。

public ResultSet queryToResultSet(){
  RecordSet result = null;  // 戻り値作成用

  // 1. SQL文を作る
  String strSQL = createSQL();

  // 2. SQL文を投げる
  boolean boolRtn = executeQuery( strSQL );
  if( boolRtn ){
    // 3. RecordSetを取得する
    result = getResultRecordSet();

    // 4.(RecordSetの結果を加工する)
    //result = arrangesData( result );
  }

  return result;
}

とはいえ、Javaの言語仕様的には2と3を分離させるのはアホっぽいのでこうしてしまえ。

public ResultSet queryToResultSet(){
  RecordSet result = null;  // 戻り値作成用

  // 1. SQL文を作る
  String strSQL = createSQL();

  // 2. SQL文を投げてRecordSetを取得する
  result = executeQuery( strSQL );
  //if( !( result == null ) ){
  //   4.(RecordSetの結果を加工する)
  //  result = arrangesData( result );
  //}

  return result;
}

これで最小限機能のSelect発行用メソッドが出来たので、
次に実処理用のメソッドを。

// 省略:ヘッダコメント!
private String createSQL(){
  StringBuffer strbSQL = new StringBuffer(256);  // キリがいい

  strbSQL.append( " select " );
  strbSQL.append( " item01 " );
  strbSQL.append( " , item02 " );
  // 〜(中略)〜
  strbSQL.append( " , itemXX " );
  strbSQL.append( " from " );
  strbSQL.append( " table_name " );
  // 〜(where文省略)〜

  // SQL文返却
  return strbSQL.toString();
}
// 省略:ヘッダコメント!
private RecordSet executeQuery( String strSQL ){
  ※ とりあえず細かい事はおいといて、コネクションはあるものとしてステートメントを使ってみる
  Statement stmt = _con.getConnection().createStatement();
  return stmt.executeQuery( strSQL );
}

// 省略:ヘッダコメント!
private RecordSet arrangesData( RecordSet rsItem ){
  // データの加工はこちらで好きなように
  
  return rsItem;
}

ちょいと小休止

これで出来上がった以下のメソッド達。
 queryToResultSet();
 createSQL();
 executeQuery();
 arrangesData();
どの辺りがポリモルフィズムかというと、「queryToResultSet()」がそう。
「createSQL()」で返されるSQLは何かは知らないし知らなくていいし、
「executeQuery()」で何をしているか知らないし結果だけくれればいいし、
「arrangesData()」にいたってはあってもなくてもいい。
そいつら実処理メソッド群が実際に何しているかはともかくとして、
呼び出す手順だけを画一化している。
手順は同様の処理時には変わりようのないってか、毎回変わってちゃいけないところなので
画一化が望ましいと。
そんな画一化された手順をいろんなことに使いまわしましょうってのがポリモルフィズムの意義であって、
「いくらでも再利用したり、取り替えたりできる」ってのは利用結果の説明でしかない。


現実問題を見たときに、上記で終わるとやっぱり使いにくいコードのままでおわる。
そんな状況で使いやすさと効果とメリットは説明できまい。
ってことで、徐々に変えてみる。

サンプルをつくるぜ 第2弾

メソッドのみで構成されたこいつはまだ多態性のカケラしかない。
同一のメソッドから唯一の形態しか取れないのはいかんでしょってことで、
クラスを用意してみる。

<クラス編>
 (1)1つに付き1種類しか役割を持たないクラス
 (2) (1)をいくつかまとめているだけのクラス

これにのっとって、(1)「Selectを投げる役割」のクラスを作る事にする。

// やっぱり省くぜヘッダコメント
public abstract class GenericSelect {
  
  // 省略:ヘッダコメント!
  public String createSQL(){
    // 実装はサブクラスにて行うこと
  }
  // 省略:ヘッダコメント
  public RecordSet executeQuery( String strSQL ){
    // 実装はサブクラスにて
  }
  // 省略
  public RecordSet arrangesData( RecordSet rsItem ){
    // 実装はサ(ry
  }
}

これを利用した形で以下の「queryToResultSet()」を変える

public ResultSet queryToResultSet( GenericSelect gs ){
  RecordSet result = null;  // 戻り値作成用

  // 1. SQL文を作る
  String strSQL = gs.createSQL();

  // 2. SQL文を投げてRecordSetを取得する
  result = gs.executeQuery( strSQL );
  //if( !( result == null ) ){
  //   4.(RecordSetの結果を加工する)
  //  result = gs.arrangesData( result );
  //}

  return result;
}

こうなればGenericSelectさえ継承しておけば遠慮なくqueryToResultSet()に投げる事ができるようになる。
たとえば社員マスタを作り、

public class EmployeeMaster extends GenericSelect{
  // 実装省略っ
}

呼び元ではこう投げる。

  // あえてこんな風に宣言してみる
  GenericSelect selecter = new EmployeeMaster();
  // ※もちろん「EmployeeMaster selecter = new EmployeeMaster();」で何の問題もない。

  RecordSet rs = queryToResultSet( selecter );

これでEmployeeMasterに定義されているこの3つの内容が実行されることになる。
 ・createSQL();
 ・executeQuery();
 ・arrangesData();

EmployeeMasterでなくとも給与マスタとか、車マスタとか、テレビマスタとかでも
手順を全く変えずに実行できる仕組みができる。

  GenericSelect selecter = new 給与マスタ();
  RecordSet rs = queryToResultSet( selecter );

になるか

  GenericSelect selecter = new 車マスタ();
  RecordSet rs = queryToResultSet( selecter );
  GenericSelect selecter = new テレビマスタ();
  RecordSet rs = queryToResultSet( selecter );

ってな感じになるかでそれぞれの検索結果が取得できるようになる。

ちょいと小休止 その2

今まで作ったソースはどこまでいってもお勉強レベルのためのもので、
実務には足りない機能しかない。
update、insert、deleteなんかカケラもないしね。
Select1種類につき1クラスってのも現実的ではないしね。
そもそもサンプルには一つ疑問が残っている。

(,,゚Д゚)<呼び元ってどこやねん

いずこかのコードなのかはっきりしない。
queryToResultSet()の形態はどこでも使えそうな気もするし、
どこにあってもよさそうな気もする。
しかして「役割」を考えたときに、本体は良くとも呼び出し元の「役割」には疑問がのこる。
処理を呼び出す役を担うものはまた処理を呼び出すだけの役であるはずなのである。
ってことは、そいつには明示的な呼び出しタイミングがあるはずで、
なんとなしに放置していては

(,,゚Д゚)<ポリモルフィズムってどう呼び出して使うのよ

ってことになる。
とはいえ。
ここからは普遍的な仕組みを元にさらにがっちりいってみる。

サンプルをつくるぜ 第3弾

updateやinsertはそれ自体が「処理」であり「処理の呼び出し」であるからして、
メソッド構成はqueryToResultSetみたいに実処理呼び出し型になる。

<クラス編>
 (1)1つに付き1種類しか役割を持たないクラス
 (2) (1)をいくつかまとめているだけのクラス

の役割で言うとさっきの
(1)「Selectを投げる役割」のクラス

(1)「SQL文を投げる役割」のクラス
に変更する。

// やっぱり省くぜヘッダコメント
public abstract class GenericSQL {
  
  // 省略:ヘッダコメント!
  public String createSQL(){
    // 実装はサブクラスにて行うこと
  }
  // 省略:ヘッダコメント
  public RecordSet executeQuery( String strSQL ){
    // 実装はサブクラスにて
  }
  // 省略
  public RecordSet arrangesData( RecordSet rsItem ){
    // 実装はサ(ry
  }
  // 略
  public boolean executeUpdate(){
    // 実装(ry
  }
  // 略
  public boolean executeInsert(){
    // 実(ry
  }
  //
  public boolean executeDelete(){
    // (ry
  }
  //
  private boolean execute( String strSQL ){
    // (ry
  }
  // ry
  public String createSQLUpdate(){
    // (ry
  }
  // ry
  public String createSQLInsert(){
    // (ry
  }
  // ry
  public String createSQLDelete(){
    // (ry
  }
}

とりあえずカタチをすぐには変えずに踏襲してみはするものの、
SQLまで埋め込んでしまうとやっぱり1種類のことしか出来なくなるのはよろしくない。
処理パラメータでも渡してしまえば一挙に解決(・∀・)!
っと思いたくなるところだが、
SQLを作る」と「処理を実行する」という二つの役割が如実に分かれるようになる。
役割が分れたらクラスもわけましょう。
ってことで、「処理を実行する」役割の「ExecuteSQL」と、
SQLを作る」役割の「MasterBase」を作る。
が、ちょっとまってほしい。
SQLを作るのは各マスタに依存する事ではないか?
であれば各マスタが実装しているほうがよいのではないか?
・・・・・・
アサピー節はおいらにゃ難しかったことを思い知りつつも、
MasterBaseはイメージベースでつくることにする。
いくつSelectを発行するのか、いくつUpdateを発行するのかは構成時の自由だからである。

// 省いていいよねヘッダコメント
public class ExecuteSQL {
  
  // Select発行用
  public RecordSet queryToResultSet( String strSQL ){
    // 実装はこのクラスにて
    RecordSet rs = null;    // 戻り値作成用
    try {
      // ry
    } catch( Exception ex ){
      // ry
    }
    return rs;
  }
  // Insert、Update、Delete発行用
  public boolean execute( String strSQL ){
    // 実装はこのクラスにて
    boolen boolRtn = false;   // 戻り値作成用
    try {
      // ry
      boolRtn = true;   // ここまできたら成功
    } catch( Exception ex ){
      boolRtn = false;  // 絶対エラー
    }
    return boolRtn;
  }
}

2つのSelect、1つずつのInsert、Update、Deleteを投げるMasterBaseはこんなカタチ。

// やっぱり省くぜヘッダコメント
public class MasterBase extends ExecuteSQL{
  
  // 1つ目のSelect
  public RecordSet queryTo_Part01(){
    String strSQL = createSQLSelect01();
    return super.queryToResultSet( strSQL );
  }
  // 1つ目のSelectのSQL
  private String createSQLSelect01(){
    //(略)
  }
  // 2つ目のSelect
  public RecordSet queryTo_Part02(){
    String strSQL = createSQLSelect02();
    RecordSet rs = super.queryToResultSet( strSQL );
    return arrangesData( rs );
  }
  // 1つ目のSelectのSQL
  private String createSQLSelect02(){
    //(略)
  }
  // 省略
  private RecordSet arrangesData( RecordSet rsItem ){
    // 実装は(ry
  }

  // 唯一のUpdate
  public boolean executeUpdate(){
    String strSQL = createSQLUpdate();
    return super.execute( strSQL );
  }
  // ↑のSQL
  private String createSQLUpdate(){
    //(略)
  }

  // 唯一のInsert
  public boolean executeInsert(){
    String strSQL = createSQLInsert();
    return super.execute( strSQL );
  }
  // ↑のSQL
  private String createSQLInsert(){
    //(略)
  }

  // 唯一のDelete
  public boolean executeDelete(){
    String strSQL = createSQLDelete();
    return super.execute( strSQL );
  }
  // ↑のSQL
  private String createSQLDelete(){
    //(略)
  }
}

また、SQL文そのものを外部からもらう形式にすると、こんな感じで作れたりしますが、
SQLがハイブリッド結合になるような気もします。

// いつも通りのヘッダコメント
public class MasterBase extends ExecuteSQL{

  String _strSQL;

  public void setSQL( String value ){
    _strSQL = value;
  }

  // Select
  public RecordSet queryTo(){
    return super.queryToResultSet( _strSQL );
  }
  // Update
  public boolean executeUpdate(){
    return super.execute( _strSQL );
  }
  // Insert
  public boolean executeInsert(){
    return super.execute( _strSQL );
  }
  // Delete
  public boolean executeDelete(){
    return super.execute( _strSQL );
  }
}

作ってみたらあれですな。
ものすごいポリモルフィズムを実現しているように見えてきますが、
こんな多態性いらない。
やってることがSuperクラスそのまんまならコード量が増えてクラス数が増えるだけ害となるのがその理由。


物は試しで作ってみて、ロクな結果にならなかったら作る前に戻ろう。
「MasterBase」のような形式でEmployeeMasterやその他を作る事にすれば
Superクラスは間違うことなく多態性ばっちりな状態といえるだろう。

まとめ

オブジェクト指向開発を行う際に大事な事があって。
大事な事からそれなければ必然としてポリモルフィズムは実現できる。
そのために必要な事。
試行錯誤を理論立てて実行すること。
成功はもちろん失敗も予定通りとするココロが大事。
クラスやメソッドの役割分担を行い、
コードの書き直しを恐れずに何度もチャレンジすることが大事。

以下余談

とはいえっても、オブジェクト指向を用いることの目的はあくまで
システム開発の成功にあり、開発速度も速く、保守性にも優れたコードの作成であり、
ポリモルフィズムやインヘリタンスってのは手段にでしかない。
手段を目的とするのは古今東西愚の骨頂な行動である。


UMLモデラーもある一定を超えた人達は盛んに「UMLってば分りやすいっ」ってのを強調するが、
その説明は今のおいらでもさっぱりワカラン言葉遣いなのがいっぱいだ。
さっきリンクした先のもその類。


今回の例題において、サンプル2じゃ足りないからとサンプル3を作ったが、
それでもまだまだ現実には程遠い。
ResultSetだけを返せば便利なわけじゃなく、
存在確認や件数取得、マスタから1件だけ取得したい場合や
JavaBeansのようなEntityに変換したい場合もあるだろう。
.NETならResultSetよりもDataTableやDataSetのがいいかもしれない。
取得結果をそのままGridに張りたい場合や、その他の使い方はまだまだやまほどある。
その中で、なるべく役割を分担し、同一コードが増加する場面をへらし、
誰が作っても似たような構成で構築されるための前提作りはいろいろな要因と部品を必要とする。
サンプル3はまだスタート地点。
その先の要望はまだまだあるってこった。


一つ一つ条件を増やしていき、
構成が崩れないように崩壊しないようにクラスやメソッドを前提を守りながら仕分ける。
その繰り返しが真っ当になされていればサンプル3も、
おいらが現実に生み出したコードになる。
JavaでもC#.NETでもPHPでもVBでも手順の必然性はかわらない。
リフレクション使えたほうが楽。
デリゲート使えたほうが楽。
そーゆー違いがあるくらいで、必要な手順そのものはかわらない。