2つのインタフェースと1つの共通インタフェースをめぐる失敗録

HTMLタグのホワイトリスト実装にはまって半日を無駄にしてしまった。ありがちな、汎用性を求めすぎた結果だった。

実装したかったこと

  • テキストボックスに入力された文字列中の使用HTMLタグホワイトリストチェック
  • パースは別途ライブラリがする
  • 要求は以下
    • 「特定の属性必須」
    • 「特定の属性はあってもなくても良い」
    • 「特定の属性値は指定の正規表現にマッチすること」
    • 「指定した属性以外は許容しないこと」
    • 上記4条件を組み合わせたタグを定義すること
    • 同じタグに対して、複数のホワイトリストが定義できること。(例:<span style="color:red;"><span style="text-decoration:underline;">のみ許可)

最初にしようとしてつぶれたこと

言語はJavaJavaといえばインタフェース。

デザインパターンでいうDecoratorやComposerのようなことをしようとして、以下のインタフェースを定義してみた。

public interface TagMatcher {
   /**
    * 渡されたHTMLタグがマッチングルールに適合するかを判定します。
    * @param el HTMLタグ
    * @return 成否
    */
   public boolean isMatch(Element el);
}

これ自体は問題なかった。呼び出し元からバインドする事項は高々これ1つだった。

まず、「属性-属性値を1つ検証するルール」「無条件でtrueとするルール」など単発のルールを実装して、 それらをDecoratorする形で「配下に持つ複数のルールのand(or)をとるルール」「配下のルールを否定するルール」を作った。

ぶちあたったのはもう一つのインタフェース。

このホワイトリストは元のXMLから与えらえる引数によって引数が定義される仕様だった。 例として、先ほどの2つのspanタグの定義は、以下のように与えられる。

span[style="color:red"],span[style="text-decoration:underline;"]

なるほど単純な繰り返しで、パース自体は比較的簡単に可能だ。

しかしつなげてみると

ホワイトリスト定義
  -> (ホワイトリストを分解して汎用データ構造に仕立てるロジック)
  -> 汎用データ構造
  -> (汎用データ構造をもとに、やりたい検証内容を仕立てるロジック)
  -> 検証結果

というロジックになってしまった。言い換えるなら以下だ。

インタフェースA
  -> (インタフェースA → 汎用データ構造の変換処理)
  -> 汎用データ構造
  -> (汎用データ構造 → インタフェースBの変換処理)
  -> インタフェースB

公約数

例えて言うと、ここでいう「汎用データ構造」は公約数を因数にする数のようなものだと考えている。

インタフェース間で受け渡す情報量や表現力を増やすには、公約数を大きくせねばらなない。 乗除を減らすにはインタフェースA-Bの公約数を増やせばいい話だが、 この約数は「2」とか「10」とかのcommonな分かりやすい数が好ましい。 (例えば「19」とか「113」とか、メタったルールを約数にすると嫌なコードになる)

実装したいことと今までに積み上げたことを整理してみる。

  • インタフェースA(ホワイトリスト定義)で定義する内容は結構多い
  • 共通データ構造(isMatch)にバインドする内容は結構少ない
  • インタフェースBを通して呼び出し元がしたい内容は結構多い

言い換えると

  • インタフェースAの数はでかい
  • 公約数が小さい
  • インタフェースBの数はでかい

これを守った上で取る施策は以下になる。

  1. 汎用データ構造をインタフェースB寄りにする(インタフェースBの約数を増やす。インタフェースA → 汎用データ構造の変換処理のボリュームを積む)
  2. 汎用データ構造 → インタフェースBの変換処理のボリュームを積む(約数を単純にする。)

どうあがいてもロジックに影響が出る。また、互いの機能が連携しあうため、修正が入るならばどちらか膨らんだほうのロジックに手を出さなければならない。

思い返せば、典型的なインビーダンスミスマッチだった。各両端のインタフェースの公約数、なにより内部の汎用データ構造が1つのインタフェースであると見抜けなかった。

顛末

結局、インタフェースや汎用性の美しさを捨てて、古典的な業務モデルクラスを設計した。 気を付けたのは約数の種類、具体的には業務的ルールに関する構造と言語特性・データ構造に関する構造をなるべく共通化することだった。 公約数を大きくした。

