がるの健忘録 このページをアンテナに追加 RSSフィード

2016-05-17

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

ちと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」であれば、上述のような感じが割合とお好みかなぁ、と。

2016-05-04

[][]他言語やる時のラーニングパターン 基本変

前提として「なんか1〜2つくらいの言語はある程度の所まで扱える」前提です。

「ある程度」ってのも難しいところなのだけど、ざっくりと書くと大体「数年以上、くらい、まじめに学習しつつ実務やってる」くらいかなぁなんとなく。

基本的に「手続き型言語及びその延長線」が想定範囲です。「関数型」はあんまり入れていないので、その辺も注意。

まぁまだ、実務だと手続き型のほうが圧倒的多数だとは思うんですがねぇ………多分。


準備

「信頼できる」一次情報の場所を把握しましょう。

その言語を開発/制定しているところ、ないしそこに「限りなく近いところ」がお勧め。

「人気があるBlog」とか「人気のある書籍」は、「なぜ人気なのか」によってお勧め出来たり出来なかったりするので、人気よりは「一次情報からの距離」で選択をしたほうが安全です。


初手

まずはその言語でHello Worldから。

「出力の仕方」と「実行環境整え」ってのが主眼。

さりげに「最後の改行」とかで癖があったりするので注意。


コメントの書式

コメントは重要なんで、とりあえず書式くらい把握しておきましょう。

「その言語にとってのポピュラーなコメント形式(例:JavaJavaDoc形式)」を合わせて把握しておくと、色々と便利です。


変数宣言と型の有無

一旦いわゆる「プリミティブな型(≒配列以外)」を把握。

あと「変数を出力する」方法を把握。「変数の前後に勝手に空白だぁの改行だぁの」が入るケースがあるので「そーゆーのを入れない方法」を把握しておくと、後でひょんな時に楽が出来ます。


文字<->数値変換(型有り言語の場合)

型あり言語の場合、どのみちやることになる可能性が高いので、文字列と数値との交換方法を把握しておく。


メモリ管理の有無

まぁ「C / C++」か「それ以外」か、の分岐のような気がひしひしとせんでもないのですが。

「自分でメモリを管理する」系なのか、そうじゃないのか、簡単に把握しておきましょう。


ポインタポインタ(参照の参照)」が持てるか持てないか

ネストしたポインタ(参照)が持てるかどうか、は、以降の色々なところで引っかかってきたりします。

