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

2017-07-22

[][][]多言語対応のあれこれ

ふと生徒さんに質問をいただいたのもあって。

ちょうどよいきっかけになったので、せっかくなんでBlogで。


本質的には「どの言語のどの領域」でもある程度応用が利くかと思われますが。

一応、おいちゃんの記述なんで「MySQLPHPつかったWebアプリケーション」をど真ん中に据えて、ってな感じで。


まず「ユーザからの入力について多言語対応したい」は簡単で「保存するデータ及びHTMLUTF-8にしましょう」で、fin。

いやまぁUnicodeであれば大体無問題だと思われるのですが*1HTMLとかでUTF-16って、あんまり見た記憶がないんですよねぇその辺詳しい諸氏の突っ込み求む。

ひとつポイントがあるとすると「MySQL文字コードは、utf8じゃなくてutf8mb4」ってあたりなのですが、そのためにはMySQL5.5.3+というバージョンが必要なのでそれより低いバージョンの場合はバージョンをあげましょうここに慈悲はない。


お次に「プログラム的に動的な文字の出力」をどうするか、ですが。例えば「お知らせ」とか。

この辺あたりから少し面倒になるのですがおおむね

・ユーザが「表示してほしい」言語を忖度する

忖度した結果としての「選択された言語」のデータを引っ張ってくる

ってまぁ、こんな感じ。


まぁ最終的には「ユーザに選んでもらう」でよいですし、選んだ結果は「Cookieあたりに保存」しておけばよいのですが。

初手のアクセスで「きっとこの人は日本語圏の人なのではなかろうか?」を推測したいのであれば、HTTP RequestヘッダのAcecpt-Languageを見ると、比較的、ヒントがあったり。

PHPの場合

$_SERVER['HTTP_ACCEPT_LANGUAGE']

で取得可能。帰ってくる値は、例えば

"ja,en-US;q=0.7,en;q=0.3"

ってな感じなので。初手にjaがあったら「なんとなくこの人、日本語圏の人なのではないだろうか?」と推測が可能。

それ以外は適宜しらべて。en-USとかきたら英語圏だし、それ以外で斜めに調べた限りだと「de (ドイツ語)」「es (スペイン語)」「it (イタリア語)」など。

後ろの「q=0.7,en;q=0.3」にも本来的には意味があるので、興味がある諸氏は適宜しらべたし(大まかには、各言語の優先確率)。

雑に行くんなら「先頭2文字で判断」でも、当面は困らないんじゃないかなぁ切り出しておいて問題が起きたら修正すればいいんだし(雑)。


で、あとは例えば「お知らせ」なら、お知らせテーブルに「言語」とかいうカラムをつけておいて

ja: お知らせです。

en: It is news

it:saluti

とかって感じでデータを用意して出力すれば、それでOKな感じ。どっちかってぇと「各言語のコンテンツ」用意するのが面倒だよねぇ、的な。

でもまぁそれはコンテンツ用意する人の問題なので、サイトの骨格であるシステム作成のおいちゃん的にはいったん気にしないw


さて割と一番大きな本題「HTMLなどの静的*2なファイルの文字」をどうするか、ですが。

大枠として2種類あって、かつその2種類にはそれぞれ亜種がいくつか存在します、ので、それぞれ、ある程度(もしくは簡単に触りだけ)説明をしていきたいかなぁ、ってのが、本文章の趣旨。


大まかには

テンプレートを切り替える

・出力文字を切り替える

の2種類。

それぞれ、少しかみ砕いて。


テンプレートを切り替える

いやまぁそのまんまなのですが。

例えば「ログイン」Pageがあるとして、ボタンに「ログイン」とか日本語で書かれると、英語圏の人は多分いろいろと困るです。

……いや日本のサイトで「login」って書いてあっても困らない気がビシバシとするのですが、その辺は置いといて。


てっとり早いのは「英語圏用のテンプレート」と「日本語圏用のテンプレート」とを別々に用意して出力を切り替える、って方法がありまして。

対応言語数が少ない&ページ数が少ない&更新頻度が低い(更新そのものが少ない)のであれば、割と手っ取り早い解決策だと思われます。


方法としては

・言語ごとにディレクトリを分ける

・言語ごとに拡張子を分ける

って方法がありまして。


例えば(Smartyチックに)login.tpl、ってテンプレートがあるとしますと。

ディレクトリで分ける」パターンであれば、

templates/ja/login.tpl

templates/en/login.tpl

って風に入れて。

拡張子で分けるのであれば、(Smarty的に「本当の意味での拡張子」はいじると面倒なんで)

templates/login.ja.tpl

templates/login.en.tpl

って感じにすると、切り分けられます。


上述のような切り替えの作業は、どこか一か所にまとめておくといろいろと楽ですよね。

うちのフレームワーク(MagicWeapon)であれば、viewクラスのmake_template_filename()メソッドを上書きして、って感じかなぁ。

まぁ大体の*3コードであれば、どこかしら「テンプレートのファイル名を取得する」的な一点があると思うので、そこを「キュッ」と絞めると、いけると思います。


出力文字を切り替える

対応言語数が多い&ページ数が多い&更新頻度が高いのであれば、「出力文字による切り替え」を想定したほうが楽かもしれません……初手面倒ですが。

端的には

・各言語用の翻訳ファイルを用意して

プログラムを通して文字列を変換する