共通データ構造を新しいインタフェースで利用したり、互いのインタフェースの因数が非常に多い場合、小さな公約数にも利があったと思う。 が、局所実装は結局仕様変更によって捨てられるコードであり、まだ300行に満たない共通実装はインタフェース変更を受けるし多少の汚れがあっても十分読めるはず。。願わくば今のきれいなままで死んでほしいけれどもきっと叶わないだろう。

モダンなブラウザクライアントJavaScript開発マネージャjspm

遅れながらjspmに触った。

JavaScriptのパッケージマネージャ、ブラウザ未サポートの新鋭機能をES5にしてくれるようなトランスレータ、おまけにバンドルも勝手にしてくれるすごいやつだ。 System.jsとの併用をオススメしているが、ぶっちゃけるといらないぐらい便利だ。

ES6以降のJavaScriptは最初が面倒くさい

今現在、新しめのJavaScriptの環境で開発しようとすると、開発環境づくりが面倒くさい。

理由として、ちょっと昔話。

ES6以降、JavaScriptは複数ソースコード間の連携についてやっと取り込み始めたが、ES5既存そのまま開発者が使いだすと、色々問題がでてくる。

ひとつが、新機能文法の実装が、各ブラウザの足並みがそろわないこと。(新鋭のものでも、結構遅い。古いIEなんかは実装すらしない)

ふたつめに、ファイルを分割しまくると、下手すると数百個のファイルをブラウザがダウンロードすること。(しかもjsごとに依存チェーンを解決しながらなので、ほぼシーケンシャルにファイルをダウンロードすることになる。。)

これらの解決に、色々ツールを使っている(ES6→ES5へのTranslate、1つのjsファイルにするbundleなど)。 相互の設定が非常に面倒くさい。それぞれコマンドラインツールなので、だいたいtask runnerを導入してビルド自動化するが、プロジェクトごとにファイル構成が違ったり、あとスクリプトファイルのメンテも面倒くさい。

jspmが解決してくれること

jspmは、上記でやることを一つのコマンドラインツールにパッケージングしたツールだ。 つまり、ツールの個別インストール+タスクランナーに書かなければいけなかったことを、全部中に隠ぺいしてくれる。

jspm init としたら初期設定とどのTranslator使う?とか設定できたり、コマンド一発でbundleできたりする。 ライブラリの導入も、jspm installとNodeライクにできてしまう。

ES2015+の過渡期、JavaScriptの敷居を下げるのに大いに役立ってほしい。

サンプル

github.com

【Java】javacでcom.sun.xml.internalパッケージをコンパイルする

Javaファイルをコンパイルするにjavacコマンドをよく使うが、この中でデフォルトではcom.sun.xml.internalパッケージを無視する。

unageanu.hatenablog.com

上記ブログではclasspathに明示的にrt.jarを入れるとあるが、 Ant経由だとどうもだめみたいだ。。

ちゃんと有効にするフラグがあるので紹介。

stackoverflow.com

javac -XDignore.symbol.file Test.java

【Docker】Volumeが消えない

Dockerを色々触ってる。

Volume関連で探し物をしていたら、たまたまこんなエントリを見つけた。

qiita.com

Volumeをマウントしているコンテナを削除しても、Dockerの中で確保したVolumeのリソースかなんかは残るらしい。

併せて、削除用のスクリプトも掲載されているので、自前のメモに書いておく。

2016/10/25追記

docker rm -v <コンテナ>で一緒に消えるらしい

【bash】ls、grepの「引数が長すぎます」をawkで回避

黒魔術っぽくて敬遠していたawkをちょっと触ってみると、やっぱり黒魔術だった

github.com

# 基本1 標準出力を空白文字区切りで分割し、後のスクリプトっぽい部分で一つ目なら$1、二つ目なら$2と書く。
ls foo | awk '{print $1 $2}'

# 基本2 改行の分だけ動作を繰り返す
# ls -1 ... ファイルの一覧を改行区切りで標準出力
ls -1 foo | awk '{print $1}'

# 基本3 スクリプトっぽい部分"{ ... }"は bashではない。
# 独自(C言語っぽい?)文法。
ls -1 foo | awk '{
   print "----- " $1 " -----";
   # コマンドの字句を作成し、変数に保存
   command = sprintf("cat foo/%s", $1);
   
   # bash実行。
   buf = system(command);
   # ファイルディスクプリタを閉じる
   close(command);
   print "";
}'