おいちゃんの手元で近しいあたりだと、C、C++はもちろん持てるのですが、Perlも可能なので、気をつけないと「根の深いネスト」に苦しめられます(苦笑

PHPは「持てない」のと、この手の場合大抵「変数の持ち方に言語なりの工夫がある」ので、そーゆー言語で下手に「意図的な参照渡し」とかすると地獄経由悲劇行きになりやすいので注意しませう。


if文の把握とswitch文の有無の把握

ifはまぁ大抵あるので、else / elseif 含め、書式を把握。

switchは合ったり無かったりするので、まず有無から確認。

「波括弧の有無」とかも一応含むけど、書くほどのお話でもないよね?


文字列比較の確認

数値の比較はわりと「どこも似たり寄ったり」なのですが。

一方で、文字列の比較は、割合と各言語によって方言があったり癖があったりするので、確認をしておきませう。

特に「オブジェクトの強い系の言語」の場合、気をつけないと「文字列の一致ではなくて、インスタンスの一致」を見ちゃったりする可能性があるので。

「(違うインスタンスでも)文字列的に同一なら一致」の方法は、ちゃんと把握しておきましょう。

この辺、後述の「イミュータブルな変数」とかにも引っかかってくるケースがあります。


loopの把握

いわゆるfor / while文系なのですが。

C言語のfor文」がポピュラーなのか、それとも「Range-based for loop」なforがポピュラーなのか、まずその言語の「スタンダード」を把握しておきませう。

Range-based for loop系の場合「一定範囲の数値を作る方法(大抵は、generator)」を合わせて捕捉しておきましょう。


配列の把握

「どんなタイプの配列を持っているか」は、意外と言語毎に異なるので、一通り「配列群」を総ざらえで把握しておくとよいでしょう。

また、種類が豊富であれば豊富であるほど「理由がある」もんなので、ある程度使い分けられるようにする…か、ド最悪「とりあえずコレ使おう」っていう目星をつけておきましょう。


イミュータブルな変数」の把握

配列の把握に似ているっていうかベン図的に重なる部分があるのですが。

言語毎に、部分的に「この変数イミュータブル(不変)な!」っていうモノがあったりして、安全性の観点から「使う事が推奨される」事も多々あったりするので、押さえておきましょう。

C++constは、一旦この辺の範疇としておきます(C++以外で、引数とか戻り値とかのconstとかってあるのかしらん??)


関数とクラスの書式

この辺は比較的似たり寄ったりになりやすいんだけど(without JavaScript)、とりあえず基本的な記法を把握。

ちなみにどんな言語であっても基本的に「1ファイル1クラス、ファイル名はクラス名」ってのは揃えておいたほうが圧倒的に無難です。

あと「関数(クラス)の中に関数(クラス)が書けるかどうか」「無名関数(含むラムダ式)、無名クラスは作成可能か」「関数引数として使えるか」とかあたりは調べておくと無難です。

またこの辺は言語毎に「同じ単語を違う意味で使っている」ケースが増える箇所なので、「その言語におけるこの用語の意味」を、一度ちゃんと調べ直しておくと、変な先入観で死なずに済みます。


「破壊的[メソッド|関数]」に対する印象値

極論大まかに「メモリを死守する系は破壊的関数を是としたがる傾向があり」「安全性とかを死守する系は破壊的メソッドに殺意を抱く傾向がある」と思われるのですが*1

やり方を知る事も大切ですが、それ以上に「その言語はどちらをより好む傾向があるのか」を大まかに把握しておくと、変なトラップに引っかかりにくくなります。


変数のスコープの確認

大まかには「関数スコープ」なのか「ブロックスコープ」なのか。

あと、スコープの外側に変数がある場合に「見れない」のか「繰り上がるのか」とか、変数宣言の仕方によっては「関数内で宣言しているのにglobalになっちゃうよママン orz」とか(例:JavaScript)。

そのあたりはしっかりと押さえておきましょう。


クラスの継承とかの書式の確認

継承(extend)とかMixinとかtraitとかinterfaceとかabstractとかvirtualとかnamespaceとか、あとついでにアクセス修飾子(private / protected / public)の有無(Perlにはないんだ…)とか、そこら辺の「少し突っ込んだ」概念を把握しておきましょう。


その言語独自の演算子の確認

結構「その言語固有、ないしそれに近しい演算子」はあるので。

演算子は一旦、レジュメ的に総ざらいして「参照出来る」状態にしておくと取りあえず楽です。


ここら辺くらいまで来ると、多分「とりあえずその言語で書かれたソースコードが大体読める」んじゃなかろうかと思われます。

なので、ここら辺くらいまできたら、数本くらい「その言語で書かれたあんまり長くないコード」を読んで、不明点を逐次調べていくと、大体「とりあえず書く」くらいまでならあんまり困らなくなると思うのです。

[][][]他言語やる時のラーニングパターン フレームワーク

新しいフレームワーク触るときの手出しの仕方のパターンを合わせて。


URLルーティング設定の確認と、場合によっては破り方

まぁ大体「書かなくてもデフォルトで動く」ケースが多いのですが(その場合は「まずHello World」へ)。

「明示的な記述が必要な場合」は、まず、1Page分でもよいので、調べて記述しておきましょう。

あと「特別なルール(このURLならこっちの処理にしたい、など)」は、調査コストとの兼ね合いになります…が、ヒネたものでなければある程度フォローされている可能性が高いので、適宜調査して実験して、実装しましょう*2


まずHello World

もっぱら「空っぽの処理無し」+「テンプレートファイルの位置と書式」の確認。

あとは「そもそもそのフレームワークインストールがうまくいってるか?」の確認も込み。


設定ファイルの把握

以降随時チェックにはなると思うのですが(で、大抵の場合、デフォルトで動いたりもするのですが)。

とりあえず

デバッグモードの on/off

DB設定周り

・時刻、言語(文字コード)周りの設定

テンプレートエンジン周りの設定

あたりは最低限確認をしておくとよいです。


用語の確認

特に昨今の場合、MVCないしそれに近しいお名前はちょいちょいと見受けられる…のですが、特にCとM(中でもM)の意味が、言語によってフレームワークによって、下手すると本気で「別物ぢゃん!!」レベルで違うので。

新しいところに言ったら、一旦心の中のお茶碗を空っぽにして、「その言語/フレームワークにおけるこの用語の意味」を、知っている単語ほど、再確認しておきましょう。


テンプレート変数を渡す

大体「単値」「配列(正確にはhash配列配列) )」「if文の書式」の3つが別れば大体いけます。