となります。


これにも方法がいくつかあって、大まかには

サーバサイドで自力実装

クライアントサイドで自力実装

・「gettext」ってのが割とあちこちの言語でライブラリとして存在するので、それを使う

のいずれか、になります。


共通があるのでgettextがよさそうなもんですが…それなりに使い方とかお作法とか癖とかがあるので。

PHPの場合は「インストール」も必要ですしねぇ。

前提条件や癖やそのあたりが「呑み込めそう」なら、gettextを使ってみるとよいんじゃないかなぁ、と思います。

xgettextとか面白いんだけどなぁ……「テンプレートエンジンを使う」前提だと、幾分、ハードルが上がったり諸々が以下略。


自力で作る場合は、おおむね

・辞書のフォーマットを決める

・辞書ファイルを必要言語数だけ作る

テンプレートに辞書ファイルをぶつけて出力する

といった感じ。


んと……ざっぱに、例。

例えば辞書ファイルを「コード: 翻訳文」とします。

日本語と英語を用意してみませう。


辞書.ja

login_button: ログイン

login_text: こちらからログインしてください。

password_reminder_text: パスワードがわからない場合はこのボタンを押してください。

password_reminder_button: パスワードリマインダ


辞書.en

login_button: login

login_text: Please log in from here.

password_reminder_text: Please press this button if you do not know the password.

password_reminder_button: Password reminder

*4


いろいろと面倒なんでいったんSmarty前提。

まずPHP本体側で、「言語にそった辞書」を渡します。

辞書は、key=コード、value=翻訳文、のhash配列方式で情報があると仮定

// 辞書の選択
if (日本語圏なら) {
  $辞書配列 = 日本語の辞書;
} else if (英語圏なら) {
  $辞書配列 = 英語の辞書;
} else {
    // 例外でもぶん投げるかねぇ
}

// 辞書をアサイン
$smarty_obj->assign('dic', $辞書配列);

んで、Smartyでは、例えばこんな風に記述します。

{$div.login_text}
<form ...>


<button>{$dic.login_button}</button>
</form>

こんな風にしてテンプレートに「一切出力用の自然言語を書かずに」辞書ファイルに追い出すと、まぁいろいろとできたり出来たりします。ざっくりとは。


結論

がっつりと多言語対応って、割と案件数的にも少ない気がするので。

調べると、案外とネットの情報も少ないんですよねぇ……なので、書いてみた。


なんかほかにも手法ありそうなのですが、まずは「こんなのもあるよ〜」的に。

突っ込みとかあったら突っ込んでくださいませ > 諸氏

*1UTF-7? なにそれ美味しいの?

*2:政敵って変換するFEPについてどう思う?

*3:まっとうな

*4:英訳への突っ込みはこれを禁止するwww

2016-06-06

[][]「後で」っていつだろう?

ものすごく直近としては、teratailでの、とある質問(複数)&他の人の回答を見たあたりで


セキュリティ的にシャレにならんもんが書いてある

・一瞬、突っ込もうか悩む

・「否定的な返答の可能性」が脳裏に横切って、面倒なんで放置


ってのを大体脳内で1秒くらいでやりとりしたあたりが直近なのですが。

うんまぁネタ自体は「わりとあちこちに転がっている」ので。


割とよくあるのですが

・コード(設計)上の、(はっきり言うとかなり初歩的な)セキュリティホールを発見

・指摘

・「一端動かすのが大事なのです! セキュリティは後で直すのです!」という返答

という一連のやり取り。


指摘については「やんわり」から「がっつり」までバリエーション試してるので、多分どれでやっても一緒なんだろうなぁ、という印象値。

で思うのだけど「後でっていつよ?」


ちなみにいくつかの案件でみた「後で」のその後の経過って、大体ワンパターンで。


・初めは「一端動かすのが大事!」という理由でセキュリティドン無視

・サービスが動き出す

セキュリティを考慮するには、ある程度の工数が必要(な程度に汚い作りと設計になってる)

・「そんなコストは現在かけられない。あとで余裕がでたらかける」といってセキュリティ無視


って流れで固定。

まぁ「リスクが見えない人」にとって、セキュリティにかけるコストは無駄だわなぁ。


もちろん可能性とし「ちゃんと後日の修正コストを見越しての、設計とコーディングスケジューリング」が出来ているところが0とは言わない。

でも、例えば「通信インタフェースの設計でミスってる」場合、そのレイヤーから修正するのって、かなり手間ぢゃない? って思うざますの。


根っこにあるのは「勉強不足」と「知識不足」。

その不足が「学習が足りない」のか「発展途上」なのかはまた別議論なんだけど。「学習が足りない」んなら正直「そのレベルで金もらって仕事すんな」って感じだし、発展途上なら「今このタイミングで学ぼうよ後回しなんてしないでさ」って思う。

それを「後で」って棚に上げる時点で、なんていうか「どんなもんなのかねぇ?」と。


なので。

セキュリティは一端無視」って言われた時点で、おいちゃんのプロジェクトでないのなら「あぁそうですか(微笑み)」で終了だし、おいちゃんのプロジェクトなら…多分、言った人間切って終わり、かなぁ(苦笑


以上、ふと思いだしたので、戯言程度に。

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から。

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

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


コメントの書式

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

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


変数の宣言と型の有無

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

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


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

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


メモリ管理の有無

まぁ「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-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