# 基本4 演算できる。なんだこの気持ち悪いインタプリタ
echo 1 | awk '{ print $1 + 1 * 2 }'

# 基本5 if もループもできる。
echo 1 | awk '{
  for (i=1; i<100; i++)
  {
    sum += i;
    if (sum >= 100) {
      break;
    }
  }
  print "i:" i " sum:" sum;
}'

スクリプト置くならえんやこら手続きがいるけど、手順書に乗っけるだけならば検証作業はいりませんなぁ(ゲス顔) その代わり、結構遅いんやねこれ。。

本題

grep "" dir/*/*/*/*で、400byteのファイル×10万個ぐらいひっかけると、「引数が長すぎます」といわれて死んだ。 awkを使って、動的にコマンドを生成しつつ回避。

github.com

#/bin/bash

# ファイルが 数万個のところでlsとかすると、「引数が長すぎます」とエラーが出る。
# awkを使って疑似回避。
# 非常に重たい。
ls -1d msgs/*/* | awk '{
  command = sprintf("grep ERROR %s/*/*", $1);
  system(command);
  close(command);
}'

CEATEC 2016いってきた

日本なんでもIT展覧会 CEATEC 2016、いってきました。

f:id:packpak:20161008183355j:plain

今年のキーワードはズバリ「IoT」。 去年もIoTの前哨戦があり、各企業ができる限りの方向性を展示したのち1年あけての成果報告会といったところ。

エリアが2つに分かれていて、自分は技術出展のほうをメインで回っていた。 一般客というよりかは、ほんとうに企業人や学生、研修、研究者が主。 手を取りあうチャンスでもあってマニアックなブースが多く、非常に見ごたえのある展覧会だった。

IoT製品小型化は十分?

今回のテーマ「IoT」を飾るごとく、IoT関連の出展がいっぱいいっぱい。

まず大きく一角を占めていたのが、部品や端子といったハードウェア組み込みモジュールの展示エリア。

f:id:packpak:20161008184235j:plain 製品のバリエーションの広さがIoTを用いたソリューションの新規性や安定した事業の難しさを表してた一方、 何より興味深かったのが製品のパターンが明らかに見て取れたことだ。

  • 既存の部品を小型化(アクチュエータ、センサー、コンデンサレベルのものも)
  • ワイヤレスモジュールのIoT向け適応(小型化、省電力化、低電力規格の適応)
  • 電源、電池の課題(特に小型化と容量の確保。ワイヤレス給電もみたかな)

どこにでも適応できるようにするにはもちろん、どうやって、自立稼働できるようになるか、メンテのコストが下げれるようになるか、の創意工夫がいくつも見られた。

製品を見る限り、小型は十分なんだろうか?例えば倉庫で稼働する製品なんてオーバーキル相当だし(単価はどうかしらんけど)、一体型で製造したらもうなんでもはっつけれるサイズになりそう。

残る課題は電力と電源、あとは長期間の使用と稼働維持がどうなんだろう。例えば農業で使う場合、それなりの耐性が必要なはずだ。 (話を聞いたらちょっとはわかったんだけど、時間がなくても見るだけでスルーしていた。。)

IoT自体はサービス貧乏

IoT関連のソリューションをいくつか見た。

f:id:packpak:20161008194755j:plain

・・・が、この辺りはあまり見ごたえがなかった。 どれもこれも要約すると「IoTで無人で監視して、画面から管理者が一覧を見る」または「開発を支援するためのソリューション」にことにつきる。

考えてみればそうだ。

機械を小型化して色んなところに適応するっていうのは、つまり自動で何かやってくれることを期待するわけで、IoTはついでにネット繋げて遠距離間で連携しあうとこがミソだけど、ネットワーク形態ってクライアントサーバ(Master-Slave)とP2Pぐらいしかない。 で、今のIoTでサーバーとか自律した機能を持たせると電力がもたず、必然的にクライアントになってしまう。

IoT製品自体が(または単独で)ユーザーにメリットを与えるのは非常に難しく、バックにソフトウェアサービスがあってこそ初めて現実的な事業になる。

