Category Archives: INTER-Mediator

FileMaker Cloudが出たぞ!

突然、FileMaker Cloudがリリースされました。FileMaker Server 15のクラウド版ですが、Amazon Web Serviceで稼働するFileMaker Serverです。CentOS 7ベースです。なんと、カスタムWebは動かないので、INTER-Mediatorでは利用できません。他に、LDAP/Active Directoryのアカウントでのログイン機能もありません。ESSはサポートはしますが、ドライバが非対応ということはちょっと簡単ではなさそう。その他諸々いろんなチェックポイントがあります。

しかし、よく見ると、「FileMaker Cloud is currently only available in the United States and Canada.」と書かれている。日本のお客様はお預け〜!? ってことではいさようならとは行きません。AWSでどうやって日本からのアクセスを切るのかいなと思いつつ、AWSにログインして、リージョンを「バージニア北部」にします。そして、EC2のインスタンスを作って見るわけですが、ここで、AWS Marcketplaceを選択してFileMakerで検索すれば、あらあらちゃんと出て来ますね。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-09-28-0-29-05%ef%bc%882%ef%bc%89

一番規模の小さいのを選ぶと、ちゃんと値段もわかります。この「時間課金」というのはAWSでの一般的な課金方法です。FileMaker社からライセンスを買わなくても、時間課金ができるとは言え、よく見てください。最低の構成でも1時間が1ドル強、上がっても1.229ドル/時間。要するにアマゾンの費用に比べてFileMakerのライセンス料が異様に高いのです。月間30×24=720時間として、732ドル、ということで、月間7〜8万ほどかかります。まあ、こちらを選ぶ人は少ないでしょう。FileMakerからライセンスを買って使ってください。なお、その場合、AMIの選択肢が違います。ライセンスを自分で持っている(BYOL)人向けのAMIがあったりします。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-09-28-0-33-22%ef%bc%882%ef%bc%89

ここで、「お金を払っても試して見るか」と腹をくくったのですが、よく見ると、15日はフリーライセンスということで、Amazon側のEC2使用料のみで、15日は使えます。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-09-28-0-35-36%ef%bc%882%ef%bc%89

そういうわけで、なぜ日本では「入手できない」となっているのか?AWSは全世界に共通のサービスをしているのだから、昔の代理店ビジネスのようなことは最初からできるわけがないことは明白なのにどういうことでしょうね。

ということで、操作等より詳細を知りたい方は、インストール後に出て来たヘルプのページをご覧いただくと早いでしょう。

第1弾は以上!