あと、テンプレートに渡す変数デフォルトで「HTMLエスケープされているかされていないか」の確認。

万が一「されていないがデフォルト」の場合、テンプレートエンジンなりviewクラスなりをラッピングして「デフォルトエスケープ」に倒せるように全力を尽くしましょう!!

デフォルトエスケープの場合、とりあえず「エスケープさせない設定」の方法を調べておきましょう。


DBハンドルの取得

大体の場合「どっかに設定するとどこかでとれる」と思われるので、その「どっか」を把握しましょう。


テーブル名、カラム名、その他制約の確認

特にORマッパー系を使うものの場合、テーブル名やカラム名に一定の制約やルールがあるものがあるので。

その辺は把握しておきましょう。

「そのルールの破り方」は、必要そうなら合わせて調査。ただ「ルールを破るコストが高い」可能性も結構あるので、「ルールに合うように仕様変更が可能」であれば、仕様変更しちゃったほうが、コスト安かもしれないですが。


単純なCRUDの作成

もうちょっと端的には「SQLの発行の仕方(またはORマッパーの使い方)」と、特にSQLを自力発行する場合は「プリペアドステートメント機構の使い方」のあたりをしっかりと把握しましょう。

「複数のやり方がある」可能性が高いので、とりあえず「今回想定しているやり方」だけ押さえておく、でもよいと思いますし、「フレームワークの推奨のやり方」を合わせて押さえておく、というのもよりよいかと思います。


Cookieの設定の仕方とセッション保持のやり方とセッションデータの持ち回りかた

とりあえずセッションは、些か狭義に「サーバ側で情報が保持できる仕組み」と定義しておきます。

クライアントサイドに情報を持ってもらう方法としてのCookie」と「サーバサイドで情報を持たせるためのセッション」の、それぞれの書式を把握しましょう。


「管理画面」の作り方

業務系前提ですが、大抵「frontと管理画面」を作る事になると思うので。

一旦「そのフレームワークにおける、ポピュラーな管理画面の作り方」を把握しておきましょう。

いや「別プロジェクトで全部作る」でもよいのですが、frontと管理画面で「同じクラスとか関数とか設定値とか使いたい」シーンも多いので、それを「全部copyでやる」と些かしんどいのではないかなぁ、と思うので、その辺込みで「ちゃんとしてる」方法を見つけるとよいと思います。


「環境別」設定ファイルの作り方

開発、ステージング、本番で設定ファイルの「一部」が異なる(「一部」は一緒)だと思うので。

可能であれば「環境別のconfigと環境共通のconfig(場合によっては含む定数)」と、それらを「いかに環境別に振り分けるか」の手段を想定しておきましょう。

大体は「環境変数の値でif文でスイッチ」とか多いのですが、おいちゃんは「シンボリックリンクで振り分け」とかやるほうがよりお好みなので、まぁ適宜。



この辺までくると多分「普通のものなら」なんとか書けると思うので。

