gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

「意味で括る共通化」と「機能で括る共通化」

ちとfacebookでよいネタがあったので、軽く展開。


元ネタで書いてある内容としては大まかに
・2つのform(ログインと新規登録)でそれぞれvalidate(今回は一旦、必須チェック(空白チェック)のみ)を書きたい
・各formごとに空白チェックを書いていくのは無駄なのでうまくまとめたい
っていうお話し。


おいちゃんの結論としては
・まず「個々のform毎にclassを切る」。PHPの一般的フレームワークならModel継承クラス、うちのMagicWeaponならdata_clump継承クラス
・「空白チェック」は共通化してもいいんだけど多分1行で書けちゃうからあんまり旨みはなしw
ってところかなぁ。


かみ砕いて。


PHPの一般的にMVCと呼ばれるところのフレームワークにおけるModelがナニを意味するのか」についての議論は色々あろうか、と思うのですが。
とりあえず「一塊のデータ群」と「そのデータ群に対する処理」は、Modelに書いておいた方がなにかと「コントローラーが太らない」と思うのですだよ。
なので…もっそ概念的コードですが。
コントローラの中では、例えば上述formのお話しであれば、例えば新規登録なら

class 新規登録コントローラ {
  //
  pub func 新規登録確認() {
    //
    $model = new 新規登録model();
    $model->CGIからの入力データをインスタンスん中に入れる(); // 引数は無しでもいいしcgi requestクラスとかあるんならそれ渡せばいいし

    // 空白チェックをする
    if (false === $model->validate()) {
      $model->validateエラーの情報をviewに入れる(); // 引数でviewインスタンス渡すもよし、戻り値をハッシュ配列にしてviewに設定するもよし!
      エラー用viewを表示();
      return ;
    }

    // XXX ここまできたら問題ないはずなので
    $model->model自身の情報をセッションに書き込む();
    確認画面用viewを表示();
  }