[開発プロセス#9] コントロールの処理の詳細化

前回までで、バウンダリーを詳細化するところまでを記述したが、またまた時間が空いてしまった。サーバーが起動しなかったり、開発の作業に集中したりなどしていた。

さて、続いて、コントロールの中を詳細化する。実際に詳細化できるのかを検討する前に、なぜ詳細化しなければならないのかを改めて検討しておく。多くのフレームワークは、コントローラーという1つのクラスを作ることからチュートリアルが始まる結果、1つのクラスで作るものだと思い込んでいたり、あるいはクラスを複数用意するとしても、連携する手法は特にフレームワークで用意されていないなど、「巨大な1つのコントローラー」を用意する素地が、意図しなかったとしてもフレームワークがお膳立てしているという状況であるとも言えるだろう。一方、最近、ある仕事で、ethnamというグリーが当初開発したethnaの後継というPHPのフレームワークを使う機会があった。このフレームワークは改めて、フレームワークの機能との対比を考えるときの題材の1つにする。このフレームワークの特徴は、コントローラーというクラスを作るのではなく、中心的なコントローラーに対応する決められた名前のメソッドがあるという点である。決められていることは、prepare、perform、preforwardという3つのメソッドが順番に呼び出されるということだ。prepareは結果次第でのちの作業をキャンセルできることから、バウンダリに近いところにあってデータの検証などを行う入力用ビューコントローラーと言える。一方、最後のpreforwardは、出力の直前に呼び出されることから、出力用のビューコントローラーと言える。中間のperfomは、まさに、コントローラーとしての役割を持ち、ここでモデルへ働きかけるなどのビジネスロジックとのやりとりを記述することになる。これらのメソッドが保持されるクラスは決められた場所に作成するようになっているため、コントローラーの粒度はクラスよりもメソッドの方が適切だと言えるフレームワークである。コントローラーというクラスを作る話と、コントローラー相当のメソッドがあるという話は、設計の上では極めて大きな違いがある。抽象的な記述から、フレームワークにあった実装を行うという方針でもいいのだが、この方針だと属人的な実装テクニックや、あるいは「言わずもがな」なルールを読み解かないといけない場面が多そうだ。結果的に、大きな単位で何をすべきかは考えるのがモデルの1つの目的ではあるもの、コードの段階ではもっとも粒度の低い1つのステートメントを記述しなければならない。ならば、コントロールの中の粒度をより細かくして一度考えてみる必要があると思われる。また、フレームワークの機能との対比をする上では、やはり一度は細かな処理に分ける必要があると考える。

まず、次の図は、クラス図を元に、一覧と検索を持つようなWebページのモデルを記述したものである。astah*でクラス図を用意して、バウンダリーは大まかながら、主要なコンポーネントを抜き出して1画面を枠で囲う形式で記述した。モデルは、この段階では概略として記述し、コントロールの中の処理の1ステップずつをクラスで記述した。

クラス図0

この図を描きながら考えたことは、まず、処理には順序があるということである。入力されるバウンダリーから、出力されるバウンダリーに向かっていけば判別つかなくもないが、図を明確にするには、処理の順序を矢印で示し、どちらが先なのかを記載する必要があるということだ。

続いて、どうしても「分岐」は必要になる。前の図は、クラス図で適当に書いたものである。この点から、前の図にはないが、繰り返しや並行処理的なものを記述が必要になることは十分に可能性があるがある。

ここまでに至るまでにすでにお気付きのように、コントロールの処理を詳細化したものは「フローチャート」なのである。もっとも、UMLでフローチャートに近いものといえば、アクティビティ図である。前述のモデルをastah*のアクティビティ図で記述してみた。ほとんど同様な図が描かれているが、バウンダリーやエンティティのアイコンに接続された矢印が今ひとつうまく処理できていない。astah*でこの状態にするには、オブジェクトのオブジェクト名を空、ベースクラスをバウンダリーで使ったクラス名を選択することででき、その後に右クリックして、標準アイコンを選択するもこのようになってしまった。フローチャート部分は作りやすいが、astah*での作業は明らかにクラス図の方がみやすい図を作れる。

アクティビティ図0

この先、図をどうやって作成するかは問題だが、とりあえず、クラス図に戻り、バウンダリーの詳細化を行った。画面要素のキーあるいはフィールドの記述をコンポジションでバウンダリーに追加したが、「JavaScriptアラート」のような手法の指定もあるかもしれない。特にこういう場合の記述はないと思われるので、点線矢印で適用先を記述した。

クラス図0_0

このモデルから実装の方針をもう少し進めよう。バウンダリーの集合が3つあるが中央にある「エラー表示」はシンプルにJavaScriptのアラートを使うとしたら、「エラー表示画面作成」は、単にJavaScriptでwindows.alert(“…”);を実行することである。また、検索ボタンを押した時には検索条件はクライアント側にあるのだから、赤線枠で囲った部分は「検索条件入力画面」のページの中にJavaScriptでの記述が行えることになる。また、そうなれば、エラー表示に伴う「OKボタン」のクリックによる「検索条件入力画面生成」の処理を行う必要はない。その画面を崩さずにアラート表示したのであれば、単にアラートの表示だけでもOKである。結果的に、「エラー表示」は「検索条件入力画面」の一部となった。あとは、検索条件の検証ルールが与えられれば、実装はできそうである。

クラス図0_0_0

残りのアクティビティを分類して見る。分類の基準は、順次実行するアクティビティであるという点になる。つまり、まとまって実行されるアクティビティのセットは少なくともある段階では同一のメソッドの連続したステートメントに記述されていることになるので、その点から分類をする。そうなると、「検索条件入力画面生成」が1つ、さらに「条件を与えて顧客情報検索」「検索結果表示画面生成」が別の分類となり、図中ではそれぞれ分類2と分類4で囲った。分類4の中にある2つのアクティビティは、データ検索とビューの生成といった違うことを行なっている。そうであるのなら、さらに分類すべきであるとも考え、図中の分類3で囲った部分が抽出される。結果的に全部バラバラということにもなるが、分類4のみ2つのアクティビティからなるとも言える。

ここで、まず、分類3の「条件を与えて顧客情報検索」であるが、データを扱っているので明らかにモデルに関わる機能である。そして、いくつかある「画面生成」は明らかにビューに関わる機能である。図の右側に対応するコントロールを記述したが、それぞれ対応するオブジェクトを記述できることから、この図はロバストネス図をさらに詳細したものであるという点は明白である。

実際に実装するとなると、分類3「条件を与えて顧客情報検索」はデータ処理が含まれており明らかにサーバーサイドの処理となる。2つの「画面生成」は、例えばPHPのフレームワークのCodeIgniterであれば、サーバーサイドで記述をする。これくらいの機能ものだと1つのコントローラークラスにまとめてしまってもさほどのコード量ではないと考えるかもしれない。

ここで重要な点がある。それは分類1はクライアント、分類2〜4はサーバーサイドでの実装を行う点が、ある状況では決定づけられてしまうということだ。一般にはサーバーサイドのフレームワークの場合「画面生成」という処理から先は自動的に行われる。画面生成に必要な状況をコントローラーで作り出すのが開発作業である。そこで注目したいのは、考慮すべきクライアントとサーバーの切れ目である。画面生成はどちらかといえば、フレームワークでほぼ自動化されいているので、考慮対象外である。一方、「入力内容検証」後にエラーなしとなった場合、「条件を与えて顧客情報検索」に移行する場面がある。ここは矢印が1つだが、実際には、サーバー側にはPOSTメソッドの受け入れから、該当する処理が記述されたメソッドを呼び出すまでの処理(例えば「ルーティング」など)が入ることになる。これらは自動的にできないメソッドも多い。

これらの検討結果を加えたモデルは次のとおりである。まず、検索条件の入力はフォーム(HTMLのformタグ)で記述することにして、フォーム自体をサブシステムとして記述する。入力結果を検証した結果、サーバーへPOSTするのは、ブラウザが本来持っている機能である。また、POSTを受け入れるのフレームワークあるいはCGIとして持っている仕組みを利用できるので、これらは実装対象ではない。ただし、正しく受け入れがなされて意図したメソッドが呼び出されるようにするための処置は必要であり、それはフレームワークごとに違っている。

クラス図0_0_0

このように小規模なWebアプリケーションでもクライアントとサーバーの分離やフレームワークのサポートの有無を考慮しながら、実装ポイントを探るということが必要になる。従来のオブジェクト指向を基調としたモデルでは粒度が粗く、実装の具体的な手法までをモデルまで記述することはしなかった。言い換えれば、実装した結果の要約を逆解析するようなものであったとも言える。現実の開発でここまであるいはさらに細かい詳細化がモデルの段階で必要かどうかという議論はあるだろうが、その点は別にしてもWeb開発の学習やフレームワークの理解にはこうした手法が有効であると考える。

[開発プロセス#7] 開発対象システムのモデル記述

システム開発でモデルを作成することにより、これから作るシステムが要求に合致したものであるのかといった側面の検討だけではなく、効率の良い実装が可能であるのかといった側面や、予想される改変や拡張に対して実現可能性やその効率性、テストのしやすさなど、実装に直結すること以上に利用できる。しかしながら、その作成するモデルは、様々な手法がある。要求の記述という点ではユースケース図とユースケース記述が出発点にはなるが、データベース主体の開発だと、ここでER図の作成を主体にすることが多い。データベースの構造は対象ドメインの状況を記述できるようになっている必要があるのだが、その結果、全てがデータベースの設計結果であるスキーマに集約されているという見方をしてしまいがちである。ある作業を、スキーマで解決するか、あるいは実装するプログラムで解決するかはもちろん考えるのではあるが、では「ここの処理はプログラムで」というアイデアはER図やクラス図では不明確である。メモを残すなどの工夫が必要になる。

一方、Webシステムではワイアフレームといったざっくりとした画面のデザインをもとに、画面遷移を定義することで、システムの全体像を示そうとする。しかしながら、画面デザインが反映するのは、システムへの実装以前にドメインに対して実施される分析をもとにして作成されるビジネスモデルであり、実装に直結したER図等のモデルからのズレが生じする場合もある。しかしながら、HTMLでの動的なユーザーインタフェース作成機能は限られていたので、例えばチェックボックスであればどういう動作をすべきかということを逐一記述しなくても、半ば「常識的な判断」で進めることであまり大きなブレは発生しなかった。さらに、結果的にビジネスモデルとER図で記述されるスキーマとはあまり変わらないことお多いので、HTMLのモックアップが「設計」として扱うのは、デザイン主体の開発会社では頻繁に見られる方法である。

このように、ある程度のスキルや、ワークフローができてくると、途中を省略する傾向は強く、単一の会社でそれが回っている間は特に問題視はされないだろう。しかしながら、Webの世界は変化が激しい。特にここ5年くらいの時期はクライアントサイドの作り込みの度合いが高くなり、ユーザーインタフェースは複雑化している。その結果、Webのクライアントサイドは多階層的な機能分離を図る必要が出るなど、スマホのクライアントのネイティブアプリケーションに近い構造になっている。

こうした状況が変わってきた時、ワークフローを見直す必要があると考える。今までの知見や常識として暗黙知的に取り扱っていた知識だけでは、新しい問題に対する解法がない可能性が高く、最適な設計ができなかったり、チーム内での意識の疎通が悪くなる。そのために、本来的な詳細なモデルを記述するということが必要なのである。そこを出発点にして、省略やあるいは詳細化をするとしても、Webシステムのモデル記述の手法を一定のレベルで確立する必要があると考える。

UML自体の汎用性は高く、その点では有用性も高いのは事実である。しかしながら、UMLやその周辺手法を使ってWebシステムの記述をするにあたり、次の点に対して何らかの解決策が必要と考える。

  1. 詳細化するとき、明確なルールは特にない
  2. クラス図を主体にすると、オブジェクト指向が前提となる
  3. ボックスがクラスやオブジェクトで、呼び出しが矢印線的な切り分けによる分かりやすさはあるが、処理の記述では呼び出し線に名称を書くことになり、図を見やすく作ることが困難になりがちである

まず、1の詳細化についてである。作成したモデルと、使用するフレームワークの提供機能を比較検討して、実装箇所を特定するには、極端に言えば、プログラムの1行レベルまでを、モデル内の1つの要素として記述することも必要とされる。また、ロバストネス図やシーケンス図では「画面」を1つの要素として扱うが、実際に処理の流れを追う場合、ボタン1つ1つは原則として異なる動作をするので、ボタン1つ1つを要素として表したいということになる。このための指針が費用である。

2つ目のオブジェクト指向である点は、Javaのプログラムだけを作るような場合には確かに有効ではあるが、HTML、CSS、JavaScriptが絡む世界では必ずしもオブジェクト指向ではない。つまり、クラスやオブジェクトを1つの単位として考える場面は実装時にはあるかもしれないが、分析時にはどちらかと言えば、より細かい単位での要素を記述したい。最終的にはメソッドが集まってクラスになるとしても、対象としたのはメソッドであり、その中でどんな処理をしたいかということである。

3つ目は、コミュニケーション図でオブジェクトからオブジェクトに線を引き、線に添えてメッセージ呼び出しを記述する。この方法は実体あるいは実体化可能なものはボックスであり、メッセージは矢印線であるという明確な分類があるものの、簡単なモデルでも、オブジェクトとメッセージを見やすく配置するのはかなり大変である。ましてや、複雑なモデルだとなおさらである。ここではメッセージの記述が作図作業を煩雑にしている点は明白であり、それに代わる表記方法を検討すべきであると考える。

これらの懸案をまずは見える範囲での解決を行った上で、Webシステムのモデルを記述することにする。まず、1〜3に対する提案を次回より行う。

 

[開発プロセス#2]何のための何を目指したプロセスか?

開発プロセスにおけるアーキテクチャ設計として先日記事を公開しましたが、継続して記事を続けるにあたって、ヘッダに [開発プロセス#N] (Nはシリアル番号)を入れることにします。前回は大まかな話でしたが、今回は検討する開発プロセスの目的を定めます。今回は、当たり前の話ばかりだと思います。

開発において、いきなりプログラミングすることはまずないと思いますが、何らかの検討を経て実装に入ります。実装なくしては当然ながら完成しないので、実装、すなわちプログラミングは開発の中心と思われているかもしれません。しかしながら、実際に開発経験があると理解できることは、事前に検討して問題解決していることと、実装しながら問題解決に当たるのとでは、前者の方がはるかに効率的であり、出来上がったコードの質が高くなるということです。「仕様書をちゃんと書けよ」ということになる一方、仕様に関する成果物に労力をかけないまま実装に入ることもあります。これらの上流工程にどの程度時間をかけて、どの程度の成果物を得ればいいのかということの議論は昔から今に至るまで、そしてきっと未来永劫続くことと思います。ウォーターフォール形式など、開発プロセスが決まれば、1つのプロセスの終了と、検討結果に関する成果物とが対応することになり、いわば自動的に仕様書を書くことが前提になります。一方、仕様書を記載したり、あるいは変更結果を反映させる作業は決して小さな負荷ではありません。アジャイル開発のように、完全な仕様書を書くよりも動くコードを書くことを優先させる手法も出てきている一方、このことを都合よく解釈して「アジャイルはドキュメントは書かなくてもいい」と決めつける人たちがいることも事実です(アジャイル勘違い集#3)。しかしながら、こうした決めつけが出てくる背景には、設計の成果物の労力に見合う結果が得られるのかという懸念があるのではないでしょうか。

実装に入るまでのプロセスを重要視し、仕様書を作るという立場は崩しません。しかしながら、アジャイルソフトウェア宣言には「包括的なドキュメントよりも動くソフトウェアを、」とあります。包括的な、すなわち完全なドキュメントでないけども、設計する上で考えたアイデアや制約などがドキュメントとして参照できる必要があります。ではコードをしっかり書けばいいという考え方もあります。Googleへの注目が増大される時代に、「Googleでは仕様書ではなくコードを見る」のだというイメージが定着し、ここでも勝手な解釈になるのでしょうけど「仕様書はなくてもすごいシステムは作れる」と思ったと考えられます。また、コードは常に最新であるという主張は間違いはありません。しかしながら、コードはコードであり仕様ではありません。コメント欄に記述できる内容も限られていますし、コード上で明示的ではないあるいはその真髄に迫るには時間をかけて解析しないといけないような様々なノウハウが実装には注ぎ込まれているはずです。コードで仕様に関するコミュニケーションがとれるとしたら、その内容に精通したプログラマ同士だけでしょう。オープンソース開発では、コミッターの時間的制約などでそうした状況になりがちであり、単体テスト等のテストを自動化することと組み合わせれば、十分にワークする方法であることは多数のオープンソースプロジェクトから言えるのではないでしょうか。しかしながら、コードを読み解くということは、時間をかけて解析したプログラマ以外に可能かというと、ほぼ無理でしょう。顧客や発注側はそこまではしない、あるいはできないでしょう。「しない」理由は、それをやるくらいなら自分で開発できるから発注しないからです。プロジェクト管理者、あるいはユーザーテストの管理者、導入教育担当、こうした開発に関わる様々な人が、プログラマ並みにコードを読むことができるかといえば、能力的なことだけでなくビジネスに直面するコストの問題でできないと言えます。

受託開発では顧客の要望がコロコロ変わるようなことは普通にあり、これは「消極的なアジャイル」とも見ることができます。追加の開発に対しても費用が出ないことあるのに、ドキュメントの更新に追加の費用を請求する雰囲気は現場にはありません。こうして「使えないドキュメント」が山積みされることになるのですが、その使えないドキュメントでも、過去のある時点をキャプチャした情報として活用せざるを得ない場合もあるわけです。

こうした現在の開発プロセスにおいて、成果物として何が求められるのかを再考するのが、一連の[開発プロセス]記事の1つの大きな目的です。すなわち、ドキュメントに記述されることを、ドキュメントに対する負荷を可能な限り最小化したプロセスとして再考するのが目的です。以下、まとめてみました。

  • 仕様書は作成する必要がある。特に強くコミットしているプログラマ以外のコミュニケーションにはなくてはならない。
  • 仕様書が膨大になると、更新が滞り、最新の結果が反映されなくなる。仕様の変更に対してドキュメントの更新は必要だが、その負荷を最小化したい。

この次は、開発されるシステムがどうなることを意図しているのかということを記載します。こちらの方が先に記載すべきかもしれませんが、目的の話題が何度か続くとういうところです。

開発プロセスにおけるアーキテクチャ設計

INTER-Mediatorに適した開発プロセスを考えていましたが、うまくまとまりません。なんでかなと考えたのですが、INTER-Mediatorを使うという前提なしで考えるべきであり、それを元にINTER-Mediatorへの適用をする必要があると考えます。つまり、一般的な意味でのWebアプリケーションだとかスマホアプリといったものを開発するときに、どんなアーキテクチャが前提となり、設計上何を考えれば要求・要件から実装に近づけるのかという一般論をまずはまとめるべきなのでしょう。最近、たまたま、いろんな仕事の中でアーキテクチャ関連の話題の基本を再勉強する機会があり、その中で一般論になんとなく近づいている感があるので、いっそのこと、考えながらブログでまとめていくという過程で進めようかと考えた次第です。

システム開発では、要求や要件を出発点として、仕様を策定して実装に持ち込みます。外部仕様、内部仕様という区分けがある場合もあります。また、データベースを使う場合は原則としてレイヤー構造になり、データベースが底辺を担うことからドメイン分析した結果をスキーマとして記述して、実装段階では確定したデータベース定義にすることになります。こうした、設計の定石とも言えることはたくさんあり、特定の開発チームや流派、カルチャーに応じてその表現は違うものではありますが、システムを意図した通りに稼働させるという意味では目指す方向は同じであり、場面によって言い方が違うだけの場合もよくあるわけです。一方、様々なノウハウがあるものの、ある要件を満たすようにシステムを作る場合の実装に向けた機能分解は、単一の答えが存在する問題ではありません。同程度に正しい解答はいくつも作れるでしょう。結果的に、「状況に合わせて最適化する」というなんとでも取れるようなアドバイスしか出てこないことにもなりかねません。しかしながら、機能をどのように分解して、それぞれの個別の動作をどのように実装するのかということが、開発の上では非常に重要な決定であることは言うまでもないことです。ベテランや才能のある人は、そういうことをスラスラとできるのかもしれませんが、初心者あるいはある領域の経験が浅いと悩むものです。手っ取り早く知りたいという要求は年々高まり、「ベストプラクティス」や「ティップス」といった情報が重宝されるようにもなります。

こうした設計を完成させるに至る知見は、例えばオブジェクト指向分析や、コンポーネント分析、パターンなど、様々な手法があります。それらをうまく束ねた統一的な開発手法も多数存在します。私が考えていることは、そうした開発手法の1つを作り上げることであり、その結果をもとにして、INTER-Mediatorでの開発手法を確立するということです。車輪の再発明っぽく見えるかもしれませんが、問題点を検討すると、やはり時代に応じた道具に対応した手法はその都度更新されていくものではないかとも考えられます。

現在の様々な開発手法が発展したのは、オブジェクト指向プログラミングが当たり前の時代になり、コンピューターの性能が高まり、そしてインターネットで世界中が繋がって、Webという基盤ができた頃の時代です。一方、現在はスマホ登場やフロントエンドの発展が著しく、サーバー単体での開発手法以上のものが求められています。従来はWeb開発とネイティブ開発、つまりOSのAPIを直接利用する開発は、大きく違っていたのに対して、現在は言語やフレームワークは違っても同じような実装をしているという感覚があります。この領域で業務をしている人は薄々気づいていることではないでしょうか。これは、何か共通のアーキテクチャを持ち込むべきではないかと考えました。まずは、そのための問題意識として持ったことを箇条書きにします。

  • Webシステムはサーバー中心なのは仕方ないとしても、クライアント側のアーキテクチャをどう表現するのが適切だろうか?
  • スマホアプリはサーバー接続する場合が多く、「サーバー側」「スマホ側」という2つのアプリケーションの連動を行っている。最近のWebも「スマホ側」が「JavaScript処理」に置き換わるだけの違いとも言える。つまり、今時のシステムは、サーバーサイド、クライアントサイドを統合的に議論できるアーキテクチャを基盤として持つ必要があるのではないか。
  • コンピューターが速くなり、メモリーも豊富になると、RDBである必要性は薄くなる一方、やはり安定した保存はRDBが理解しやすいとも言える。RDB前提のシステム設計はある意味安定するのだが、逆に必然としてRDBが出てくる理由付けはないのだろうか?
  • サーバーサイドのアーキテクチャはともかくMVCである。いろんなMVCが存在する一方、正しいとか正しくないとか、本物だとかそうじゃないという議論があるものの、MVCは大雑把な機能分類として考えれば、役立つ場面は多いのではないだろうか。
  • クライアントのアーキテクチャはMVCなのだろうか? 違うのだろうか? クライアントサイドの主要な機能はUIの運用とその高度化であるとしたら、MVVMがいろいろな意味で包括性の高いアーキテクチャではないだろうか? ただし、バインディングとオブザーバーを基調としているだけに、それらを実装していないフレームワークはMVVMとして俯瞰できるかどうかが難しい点であると言える。
  • MVCとMVVMを出発点に考えるとしたら、それらの中での機能分割の指標としては、ロバストネス分析をベースに検討できるのではないか、ロバストネス図の要素はビュー、コントロール、エンティティであり、ほぼMVCのコンポーネントに対応するものの1対1ではない。しかしながら、要件をロバストネス分析して、MVCやMVVMを基本としたアーキテクチャとして表現することは、オブジェクトし志向分析の結果を見ても可能であると考えられる。
  • MVCなどのアーキテクチャパターンで設計しても、実装できないかもしれないし、実装するために変形が必要な場合もあるのかもしれない。過去の様々な設計手法では、汎用化がされる一方で、使用するフレームワークの制約やあるいは特有のコールドポイントの扱いなどが考慮されたものは見たことはない。しかしながら、フレームワークによる制約は可能な限り早期に検討する必要がある事柄ではないだろうか。あるいは、フレームワーク制約を組み込む段階がいつなのかを明確にすべきではないだろうか。
  • サーバーとクライアントの分類はどうすべきだろうか? 最初はそれらは関係なく、要件満たすような必要な処理に分割し、あるところで「線を引く」という作業になるのだろうか? そうすれば、サーバーとクライアントの両方を巻き込むシステムの設計はスムーズにできそうだ。

とりあえず、思いつくまま挙げましたので、ポエムに近づきつつあります。これらの考えを少しずつほぐして、手法として使えるまでにすることが目標です。

(続く)

[IM] Ver.5.3よりサポートのsourceキー

Ver.5.3より、コンテキスト定義(定義ファイルのIM_Entry関数の第一引数の配列の要素)に、sourceキーを入れるようになっています。データベース上のエンティティ名に関連するキーとしては、name、view、tableがありますが、それにsourceが加わります。データベースから読み出しを行うときのFROM句に指定されるビューやテーブルの名前は、viewキーがあればその値、なければnameキーの値が使われます。CREATE/UPDATE/DELETコマンドの対象は、tableキーの値で、それがなければnameキーが使わます。例えば、nameキーにテーブル名があれば、そのテーブル名を指定しての読み書きはviewとtableキーの指定はしなくてもできます。しかしながら、表示は何かのビューを使い、更新はそのビューではない特定のテーブルを指定したいような場合に対処できるように、viewとtableキーを用意しました。

しかし、これだけでは問題があります。あるテーブルのあるレコードのあるフィールドが、ページ上の複数のリンクノードに展開された場合、それが例えば両方ともテキストフィールドであれば、一方の値を変えると、フィールド更新のためのサーバー通信が行われるとともに、クライアントサイドで同一フィールドの別リンクノードの値を更新できるようになっています。その時、何を手掛かりにして反映させるコンテキストを探しているかといえば、「viewないしはnameキーの値が同じだが異なるコンテキスト」です。例えば、住所録的なpeopleテーブルがあり、同一ページに全レコードの一覧と、その中で男性の一覧の両方があったとします。前者のコンテキストは、name=allmembers, view=people、後者のコンテキストはname=malemembers, view=peopleとして適切なqueryキーの条件を与えればいいでしょう。こうすれば、同一テーブルから異なるコンテキストを生成して、内容が異なる一覧を1ページ内で生成できます。

この時、男性の一覧にあるレコードは、必ず全員のレコードの一覧にもあります。どちらもviewキーがpeopleなので、男性のレコードの1つのフィールドを変更すると、対応する全員の一覧にあるレコードのフィールド値も更新されます。以下、この動作は「連動」と呼びます。同一のviewキーの値を持つコンテキスト同士なので、INTER-Mediatorにとっての手がかりがあります。

しかしながら、SQLのレベルで、peopleをもとにしたビューeveryoneとsomeoneがあったとします。それらで表を作るとしたら、nameやviewを使うにしても、everyoneとsomeoneがそれぞれのコンテキストに登場はしますが、コンテキストの情報から各々が同一のpeopleテーブルから導出されていることはわかりません。そうなると、同一のレコードがそれぞれの一覧に見えていて、一方の値を変更したとしても、その変更結果を伝達する手がかりがなく、連動はできません。

このような時には、一方のコンテキストを、name=everyone, source=people、もう一方はname=someone, source=peopleと定義します。ページファイル側は、everyoneあるいはsomeoneをdata-im属性に利用する点は変わりありません。このsourceの設定により、2つのコンテキスト定義から得られるコンテキストは、同一のテーブルから来ているものということがINTER-Mediatorに伝わるので、同一フィールド値がユーザーインタフェース上で連動するようになります。

[IM]リピーター判別の汎用化

INTER-Mediatorに関して、1つ重要な仕様変更を考えています。エンクロージャー、リピーターの識別に関することです。現状は次のようなルールになっています。

(1)ターゲット指定(data-im属性)のあるノードを含む、TR、OPTION、LIタグ要素は自動的にリピーターとなる
(2)自動的にリピーターとなったノードの親ノードが自動的にエンクロージャーとなる
(3)DIV、SPANに限り、data-im-control属性を指定して、リピーターとエンクロージャーを明示的に指定できる

この3つ目のルールを廃止して、次のようなルールにしようかと思っています。ようするに汎用化です。(1)(2)のルールはそのまま継続させます。結果的に、(2)は(3′)に対しても適用されることになります。

(3′)data-im-control属性の値が「repeater」の場合、そのノード及び兄弟ノードのリピーターを集めてレコードの数だけ繰り返す。
(2′)repater群の1つ上位のノードは自動的にエンクロジャーであると識別する。

OL/ULだけでなく、DLなどのタグや、その他、SECTIONタグなど、エンクロージャー/リピーターとして使用したいタグがある点は、以前から指摘されています。SAPN/DIVが対応していれば、「なんとかなる」とは言え、時に不便なこともあります。結果的に、なんでもリピーターにしていいのではないかというのが考えた結論です。そうすれば、DIV内部にINPUTタグのリピートみたいなこともできます。

[IM]INTER-Mediatorはなぜ“会社”にしないのか?

このところ、INTER-Mediatorはなぜ会社にしないのかという主旨の質問を聞かれることが何度かありました。INTER-Mediatorに対して、「なぜ、会社にしないのですか?」「これだったら投資してもらえるのでは?」といった疑問を持たれるようです。ちょうどいい機会なので、新居が考えることを書いておきたいと思います。

まず、INTER-Mediatorもそこそこ出来上がってきており、これを元に仕事ができるレベルになっているのは確かです。新居自身、既にコンスタントにINTER-Mediatorを使った開発案件を抱えるくらいになりつつあります。としたら、一般的に考えられるのは、会社を作ってよりビジネスを広げることでしょう。また、投資が得られたら、ツールの開発などに取り組むことも現実的な作業として考えられるでしょう。しかし、新居自身は「INTER-Mediatorの会社」を作る意思は今の所ありません。

考えられる理想的な状況は、会社を作ってビジネスが軌道に乗るということではなく、次のようなことです。INTER-Mediatorはあくまで素材として存在し、それをベースにビジネスを行う会社が複数共存(連立か?)できるような状況が思想的だと考えます。1社の製品として、1社がコントロールする場合、まず、一定のシェアを確保しないと、ビジネスが存続しません。それは、言い換えれば他者を排除することにもつながります。もちろん、多大なシェアを目指すのも1つの手法ですが、既に数度の飽和をしているIT市場であり、いくつものビッグプレイヤーに1社で臨むのはかなり無理があると考えます。また、そうした一種の賭けをやると、終わりも視野に入ってきます。ビジネスの表舞台に立てるというのは魅力である一方、ダメになった時にはゼロ以下になってしまうということもあります。

INTER-Mediatorが目指すものは、システム開発プロセスの変革であり、その結果、情報システムを広げることでITのメリットをたくさんの人が、より多くの場面で享受できるようにすることです。もちろん、そうした意思はINTER-Mediator特有のものではなく、多くの人たちが様々な手法でチャレンジし続けています。INTER-Mediatorはその流れのほんの小さなインパクトにしか過ぎないものですが、理想を目指して継続させることこそが、何らかの貢献になるのではないかと信じています。そのためには、ビジネスのフィールドに軸足を突っ込むことは、リスクを増やすだけと考えています。

ただし、その一方で、INTER-Mediatorでビジネスをすることには肯定的に捉えています。自分も、業務システムの発注を受けていますし、エミックさんのようにFMPublisherというビジネス展開の一翼をINTER-Mediatorベースに進めていらっしゃる会社もあります。新居自身は、他の皆さんがビジネス展開する上で、必要であれば支援は行います。また、特定の会社だけでなく、原則として複数の会社に対しても支援しています。INTER-Mediatorのコアは「コミュニティ」だと考えています。そのコミュニティで培われたものを持ってビジネスフィールドに進出する人や会社は複数あって、それぞれが独自にアイデアを持ち、協業し、あるいは競合して、世界が広がればいいのではないかと考えています。コミュニティを中心にして、ビジネスフィールドが広がるというイメージです。そして、コミュニティの主体を、新居個人ではなく、Committeeにしたのも、そういう願いの1つの現れでもあります。

こうした手法は、オープンソースを主体にした団体の1つの生きる道だと考えます。例えば、Apache Foundationも、ある意味同様な立ち位置ではないかと考えますが、法人格云々となるといろいろなやり方があるかもしれません。しかしながら、コアコミュニティモデルとも言うべき、コンセプトとそれを実現する素材としてソフトウエア開発をコミュニティで行い、ビジネスを行う主体とは別に存在させることで、多彩な展開が実現することを願っています。したがって、INTER-Mediator株式会社を作る意思は今の所はありません。

[IM]INTER-Mediatorワークショップで作った“イベント参加申し込みサイト”

K-OFのイベントの1つとして、INTER-Mediatorのワークショップを2015年11月7日に開きました。ワークショップとなると、参加者の皆さんに手をうごしてもらって…というのが一般的ですが、その場で参加される方々もいらっしゃったので、手を動かすのはプレゼンターだけでやりました。

今回のワークショップでの開発目標は、イベントの参加申し込みです。別の勉強会での事後アンケートで、「こういうのを作りたい」と希望があり、それを題材にさせていただきました。グループカウンセリングを実施している赤石さんは、現在、フォームズというサービスで参加受付を行い、メールで送られてくる参加依頼の内容を1項目ずつコピー&ペースとしているそうです。当初は「コピペをしないで一発でExcelに入れる」という要望だったのですが、いっそのこと、受付フォームと一覧表示をINTER-Mediatorで作成するのはどうかということで了承していただき、それをワークショップの題材としました。なお、実際の運用サイトでは、以下のコード内にあるパスワードや一部の名称はもちろん変更しています。

12191812_909228155821822_1194109405261025992_n

設計内容とスキーマ

ざっくり書いたユーズケース図は次の通りです。アクターは、赤石さんと、グループカウンセリングに参加される「お客様」の2つです。お客様はブラウザで申し込みをして、その結果をメールで受け取ることになります。その申し込み結果を「申し込みリスト」に蓄積して、それを赤石さんは随時チェックするという流れになります。この時、お客様は認証等なく申し込みを行えるようにしますが、一方で赤石さんしかリストが見れないようにします。ここで、データベースへの新規レコードは認証なし、その他の処理は認証を行わないとできないようにするという基本設計もできてしまいます。また、システムとアクターの接点は2か所あり、それぞれ1ページだけで実現しそうなので、合計2つのWebページの作成で済みそうです。

ユースケース図0

ER図と同じ用途のクラス図は次のようになります。ちょっとシンプルですが、「申し込み」という1つのテーブルで作ります。本来、グループカウンセリングの実施が1つのエンティティになり、カウンセリングのテーブルを作成して、申し込みとイベントが多対1の関係を作ります。現状では1種類のイベントを、シーケンシャルに、つまり同時に複数のイベントの募集をしているといった事情もないので、1テーブルで運用します。言い換えれば、イベントには「開催日時」というフィールドしかないので、別テーブルにする必要性は薄いということです。「義援金支払い許諾」はチェックボックスで、受講条件の1つを許諾するかどうかを明確にするものです。「ユーザー情報」は、INTER-Mediatorで認証を実施するために必要なテーブル群です。

クラス図0

これらを実現するMySQL用スキーマとして、以下の記述を作成しました。実はワークショップでは、これに失敗して悩んでしまったのですが、今後のワークショップはこれを雛形にしましょう(苦笑。ユーザーやデータベースを定義してアクセス権を設定し、applyテーブル(クラス図での「申し込み」)を定義します。その後の、authuser、authgroup、authcor、issuedhashのテーブルは認証で必要とするものです。リスト表示の時に使用するuser1ユーザーのみ定義しています。

SET NAMES 'utf8mb4';
#DROP USER 'web'@'localhost';
CREATE USER 'web'@'localhost' IDENTIFIED BY 'password';
DROP DATABASE IF EXISTS akaishi;
CREATE DATABASE akaishi CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE akaishi;
GRANT SELECT, INSERT, DELETE, UPDATE ON TABLE akaishi.* TO 'web'@'localhost';

CREATE TABLE apply (
  apply_id INT AUTO_INCREMENT,
  name VARCHAR(100),
  yomi VARCHAR(100),
  email VARCHAR(100),
  address VARCHAR(255),
  content TEXT,
  agreement INT,
  startdt DATETIME,
  PRIMARY KEY(apply_id)
) CHARACTER SET=utf8mb4, COLLATE=utf8mb4_unicode_ci, ENGINE=InnoDB;

CREATE TABLE authuser (
  id INT AUTO_INCREMENT,
  username VARCHAR(48),
  hashedpasswd VARCHAR(48),
  email VARCHAR(100),
  limitdt DATETIME,
  PRIMARY KEY(id)
) CHARACTER SET=utf8mb4, COLLATE=utf8mb4_unicode_ci, ENGINE=InnoDB;

INSERT authuser SET id=1,username='user1',hashedpasswd='d83eefa0a9bd7190c94e7911688503737a99db0154455354';

CREATE TABLE authgroup (
  id INT AUTO_INCREMENT,
  groupname VARCHAR(48),
  PRIMARY KEY(id)
) CHARACTER SET=utf8mb4, COLLATE=utf8mb4_unicode_ci, ENGINE=InnoDB;

CREATE TABLE authcor (
  id INT AUTO_INCREMENT,
  user_id INT,
  group_id INT,
  dest_group_id INT,
  privname VARCHAR(48),
  PRIMARY KEY(id)
) CHARACTER SET=utf8mb4, COLLATE=utf8mb4_unicode_ci, ENGINE=InnoDB;

CREATE TABLE issuedhash (
  id INT AUTO_INCREMENT,
  user_id INT,
  clienthost VARCHAR(48),
  hash VARCHAR(48),
  expired DateTime,
  PRIMARY KEY(id)
) CHARACTER SET=utf8mb4, COLLATE=utf8mb4_unicode_ci, ENGINE=InnoDB;

申し込みページの作成

まず、お客様がイベントの申し込みを行うページを作ります。コンテキストの定義を含む定義ファイル(apply.php)は以下のようなものです。ポストオンリーモードでのページを作成すればいいので、コンテキストは1つだけです。また、新規レコードは認証なしで、その他のデータベース処理はできないようにしたいので、存在しないユーザー(nobodyknows)に対してのみ読み出し、更新、削除ができるようにして、事実上、これらの3つの処理は行えないようにします。authenticationキーにcreateキーの値がないので、createに関しては認証なく実施できます。オプション指定はなく、データベース指定のところに、dsn、user、passwordのキーで、データベースへの接続についての記述を行っています。

<?php
require_once('../INTER-Mediator/INTER-Mediator.php');

IM_Entry(
  array(
    array(
      'name' => 'apply',
      'table' => 'apply',
      'view' => 'dummy',
      'key' => 'apply_id',
      'post-reconstruct'=>true,
      'authentication' => array(
        'read' => array('user' => 'nobodyknows'),
        'update' => array('user' => 'nobodyknows'),
        'delete' => array('user' => 'nobodyknows'),
      ),
      'send-mail' => array(
        'create' => array(
          'from-constant' => "msyk@msyk.net",
          'cc-constant' => "msyk@msyk.net",
          'subject-constant' => "申し込みを受け付けました",
          'to' => "email",
          'body-template' => "confirm.txt",
          'body-fields' => "name,yomi,email,address,content,startdt",
        ),
      ),
    ),
  ),
  array(),
  array(
    'db-class' => 'PDO',
    'dsn' => 'mysql:host=localhost;dbname=akaishi;charset=utf8mb4',
    'user' => 'web',
    'password' => 'password',
  ),
  false
);

お客様が申し込みを行うページのぺージファイル(apply.html)は次のように定義しました。見出しの文字列は最低限にしていますが、実運用の時には書き直して利用していただくとして、INTER-Mediatorの動作に関わる部分だけを作ってあります。ヘッダ部での定義ファイルの読み込み、BODYタグのonload属性でのフレームワーク呼び出し、そしてボディ部での記述がポイントになります。ボディ部では、TABLEタグによるテーブルが定義されていますが、data-im-control属性により、ポストオンリーモードであることが指定されています。あとは、コンテキストとフィールド名のセットをdata-imに記述します。同意の項目はチェックボックスですが、value=1の指定があり、チェックがあれば1をフィールドに設定します。カウンセリングの日付はstartdtフィールドにしますが、ここの部分はお客様が入力するのではなく、募集するときに日時が決定しているので、その日時を初期値として規定し、INPUTタグ自体はreadonly属性を設定して、表示だけをするようにします。最後のBUTTONもdata-im-control属性を指定して、書き込みボタンにします。なお、定義ファイルのコンテキストにはpost-reconstructキーのみがあり、この場合は書き込みを行うとこのページの再ロードを行うので、そこで入力した結果が消えます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script type="text/javascript" src="apply.php"></script>
</head>
<body onload="INTERMediator.construct();">
  <table>
    <tbody data-im-control="post">
      <tr>
        <th>名前</th>
        <td><input type="text" data-im="apply@name"/></td>
      </tr>
      <tr>
        <th>名前の読み</th>
        <td><input type="text" data-im="apply@yomi"/></td>
      </tr>
      <tr>
        <th>メールアドレス</th>
        <td><input type="text" data-im="apply@email"/></td>
      </tr>
      <tr>
        <th>住所</th>
        <td><input type="text" data-im="apply@address"/></td>
      </tr>
      <tr>
        <th>相談内容</th>
        <td><textarea data-im="apply@content"></textarea></td>
      </tr>
      <tr>
        <th>同意</th>
        <td><input type="checkbox" value="1" data-im="apply@agreement"/></td>
      </tr>
      <tr>
        <th>日程</th>
        <td><input type="text" data-im="apply@startdt" 
                   value="2015-11-07 13:00:00" readonly/></td>
      </tr>
      <tr>
        <th></th>
        <td><button data-im-control="post">申し込む</button> </td>
      </tr>
    </tbody>
  </table>
</body>
</html>

定義ファイルapply.phpのコンテキストには、レコードを作成した時にメールを送信する設定が組み込まれています。そのためのメールの文面は、confirm.txtファイルに以下のように用意します。メールについては、定義ファイルでの定義通り、差出人、CC、件名は決められたものを設定し、送り先は作成されたレコードのemailフィールドを指定します。本文は、以下のconfirm.txtの中の@@1@@などの部分が新規に作成されたレコードの値に置き換わります。body-fieldsキーの値に列挙されたフィールドとその順番より、@@1@@が名前、@@3@@がメールアドレス、@@5@@が相談内容、@@6@@が開催日時を示します。

@@1@@ 様(メールアドレス:@@3@@)

以下の通り、グループカウンセリングのお申し込みを受け付けました。

開催日時:@@6@@
ご相談内容:
@@5@@

_(などなど必要な情報を追加する)_

作成したページは次のようなものです。

shot3036

申し込みを行うと、入力したメールアドレスに、確認のメールが送られます。

shot3041

一覧ページの作成

一覧ページを構成するために、以下のような定義ファイル(list.php)を作成しました。こちらは、applyテーブルの内容を参照したり、場合によっては変更や削除等があるので、すべてのデータベース操作に対して、user1で認証したユーザーだけが許可されるようにしました。また、開催日の逆順で一覧されることで、最近の開催日に対する参加者がリストの上部にまとまるようにしました。コンテキストには、削除ボタンの追加の指示はありますが、挿入ボタンはありません。挿入ボタンは、必要ならapply.htmlから自分で申し込みを入れてそれを修正すればいいと考えました。

<?php
require_once('../INTER-Mediator/INTER-Mediator.php');

IM_Entry(
  array(
    array(
      'name' => 'apply',
      'table' => 'apply',
      'view' => 'apply',
      'key' => 'apply_id',
      'paging' => true,
      'records' => '20',
      'repeat-control' => 'confirm-delete',
      'sort' => array(
        array('field' => 'startdt', 'direction' => 'desc'),
      ),
      'authentication' => array(
        'all' => array('user' => 'user1'),
      ),
    ),
  ),
  array(),
  array(
    'db-class' => 'PDO',
    'dsn' => 'mysql:host=localhost;dbname=akaishi;charset=utf8mb4',
    'user' => 'web',
    'password' => 'password',
  ),
  false
);

一覧のページファイル(list.html)は以下の通りです。特に変わったところはなく、applyコンテキストの内容をページネーションとともに表示をしています。コンテキスト定義にあるように、20レコードずつ表示をします。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="INTER-Mediator/Samples/sample.css" />
  <title>Title</title>
  <script type="text/javascript" src="list.php"></script>
  <style>
    td{
      border: solid 1px black;
    }
  </style>
</head>
<body onload="INTERMediator.construct();">
  <div id="IM_NAVIGATOR"></div>
  <table>
    <thead>
      <tr><th>開始日時</th><th>名前</th><th>読み</th>
        <th>住所</th><th>相談内容</th><th>同意</th><th></th></tr>
    </thead>
    <tbody>
      <tr>
        <td data-im="apply@startdt"></td>
        <td data-im="apply@name"></td>
        <td data-im="apply@yomi"></td>
        <td data-im="apply@address"></td>
        <td data-im="apply@content"></td>
        <td data-im="apply@agreement"></td>
        <td></td>
      </tr>
    </tbody>
  </table>
</body>
</html>

ページを表示すると、ログインパネルが表示されます。

shot3042

正しいユーザー名とパスワードを入力してログインすれば、リスト形式で、申込者の一覧を見ることができます。

shot3043

ワークショップを終えて

結果的にワークショップでは、入力専用(ポストオンリーモード)のページと、その入力した結果を表示するための2つのページを作成しました。実際のワークショップでは凡ミスに気づかずちょっと時間を無駄しにしてしまって申し訳なかったのですが、通常ではこれくらいであれば1時間程度の作業で行えるということが示せたかと思います。また、典型的なシステム開発の流れも見ていただけるサンプルになったかと思います。

なお、こうして作ったシステムも、作ってから新たなニーズが発生するかと思います。そうした追加の要求や変化する要求についても追跡させてもらって、可能な限り、アフター・ワークショップとしてお伝えできればと考えています。

[IM]JavaScriptのメソッド内関数だけをテストする

INTER-Mediatorだけの話ではなく、JavaScript一般的な話です。メソッドの中に、そのメソッドの中だけで使う関数を書くということが可能なので、INTER-Mediatorではそういう実装をあちらこちらでやっています。処理の多いメソッドでは関数に分離することで見通しが良くなりますが、加えて、メソッド内の関数レベルでのテストをするということも考えました。

以下のプログラムで、変数anObjectはあるオブジェクトを参照しています。オブジェクトはmethod1というメソッドがあり、そのメソッドの中で、内部の関数であるfunc1を呼び出しています。このプログラムにより、コンソールには、”method1″ “func1″と2行の出力が行われます。

var anObject = {
  prop1: null,
  method1: function() {
    console.log("method1");
    func1();
    // method1のその他の処理
    function func1() {
      console.log("func1");
    }
  }
}

anObject.method1();

ここで、func1だけをテスト対象にしたく、かつmethod1の実処理を行いたくないと考えたとします。その場合、次のようにプログラムを変更しました。このプログラムは、こちらのページの記載を参考にしました。

var bObject = {
  prop1: null,
  method1: function(isTesting) {
    console.log("method1: isTesting="+isTesting);
    this.func1 = function() {
      console.log("func1");
    };
    if(isTesting) {
      return;
    }
    this.func1();
    // method1のその他の処理
  }
}

bObject.method1();
(new bObject.method1(true)).func1();

ポイントは、内部関数func1をプロパティにしていることです。そして、テストか通常利用かを示す引数isTestingを追加します。この引数は省略すると、undefinedになりますので、テスト時はtrueを追加するとして、省略時は何も指定しないことにします。テストの時には、プロパティへ内部関数の設定だけを行い、メソッドはそのまま終了します。

最後からの2行目は、普通にmethod2を利用する場合を想定しており、コンソールには、”method1: isTesting=undefined” “func1″と出力されます。この記述は、最初のプログラムの最後の行にある「anObject.method1();」と全く同じで、method1はつまりは通常利用では何も変更せずにそのまま利用できます。

一方、一番最後の行の実行により、コンソールには”method1: isTesting=true” “func1″と表示されます。newがあることでmethod1をコンストラクタとして実行するので、メソッド自体を返し、その結果、func1関数がメソッドのように実行できます。コンソールに”func1″と表示するのは、method1内部のthis.func1()を実行している部分ではありません。isTesting編集がtrueなので、その前にmethod1は終了します。つまり、「(new bObject.method1(true)).func1();」という記述で、func1だけを実行できるので、この関数だけの単体テストができるということです。

ちなみに、この方法は、method1に引数があっても利用できます。