書けるようになってから「コントローラーのダイエット」とか「ちょっとこまっしゃくれたコード」とか「テクニカルなテクニシャン」とか、考えていくようにしましょう。


以上、もっそおおざっぱですが。

*1:色々大げさ

*2:ヒネたものの場合、調査コストと相談しましょう

2016-05-02

[]PHP小ネタ

filter_inputいる?

こーゆーのがある。

$s = filter_input(INPUT_POST, 'value');
var_dump($s);

興味深くはあるんだけど…デフォルトフィルターは「FILTER_DEFAULT」で事実上のノーフィルタ

警告が出ないのはそれはそれで便利なんだけど、それくらいなら@演算子でも足りるしねぇ。

あと、おいちゃん的には「型が不一致」なのは割合と好まないので。結果的に

// 数値
$i = (int)@$_POST['int'];
var_dump($i);
// 文字列
$s = (int)@$_POST['string'];
var_dump($s);

って切りそろえちゃう事が多いかなぁ。

なので、filter_inputについては「特に使用について目くじらは立てないけど、おいちゃんは今のところあまりメリットを感じていない」かなぁ。


ちなみにデフォルトがある場合…PHP7ならNull coalescing(Null合体)演算子が便利。

$s = $_POST['value'] ?? 'def';
var_dump($s);

PHP5系なら、三項演算子かねぇ。


「参照」で値が入るなぞ

上述をいくつか見ていて偶然見かけた、少し「でんぢゃらす」なコード。

まずは普通のあたりから。

ini_set("display_errors", 1);
error_reporting(-1);

function hoge($awk) {
}

hoge($awk['a']);
var_dump($awk);

実行結果

[gallu@www4142ue ~]$ php t.php

Notice: Undefined variable: awk in /home/furu/t.php on line 9

Notice: Undefined variable: awk in /home/furu/t.php on line 10

NULL

まぁ至極穏当かつ真っ当。


で、でんぢゃらすなコード。

ini_set("display_errors", 1);
error_reporting(-1);

function hoge(&$awk) {
}

hoge($awk['a']);
var_dump($awk);

実行結果

[gallu@www4142ue ~]$ php t.php

array(1) {

["a"]=>

NULL

}