ということは、2つに1つだ

  • スマホを司令塔(または電波拠点)にしたユーザーに近いところで稼働する製品
  • いわゆる工業的な製品。自身に遠距離の通信モジュールを組み込んで、特定の拠点とやり取りする。

で、それぞれのモジュールがやることは

  • わざわざ人が出向くor機材を使って測定することを自動測定する。
  • 簡易なインタフェースでユーザーに断片的な情報を通知する。

ぐらいだ。それは、要するに人が出向くことがコストに合わないような用途に適応することに等しい。(コスト的不可の解決)

「IoTができる製品を作る」だけでは儲からないのだ。 今回の展示があるように、情報量を集めて規模を食えるサービスを別途開発して売ること、はたまたIoTに必要な商材を買う一次生産者的なビジネスが最もパイが大きい。 一次生産者は無理なので、「IoTを作る」「バックエンドのサービスも作る」両者を満たす業態が今後増えていくはずだ。

AIとデータサイエンティストの昨今

技術ブースであまり数はなかったんだけど、 IoTで集めた情報をまとめる位置づけとしてAI関連の出展がちらほら見られた。

AIといっても、「ニューラルネットワークによる学習」「アルゴリズム実装による模擬的な人工知能」「データサイエンス」がごっちゃになっている感じだけど。。(小うるさいって?)

さすがにこちらはまだまだ研究しか出ていなく、大学などが未来を感じる研究成果を発表する程度だった。

データをまとめて価値を出す、という位置づけてデータサイエンスの事例がいくつか見られた。(名前わからないけど、リクルートの方の講演もちょっぴり見た。) 人材が足りていないと噂の通り、競争の産物というよりは今の事業や作業をいかに正規化・標準化するかに絞った成果が要だったが、じゃあ人が増えて、シビアな事業になって、っていう環境はまだ遠く予測できない印象だった。弁護士みたいにならなければいいけれど・・・。

AIやデータサイエンス分野とIoTは、端っこからのデータ生産者・データ統計からのサービス生産者で一見組み合わせられるように見えるが、 IoTで生産したデータを直接突っ込んだところで、役に立つのは、気象温度といった定期的に測定するものや一部の研究分野に限られるんだろう。 公共分野で近い開発をする場合、ちょっと知識あったほうがいいのかな。

海外勢の存在感

成約ありきのブースを展開していた日本人の傍ら、海外からの出展社も多くみられた。

中国や台湾は10年前や昭和を思わせる製品をひっさげてきたが、その価格が衝撃的だった。3Dプリンタ、1台20,000円。 まさにここの勝負だ、ということが見て取れる。

ほかにも海外の研究や事業のブースが、ぞんざいな扱いだが一角にあったが、 内容が非常に面白い。

3DSignalsという企業のプロジェクト概要から抜粋すると 「超音波データを収集するIoTセンサーと産業機械のアコースティックエミッションをビッグデータ解析するプラットフォームを開発している。」 とのことだ。

つまり、超音波から産業機械の状態を取得して、異常パターンの検出をアラートであげるプロジェクト。 非常に先進的で唸った。モジュールありき、他社連携ありき、既存路線の拡張が張り出される日本語の中、本当に挑戦的な内容が書きだされてあった。

ブースに存在感はなかったが、アイディアは本物だ。後ろにいるのはもっとでかいんだろう。

今年はサプライズなし

各企業や事業の足並みがそろいつつ、着実に次世代まで進んでいることが目に見えてわかったが、サプライズがあるような出展がなかった。今年は平日のみの開催となった意図なのだろうか。

もし、来年も名刺交換会になるようであれば、 行くのはちょっとためらう。

実行ファイルがパスのどこにあるか探す

TypeScript 2.0.3をインストールしたのに新構文が有効にならない。

tsc -version

Version 1.7.2

あれっ?

whereコマンド

Windowsならwhereコマンド、Linuxならwhereisコマンドを叩く。

where tsc

e:\Users\yu\AppData\Roaming\npm\tsc
e:\Users\yu\AppData\Roaming\npm\tsc.cmd
C:\Users\yu\AppData\Roaming\npm\tsc
C:\Users\yu\AppData\Roaming\npm\tsc.cmd

ぐぬぬ