アーカイブ | 09:49

それでもクロス ターゲット

21 12月

Write once, run anywhereだって?まずはそのふざけた幻想をぶち殺す!

 

まあ、全部は無理です、全部は。ただ、だからこそ、どこでも使えるコードと、どこでもは使えないコードは分けて考えましょう。

昨日は、UI層(ビュー)からロジックを分離しましょうという話をしましたが、ビューがrun anywhereしにくい部分だからというのもあります。

ロジック部分の分離

前に、こんな絵を描きました。

この絵の趣旨は、

  • 処理の中核的なロジック部分は、GUI部分とは分離して作りましょう。
  • 文字ベースでも使えるように作るのがコツです。

という話でした。

こうして分離した純粋なロジック部分は、かなりクロス ターゲットに作れるはずです。

いっそ、プロジェクトを分けて作ってしまうのがおすすめです。

これくらい徹底しないと、ほっとくとすぐにビューやコントローラーにロジックを書いちゃう人が出てきます。何度言ってもすぐに。

 

ちなみに、C#以外への移植が必要な場面(あるいはその逆、他言語からC#への移植)もあるかと思います。その場合でも、中核ロジックだけきっちり分離してある方が、移植は楽でしょうねぇ。

“ターゲット”

“クロス”の話をする前に、クロスにしたい”ターゲット”についてさらっと。いくつかの視点があります。

  • プラットフォーム
    • OSやデバイス、何を使うか
  • マウス/タッチ操作か、スクリプトか
    • わざわざ覚えなくても使いやすいのはGUI
    • 操作を記録しておいたり、一括処理したりしやすいのはCUI
  • サーバーかクライアントか
    • どちら側で処理するか

当たり前ですが、ターゲットが変われば書かなきゃいけないコードも変わります。しかし、そのターゲットごとに書かなきゃいけないコードと、どれでも共通して使えるコードはしっかり分けて作りましょう。

例え、当初予定として、単一のターゲットしか予定になかったとしてもです。

  • 予定は変わるものです
  • 普段から癖をつけておくことで、必要になった時にあわてないようにしましょう
  • これが意識できれば、自然とテストしやすいコードにもなります

 

ロジックの分離の効用

いくつか、効用が得られる状況を見ていきましょう。

UIは陳腐化が激しい

まあ、デバイス自体も変わりますから。この数年、ここまで急激にスマホが伸びると思っていなかった人も多いんじゃないでしょうか。気が付いたらコロッと変わっています。

当然、既存アプリをスマホに対応させてくれなんて要望も多いんじゃないでしょうかねぇ。予想できない勢いで、そういう要望がどんどん出てきます。

そして、UI層まで含めたクロス プラットフォーム開発が現実的でないとなると、ロジックをきっちり分離しておかないと痛い目を見ます。

ユーザーの手動操作ばかりじゃない

システムに、プログラム的にアクセスしたい場合も多いです。

  • CUI提供
  • サービス化

CUI提供

まず、CUIに関してですが、CUIの回で説明した通り、サーバー アプリなどでは、GUIとCUIを両方提供することが望まれます。今、おすすめなのは、PowerShellのCmdlet化してしまうことです。

サービス化

サービス化というのは、システム全体を1枚板に作るんじゃなくて、部品ごとに切り分けて作ってプログラム的に呼べるようにしておくことです。それぞれの部品を「サービス」と呼びます。代表的なのは、ウェブ越しのサービスを呼べるようにしておく「ウェブ サービス化」でしょう。今だと、いわゆるREST APIにしておくのが無難だと思います。

前述の通り、UIは陳腐化が激しく、割と頻繁に作り直しや追加の要件がでます。そして、その時に、元と同じ開発者を使えるとは限らないのです。部品の切り出しをしておかないと、引き継ぎのコストも跳ね上がります。

あるいは、システム開発会社に依頼するほどでもない、ほんのちょっとしたツールが欲しくなることもあるでしょう。サービスAPIがちゃんと公開されていたら、案外内製も簡単だったりします。