…警告出ないわ勝手にkey差し込まれるわ、見方によっては大惨事(苦笑


まぁこれを書いてあるBlogでも推奨していなかったのでいないとは思うのですがすがすが。

「警告を出さないために引数を参照にする」とかいう破廉恥で奇天烈な行為は、絶対に避けましょうw


…privateの意味は?

これは別所で拝見した、コードの一部。

  public function __get($name) { return $this->$name; }
  public function __set($name, $value) { $this->$name = $value; }

…これやって、プロパティをprivateにする意味あるのかしらん?

これならプロパティを素直にpublicにしたほうがマシな気がするのですが?

次点の可能性として「コーディング規約でプロパティはprivateに」ってのがある可能性が想起されるんだけど、そうだとしたら「こんなマジックメソッド書いちゃ駄目だよ」なんのためにそーゆーコーディング規約になってるか、その辺に思いを馳せないと、と思うのですます。


なので、上述マジックメソッドも「………なんで?」って感じなのですなぁ。

2016-04-22

[]「コメントを書かない言い訳」に関する考察再び

元ネタこちら

プログラムにコメント書かない文化もあるよって話

http://nzmoyasystem.hatenablog.com/entry/no_comment_culture

http://megalodon.jp/2016-0422-0815-58/nzmoyasystem.hatenablog.com/entry/no_comment_culture (魚拓:ものすげぇ念の為)


まぁおいちゃんの結論は「コメントを書け!( http://d.hatena.ne.jp/gallu/20150328/p1 )」で片付くんだけど、そこに至るまでの諸々を、改めてざっくり考察。


筆者が勤めている会社では、原則、プログラムにコメントを書かないのがルールです。

まぁそれが「ルールである限り」、それはそれでよいのかなぁ、と。後先考えなければ。


同僚いわく、「コメントが無くても読めるようなプログラムを書け」という思想が根底にあるのだそう。


適切に関数変数が命名され、スコープがきちんと管理され、ロジックの流れが整理されているコードならば、わざわざコメントを書く必要はない。逆に言えば、処理をプログラムできれいに表現できていないからこそ、コメントが必要になってしまう。だから、うちの会社のプログラムにはコメントが無いんだよ。と、そう教えてくれました。

これは…多分、エピさんが呟いてくださった内容に「全てが込められている」ような気もビシバシとするのですが。

https://twitter.com/epitwit/status/723306702678843392

んむ。それが”なに(what)" かはクラス名/関数名で、"どうやって(how)"はコードそのもので表現する。残るは"なぜ(why)"そうしたかだが、それをコメントに記すのであるよ。

ということ。


コードに書いてあるのは「どうやって」がメインだし、書いてあっても「それがなにか」まで(ちなみにwhatがめちゃくちゃなソースはそれはそれで死ぬるほど見たくないw)。

それの「なぜ」の部分を、どうやって「コメントが無くても読めるようなプログラムを書け」という言葉で賄うというのだろう? 賄えるというのだろう?


しかし、自分自身ですら書いた理由を思い出せないプログラムとは、それ自体欠陥があるように感じられます。

この時点でアウトなのが。

「思い出せない」場合に欠陥がある、というお話。であるとすると「正しく思いだした」のか「歪んで思い出した」のか、そのあたりの差分はどうやったら判定可能なのだろうか?

「思い出す」って単語の時点で「記載されていない内容である」事はまず明白で、その時点で「以前の想定を正しく思い出せたのか」どうかなんて、誰が判断できる?

もう一つより重要なのは。業務で記述したソースコードは本質的に「ほかの誰かがメンテナンスをする可能性が常に想起される」事。

「あながたこう思って書いた」思い出を、あなた以外の他人がどうやって「思い出す」事ができるのざましょ?

このあたりからしても、正直「業務でのプログラミングなのか?」っていうのが、大変に色々と疑問。


コメントのもう一つの弊害は、常にメンテナンスが必要になるという点です。

この辺は「しろ」で終了。

そもそもコードを書く時って「先にコメント」書いたりしない? 思考の流れの整理かねて。


また、メンテナンスの不備によってコメントが嘘になってしまうと、大きな負債になります。コメントにはAと書いてあるけど、実際の処理内容はBだ。これはコメントが間違っているのか? それともプログラムバグっているのか? 関係者に問い合わせたりドキュメントをひっくり返したりと、余計な作業がどんどん発生してしまう。こうなってしまうと、コメント無いほうがよっぽどマシだったってことになっちゃいますね。

これも限りなくバイアスがかかっていて。

「コメントがなく、誤った処理が書かれていて、テストコードも誤っている」場合、同様に負債にしかなりません。

まぁ「じゃぁ、コメントとコードで違うことが書いてある、ことによって誤りが見つかりうる可能性がどれくらいあるか?」っていうとそれはそれで限りなく微妙ですしそれによって「二重防壁」とか考えるタイプではおいちゃんは全くないのですが。

ただ「メンテナンス不備によってコメントが嘘になることで負債になりうるからコメント不要」ってのは、明らかに「ちがうんでないかい?」と思う。


筆者は今の会社に入社して以来、コメントを書かずにプログラムを書く修行をしておりまして、どれだけ可読性の高いコードを書けるかにはかなり敏感になりました。直感的に理解できる命名になっているか。処理の流れに不自然な部分はないか。無駄に条件分岐が増えすぎていないか。こういったことに、以前にもまして気を配るようになっています。

「直感的に理解できる命名になっているか。処理の流れに不自然な部分はないか。無駄に条件分岐が増えすぎていないか。」なんてのは、コメントの有無に限らず「敏感に」ならにゃならんところなので、この辺も大変にもにょり。

ただ、もし「コメントがあるからいいやぁ」っていう感覚の技術者さんがいらっしゃるのであれば。「自分のプライベートなコードで」「秀作として」であれば、そーゆー訓練のしかたも「ありなのではないかなぁ? もしかしたら」ってレベルではあります、が、その程度。


で、後出しの追記に突っ込み。

プログラムで表現しきれない部分は、すべてコミットメッセージやコードレビューのログ、チケットへのコメントなどに書いています。コミットとコードレビューのログはチケットに紐付いており、相互参照が可能です。

それを「コードに書いてはいけない」理由は?


結局コメント書いてるじゃんと思われるかもしれませんが、「なぜこの修正を行ったのか」「修正前後で動作がどう違うのか」などの情報は、プログラムそのものではなくコミットやチケットに付随する情報のため、プログラムのコメントとして残すのは不適切です。

プログラムのコメントとして残すのは不適切です」という記述はあるけど、では「なぜ不適切なのか」が、未記述なので、今一つ。

ちなみにおいちゃんの切り分けとしては

・基本は可能な限りプログラムのコメントで

・「全体を通した経緯」とかは、別所にて(大体はチケットのコメント)

・「削除した」部分の削除理由などはコミットメッセージ

って感じです。

理由は簡単で。「n個の本を行き来しながら読書するのと1冊の本を読書するのと、どっちが楽?」って考え方。

人間って本来的に「あちこちに移動しながら散らかると頭ん中もとっちらかる」ので、どこかに集約したいってぇのは、割と普通の事だと思うのです。

半可通は「あちこちの大量なものを一意に読み込めるオレ優秀!」とかなるみたいですが。


さらにいえば、プログラムのコメントはドキュメントとしての可読性に乏しいので、より詳しい情報を書こうとすればするほど読みにくくなります。ただのプレーンテキストなのですから当然です。それならば、文字の修飾、ファイル添付、ユーザIDのコールなどの機能が使えるチケットにコメントを書くほうが理にかなっているでしょう。

そもそも「plain textで書けない背景ってなによ?」とか思うのですが。

「文字の修飾」? はぁ? なんで必要なの?

「ファイル添付」これはまぁチケットだねぇ。だけどそれは「プログラム要求されているコメント」とかぶるの? それこそ「仕様書」レベルのお話ではなくて?

「ユーザIDのコール」ん…微妙にこれは意味が不明なので(あまりにも複数の事象が想起される)、ノーコメント。


プログラム仕様書にあたるドキュメントについては、詳細かつ簡潔(1チームにつき Word 数十ページ程度)にまとまったものが共有されており、仕様変更が発生した場合には随時メンテナンスが入っています。コーディング担当者がメンテを忘れても、レビュー時にたいてい誰かが指摘してくれるので、メンテナンス漏れが起きることは少ないです。

仕様書にあたるドキュメントがWordであるってのは色々と突っ込みたいんだが、とりあえずそれは置いておくとして、状況としては「原則的には」割と好ましいと思う。

ただ「コーディング担当者がメンテを忘れても、レビュー時にたいてい誰かが指摘してくれるので、メンテナンス漏れが起きることは少ないです。」ってことは、あるんだよね? ってのと。

まぁもちろんそれは「コメント書いたって漏れるときゃ漏れる」んだけど。

コードだと、コードレビューのタイミングで「…あれ? コメントと処理内容食い違ってるぞ?」と一目でわかるので突っ込みやすいのだけど。ここの職場の方は全員「Wordとプログラムを常に交互に見比べている」んだろうか? という疑問。

「常に交互に見比べている」んならよいのだけど、「時々見比べる」だと、時々以外のタイミングの時に「見つかるはずのメンテミスが見つからない可能性」って、ない?


さらに、ソースのコミット後はサーバ上で自動コンパイル、自動テストが走る上、リリース前にも品質チェック部隊による最終テストが実施され、確実な動作を担保しています。

この辺はコメントと全く無関係だよねぇ。


ちぃと興味深いんで改めてBlogを追いかけてみたんだけど…とりあえず今の所「まぁこんなもんかなぁ」くらいの雑感。

プログラムは「その大半が読まれる」ものなので、「出来るだけ読みやすい」ほうが全体としてはお仕事がはかどる可能性が高く。

かつ「読みやすさ」の中には閲覧性とかも含まれるので、今回のBlogの論調であれば「ならおいちゃんはコードにコメントを潤沢に書く」を選ぶかなぁ、とか思ってみたのでした。


あと。

なんで「リーダブルコード」の本が最後に紹介されているんだろ?

あの書籍、「コメント書け! 書け!」ってがっつり書いてあるはずなのだがw

2016-04-18

[]実験:issetとarray_key_existsはどっちが早い?

ちと別件で書く必要があったので、ついでに実験。

具体的には「(第一種)ホワイトリスト」でのチェックを使った、パストラバーサル防御のコードなんだけど。


ちなみに先に一つ。

「検索対象を配列の値に入れてin_array」は下策もいい所なので、いっちょまえの大人がやっていいコードではありません。

「hash配列」の前提になるハッシュ法とかに思いをはせつつ、最低でも「検索対象はkeyに入れる」くらいの嗜みを持ちましょう。


で…本音ぶっちゃけるとですね「existsのスペルがなんでか覚えられない」のよおいちゃん(苦笑

なのでそーゆー理由でissetを乱用するのですが、実際のところ「ど〜なんだろうなぁ?」ってのがあって、ちと調査。


<?php

$awk = array();
for($i = 0; $i < 200000; $i ++) {
  $awk[] = $i;
}

$t = microtime(true);

for($i = 0; $i < 10000; $i ++) {
    $key = mt_rand(0, 199999);
    $j = array_key_exists($key, $awk);
    //$j = isset( $awl[$key] );
}


$t = microtime(true) - $t;
var_dump($t);

PHP5系

[gallu@localhost ~]$ vi t.php

[gallu@localhost ~]$ php t.php

float(0.020144939422607)

[gallu@localhost ~]$ php t.php

float(0.020148038864136)

[gallu@localhost ~]$ php t.php

float(0.019990921020508)

[gallu@localhost ~]$ php t.php

float(0.020182847976685)

[gallu@localhost ~]$ php t.php

float(0.020066022872925)

PHP7系

[gallu@www4320uj ~]$ php t.php

float(0.0041100978851318)

[gallu@www4320uj ~]$ php t.php

float(0.0051558017730713)

[gallu@www4320uj ~]$ php t.php

float(0.0036821365356445)

[gallu@www4320uj ~]$ php t.php

float(0.0027759075164795)

[gallu@www4320uj ~]$ php t.php

float(0.0042760372161865)


<?php

$awk = array();
for($i = 0; $i < 200000; $i ++) {
  $awk[] = $i;
}

$t = microtime(true);

for($i = 0; $i < 10000; $i ++) {
    $key = mt_rand(0, 199999);
    //$j = array_key_exists($key, $awk);
    $j = isset( $awl[$key] );
}


$t = microtime(true) - $t;
var_dump($t);

PHP5系

[gallu@localhost ~]$ php t.php

float(0.011642932891846)

[gallu@localhost ~]$ php t.php

float(0.011620044708252)

[gallu@localhost ~]$ php t.php

float(0.011649131774902)

[gallu@localhost ~]$ php t.php

float(0.011752128601074)

[gallu@localhost ~]$ php t.php

float(0.011624813079834)

PHP7系

[gallu@www4320uj ~]$ php t.php

float(0.0024490356445312)

[gallu@www4320uj ~]$ php t.php

float(0.0030300617218018)

[gallu@www4320uj ~]$ php t.php

float(0.0031118392944336)

[gallu@www4320uj ~]$ php t.php

float(0.002769947052002)

[gallu@www4320uj ~]$ php t.php

float(0.0029900074005127)


ふむぅ…PHP7だと「どっちも差なし」で、PHP5だとむしろissetのほうが優位なのか。

もしかして「issetは本当は言語構造」ってあたりが関連しているのかしらん?


まぁ、これで心置きなく「isset使う」って選択肢が持てるからいいやw