  //
  pub func 新規登録完了() {
    //
    $model = new 新規登録model();
    $r = $model->model自身の情報をセッションから読み込む();
    if (false === $r) {
      「お前どっから入ってきたんぢゃいおかしなルート通ってくるんぢゃねぇ!」エラー表示
      return ;
    }

    // DBに書き込む
    $r = $model->insert(); // 引数に、必要ならDBハンドルとか渡す
    if (false === $r) {
      「なんか登録うまくいかなかったからもう一度試してみてね」エラー
      return ;
    }

    // セッション情報クリアして終了
    $model->model自身の情報をかいたセッション情報を消す();
    確認画面用viewを表示();
  }

って感じになって、故にmodelとしては

CGIからの入力データをインスタンスん中に入れる(); // 引数は無しでもいいしcgi requestクラスとかあるんならそれ渡せばいいし
validate();
validateエラーの情報をviewに入れる(); // 引数でviewインスタンス渡すもよし、戻り値をハッシュ配列にしてviewに設定するもよし!
model自身の情報をセッションに書き込む();
model自身の情報をセッションから読み込む();
insert(); // 引数に、必要ならDBハンドルとか渡す
model自身の情報をかいたセッション情報を消す();

が必要なことが、大体見て取れるです。
ちなみに確認画面なしなら

CGIからの入力データをインスタンスん中に入れる(); // 引数は無しでもいいしcgi requestクラスとかあるんならそれ渡せばいいし
validate();
validateエラーの情報をviewに入れる(); // 引数でviewインスタンス渡すもよし、戻り値をハッシュ配列にしてviewに設定するもよし!
insert(); // 引数に、必要ならDBハンドルとか渡す

で足りるので、大変に工数がエコノミーになりますw


ポイントは、controllerは「これやって」っておおざっぱな命令をmodelに渡すことで、modelは「あいよ」って受け取ってやるあたり。
こんな風に「やって欲しい動作」をメソッドにして、って感じでcontrollerを書いておくと、割合とコントローラーがダイエット出来ると思うのですだよ。
発想としては割とベーシックに
・「「1つのデータの塊」と、その塊に対する処理」をひとまとめに
って感じ。


「一塊のデータ」ってのはそれは「意味のあるもの」なので、それについてはひとまとめ。
んで、例えばその「一塊のデータに対するvalidate」であれば、それは「validateという意味を持つ処理」を共通化(メソッド化)しておきましょう、的発想。


この辺があるので、まずは「意味で括る共通化」をしっかりと意識しておくとよいと思うのですここは必須。


んで。
「意味的には異なるんだけど、処理的には同じだよねぇ」って機能があるです。
今回のネタだと「空かどうか」だし、それ以外にも例えばよく出す例えだと「消費税の8%計算」と「手数料の8%計算」はどちらも「元値の8%を計算する」程度には一緒の機能になります(端数処理とかが合致している前提)。


この場合の共通化ですが。
まず「意味レイヤー」では当然異なるので、意味レイヤー的には「別々のメソッドにするべき」ですこれは必須。
ここで混ぜると「後で面倒」なので、入り口はちゃんと切り分けるべき。


ただ「機能がとりあえず一緒」なのであれば、「意味レイヤーの異なる入り口から同一のメソッドを"とりあえず"呼ぶ」は、十分にありかなぁ、と。
この場合「機能が異なるような変更が入ったらその時点で修正すればいい」ので、共通化してもあんまり困らなくて。
で「共通化して困らない」のであれば、まぁ「同じ機能のコード」を、コピペするよりはまぁ共通化したほうが、色々と塩梅もよろしかろう、と。


というわけで。
基本的に「意味レイヤーの共通化」はすべきだし、逆に「意味レイヤーが異なる」のであれば、同一処理であっても一旦、最低限メソッドなりクラスなりは切り分けるべきです。
ただ「同じ処理」であれば、意味レイヤーの奥で「同じ機能レイヤーをcallする」のは普通にありなんじゃないかなぁ、と。楽だし。


で、少し局所的なお話し。
「異なるformの、必須かどうかをチェックするロジックはまとめたい」ってのは、例えばおいちゃんならこんな風にやるかなぁ、的な一例。


まずは「オリジナルなModelの基底クラス」を作るです。
そこここのフレームワークでまぁ大抵「Model」ってクラス名だと思うので、そこを前提に

class gallu_model extends model {
  // 必須チェック(とりあえずPOST前提)
  // 一つでも空があればfalse、全部埋まってればtrueをreturn
  public function validate_isempty_main(array $params){
    // 「このパラメタが空だったよ!」配列を一旦初期化
    $this->empty_params_ = array();

    // 指定されたパラメタをチェックする
    foreach($params as $p) {
      if (true === empty($_POST[$p])) {
        // 内容が空っぽだったら「空だったよ!」配列に入れておく
        $this->empty_params_[] = $p;
      }
    }

    // 「空だったよ!」配列が空っぽのままならtrue(全部埋まってるよ!)
    if (true === empty($this->empty_params_)) {
      return true;
    }
    // else
    // 空っぽのデータがあったよ orz
    return false;
  }

  // 「空だったよ!」配列を取得
  public function get_empty_params() { return $this->empty_params_; }

//private:
private $empty_params_;
}


こんなのを作っておいて。
後は、個別のmodelで…例えばloginのmodelであれば

class login_model extends gallu_model {
  // validate
  public function validate_isempty(){
    // とりあえず「必須項目」のリストを作る
    $params = array('id', 'pass');

    // validate本体をcall
    return $this->validate_isempty_main($params);
  }
}


ユーザ登録であれば

class user_model extends gallu_model {
  // validate
  public function validate_isempty(){
    // とりあえず「必須項目」のリストを作る
    $params = array('id', 'pass', 'name');

    // validate本体をcall
    return $this->validate_isempty_main($params);
  }
}


とかってやると、ある程度
・意味で共通化できて
・ついでに機能の共通化も出来る
のかなぁ、などと。


まぁ「どの処理をcontrollerに書いてどの処理をmodelに書くか(viewに処理を書く輩に禍と呪いあれ)」ってのは色々議論があるところだと思うのですが。
おいちゃんは、まぁいわゆる「PHPフレームワークMVC」であれば、上述のような感じが割合とお好みかなぁ、と。