ほぼ自動

CUI提供にしろサービス化にしろ、中核ロジックをきっちりライブラリ化してあれば、作るのは非常に簡単です。コマンドや、ウェブ サービスAPIのURLと、プログラム コードの間でかなり単純なマッピングをするだけです。

というか、このマッピング作業は完全自動化できるレベルです。

単体テスト

ロジックの分離は、テストのしやすさにも影響します。前節の通り、プログラム的に扱いやすいようになっているはずなので、テストの自動化もしやすいです。

そして、ロジックを分けましょうの回で説明した通り、テストをしないと死にます。あとから確実に泣きをみます。

サーバーとクライアント

このご時世、処理はサーバー上で行って、クライアントは表示だけにすることも多いです。

ところが、常にネットにつながっているわけじゃない。特に、最近はスマホが流行りですし、通信状況がコロコロ変わります。また、些細なことでいちいちサーバー上に問い合わせていたら、応答性が悪くてストレスフルになったり、帯域圧迫したりという問題もあるでしょう。

そこで、特にゲームなんかだと多いんですが、サーバーと同じ処理をクライアント上でも行うことがあります。ここで1つ注意点としては、処理をクライアント側に移すんじゃなくて、両方で2重に処理する必要があります。クライアント上で計算した結果はあくまでキャッシュ的な扱いで、本当にサーバー側と不整合がないか、どこかで同期をとる必要があります。あるいは、ゲームだと深刻な問題になりますが、クライアントだけで処理してしまうと、いわゆるチート プレイがし放題です。

こういうとき、どちらでも使える状態でライブラリ化してあれば、何の苦労もありません。単一コードを、2つのプロジェクトから参照するだけです。

まあ、現実は…HTMLアプリとかでクライアントがJavaScriptになってしまうと、その夢はもろくも崩れ去るわけですが…。とはいえ、前述の通り、移植をするにしても、ロジックを分離してる方が楽です。

 

少し余談。ひどいものだと、仕様書もくそもなくて、「Flashが仕様みたいなものだから、Flash開発担当の人に聞いて同じものを実装して」みたいな話も…ざらに。

まとめ

いろんなターゲットがあります。

  • UI技術の入れ替わり
  • GUIかCUIか
  • サーバー処理かクライアント処理か

UIまで含めたクロス ターゲットは非現実でも、中核となる処理はだいたいどのターゲットでも動くはず、とういか、動くように作れるべき。

テストもしやすくなりますし、もし別の言語に移植する必要があるときも、ずいぶん楽になると思います。

どのターゲットに対しても使える処理は、独立したライブラリとして作ることをお勧めします。

 

補足

今回も何点か補足。

Portable Class Library

.NET Frameworkといっても、参照可能なクラス ライブラリの範囲が変わる、プロファイル(profile)というものを何種類か持っています。

どのプロファイルからでも使えるライブラリを作るためには、Portable Class Libraryという特別なプロジェクトを作る必要があります。

今回説明したような、「中核ロジックを独立させたライブラリ」を作るなら、このPotable Class Libraryを使うのがいいでしょう。

C#→JavaScript

Googleは結構、JavaからJavaScriptコードを生成してるんでしたっけ?まあ、ですよねー。

.NET界隈でも、C#からJavaScriptコードを生成するようなツールが結構あります。有名なものでは、Script#など。

サービスのメタデータ交換

サービス化するということは、ある誰かがサービスを作って、別のある誰かがそのサービスを参照することになります。そして、通常、両者がソースコードを共有することはないので、「ソース コード → サービスAPI化」のマッピングとは逆に、「サービスAPI → ソース コード」のマッピングもほしかったりします。

そのために、「このサービスはこんなAPI、こんなデータを公開しています」というような情報(メタデータ)も公開したいです。ちゃんとこのメタデータが公開されていれば、サービスの利用側が非常に楽になります。