アーキテクチャーパターン

帳票フォーム編集用のパッケージソフトウェアを開発しています。

フォーム上にコントロールを配置したり、プロパティを設定したりするもので、GUI統合開発環境と同様の機能が必要とされ、ソフトウェアとしては複雑な部類になると思います。言語はDelphi7を使用しました。

フォームやコントロールはライブラリに用意されているGUI部品を継承して作りました。Drag&Dropによるコントロールの移動・サイズ変更・プロパティエディタによる編集などGUIらしい機能ができました。そして、ツールバーによる編集やフォームに表示しないコントロールに対応しようと考えました。ツールバーではプロパティエディタと同じプロパティを編集するため連携しなければなりませんが、既にできあがってしまったプロパティエディタはそのような配慮はしていなかったため連携への対応は難しく、やり遂げたとしても場当たり的なコーディングになってしまいそうです。またコントロールはGUI部品を使ったためフォームに表示しないコントロールには対応できません。簡単に乗り越えそうにできない壁に当たってしまいました。

敗因は内部状態を表すオブジェクトとGUI部品を一体にしてしまったことにあるようです。以前からMVCと言う言葉は見かけていましたが、MVCが問題を解決する物ではないかと考えて情報を集めました。MVCで対応できそうと確信して、大変でしたがコードを書き換えていきました。ツールバーや非表示コントロールを実現するまでには至っていませんが簡単に対応できる仕組みは用意できました。

MVCにもバリエーションがあるようで、私が採用した物はDocument-Viewパターンに分類されるようです。MVCはControl(入力) - Model(処理) - View(出力)と機能を分けるのですが、Windowsに備わっているControlは入力イベント処理と表示が一体になっていますのでこれを素直に利用するとVCが一体の構造になり、M→Document、VC→Viewと言う対応になります。VC(View)が発行したコマンドをModel(Document)が処理し、Modelでの処理結果をViewに表示します。DocumentとViewは1対多の対応になっていてObserverパターンにより処理が終わったことをViewに通知して表示を行います。Model(Document)は処理を行うだけでなく処理結果を保持します。VC一体とするデメリットはView毎に入力イベント処理を記述しなければならず共通化しにくいことです。しかしながらプロパティエディタとGUIフォームで入力イベント処理の共通部分はありませんのでこの場合には問題となることもなさそうです。

MVCもパターンの一つですがデザインパターンと言うには適用範囲が広すぎます。MVCのようにプログラム全体の構成を表すパターンを「アーキテクチャーパターン」と呼ぶそうです。ソフトウェアアーキテクチャー - ソフトウェア開発のためのパターン体系F.ブッシュマン他にアーキテクチャーパターン・デザインパターンが紹介されています。この中にReflectionパターンが紹介されていてこれも早速適用しました。

デザインパターンを学習したときにも感じたことですが自己流だけでは限界があります。アーキテクチャーパターンを学習してまた一つできることが増えました。

参照

Reflectionパターン考察

アーキテクチャーパターンで触れたように帳票フォーム編集パッケージにReflectionパターンを採用しました。

フォーム上のテキストボックスでは、Caption, FontName, FontSizeなどのプロパティを持たせて、これらプロパティに応じた印刷を行うようにします。これらのプロパティはプログラム上もクラスのプロパティとしてインプリメントしています。このような仕組みで印刷を行う処理は問題なく書けます。今回はこれらのプロパティを編集できるようにしなければなりません。プロパティ毎にSetter, Getterを呼んでいてはプロパティエディタの機能は到底作れないのでDelphiに備わっている実行時型情報(RTTI)を利用して、オブジェクト・プロパティ名文字列・値を指定してプロパティを設定・取得する汎用的なメソッドを定義しました。

キーになるのがプロパティ名なので、プロパティのコレクションを扱ったりプロパティの値を列挙したり、Undo, Redoを実現したりといったことがやりにくく似通った処理の記述が増えてリファクタリングが必要になってきました。そこでプロパティを一つのオブジェクトして扱うように改めました。プロパティを表すオブジェクトは実行時型情報から作り出します。このようにすることでプロパティのコレクション・列挙を扱うのに通常のクラスライブラリが使えるようになり、文字列解析処理も減りました。冗長な記述もなくなって結果的にプログラムがすっきりしたと思います。

実行時型情報などのメタデータを使って汎用性を持たせるパターンをReflectionパターンと呼びます。

Reflectionパターンについて別の例で考察してみました。販売管理を考えてみると、伝票番号・商品・数量・単価・金額などが伝票の属性として必要になります。これをシステムにインプリメントするとき、2つのアプローチがあると思います。一つは伝票をクラスとし伝票番号などの属性をクラスのプロパティにするものです。もう一つは伝票をコレクションとして扱い、個々の属性は属性名をキーとするコレクションの要素として扱うものです。

クラスプロパティによる表現では業務分析により導出したモデルがそのまま設計上のクラスになります。金額の計算は金額=数量*単価といった具合に直感的な記述ができるのです。デメリットは汎用的に属性を扱うことができなくなることです。コレクション要素による表現では汎用的に属性を扱うことができますが、属性相互の関係式はItem['金額'].Value=Item['数量'].Value*Item['単価'].Valueといった風に冗長な記述になってしまうデメリットがあります。

オブジェクト指向ではこの2つの表現に関わる矛盾を解決してくれません。Reflectionパターンは一つの解決策なのです。これ以外に言語仕様でカバーできる物もあります。私が扱った言語の中で、JavaScriptは連想配列の要素はそのまま属性になります。Perl,
Rubyもクラス属性とコレクション要素を簡単に変換できる機能を備えています。Accessではほとんどの局面で属性をコレクション要素として扱いますが、フォームではフィールド名・コントロール名がそのままフォームのプロパティ名になります。使ったことはありませんがO/Rマッピングも同様の問題を解決するための物でしょう。

クラスプロパティ表現とコレクション要素表現の矛盾、決定打がないためにあちらこちらでいろんな解決策が模索されている状態ととらえられそうです。

現在はIDEの機能を利用することを前提にクラスのプロパティ表現としています(IDEとアーキテクチャー参照)。

Google検索:Reflection パターン, ドメインモデル, O/Rマッピング

Ajax

Google mapを見たときは感動しました。Webのシステムでここまでできるのだと。Google mapに代表されるような、クライアント側の処理により、ページを遷移することなくサーバ側と通信しながら動きのあるサイトを構築する技術をAjax(Asynchronous
JavaScript and XML)と呼ぶそうです。

最近構築したシステムではAjaxに習って一部導入してみました。例えば、伝票入力画面で商品のアイテム数が少ないのであればSELECT要素を使って選択すればよいのですが、アイテム数が多いとHTMLがふくれあがりますし、オペレータが商品を選ぶのも難しくなります。そこでコードを入力すればそれに相当する品名を横に表示すると言うことを隠しフレームを使ってやってみました。スピードが遅くならないかと心配しましたが十分実用的なスピードでレスポンスが帰ってきました。

Ajaxを全面的に使用すれば、最初に表示枠となるHTMLとJavaScriptをクライアントに送り、クライアント側のJavaScriptからクエリーを発行してデータベースを読む。編集後保存ボタンを押したら、保存用のクエリーを発行してサーバ側で保存処理する。このような処理の流れとなり、この間ページ遷移が一切起こらないと言ったことも可能になります。サーバ側の処理は軽く単純になります。

問題点もあります。

  • JavaScriptで作成するスクリプトが大きくなることや、ロード時の処理が重くなるので最初にページを読むときに時間がかかる。(IEはDOMノードをたどる処理を行うと遅いようです、Opera8.54は早いです)
  • JavaScriptのデバッグを効率化しなければならない(FirefoxのJavaScript Debugger活用)
  • ブラウザの機能に大きく依存するためクロスブラウザ開発が難しくなる

IEはバージョン6では、ActiveXを使ってサーバへの通信要求XMLHttpRequestを発行できますが、バージョン7ではネイティブ対応するそうです。機会があればAjaxを全面的に採用したWebサイトの構築をやってみたいと思います。

現在、派手さはありませんがデータの更新等の処理にAjaxを使っています。ブラウザの戻るボタンで戻っても不自然でない動きをします。(2011年8月9日)

キーワード:Ajax, XMLHttpRequest, prototype.js

ruby

データベース連携のWebシステム開発には今までPerlを使ってきました。文字コードをutf8で統一するということがあり、運用を行うレンタルサーバのPerlのバージョンが古いためスクリプトをutf8で書けないので、今回はrubyを使ってみました。

開発する過程で感じた特長をあげてみます。

  • 最初からオブジェクト指向で言語設計されているため、オブジェクト指向による開発が行いやすい
  • Perlに比べてすっきり書ける(basic的、pascal的制御構文)
  • 不定個数引数が自然にインプリメントされている
  • ブロック(イテレータ)を活用するプログラミングスタイルが根付いている
  • ライブラリが充実している
  • 統合開発環境RDEがあり、インテリセンスにも対応している
  • メーリングリストでの情報交換が盛んで、言語設計者自身も丁寧にフォローしている(私の質問にもお返事いただけました)

気になる点もありました。

  • 型チェックが甘いため、タイプミスに気がつきにくい
  • 変数宣言がないのでスコープを明示できない
  • データベース連携のためのライブラリDBIに関する情報が少ない

総じて使いやすい言語で、テンプレートライブラリや、データアクセスライブラリなどのライブラリ化を進めることができました。そのため細かいところまでフォローしたシステムが構築できたと思います。

参照整合性(外部キー)制約 使う?使わない?

データベースエンジンには参照整合性制約機能が備わっているものがあります。

参照整合性制約を得意先マスタと売上伝票の例で説明すると、得意先マスタに登録していない得意先コードを売上伝票に登録しようとしてもできない、売上伝票に登録している得意先を更新・削除しようとしてもできないなどの制約をデータベースがチェックしてくれるのです。これによってマスタにない得意先コードが売上伝票に存在しない、言い換えると不正なコードを持つレコード(迷子レコード)が存在しないことが保証されます。

これを使うかどうかは時々議論になるようです。制約必要派の言い分はアプリケーションのチェックが楽になる、アプリケーションを介さずにデータを操作したときにも制約が保証される。不要派の言い分は大量の更新・削除処理で負荷が掛かる。開発時の変更が不自由である。アプリケーションでチェックすれば問題がないといったことです。

私は参照整合性制約を積極的に利用しています。

最近の開発案件では、ほとんどが新規開発ではなくてシステムのリプレースであることが多いのですが、従前のシステムに登録されたデータを見ると大抵の場合に迷子レコードが存在しています。

制約不要派は、UI上登録するときにマスタの一覧の中から選ばせるなどの方法で事前チェックが入るからデータベースの制約は2重チェックだという意見があります。これには落とし穴があります。ユーザがコードを選択してからレコードが登録されるまでの間には時間がありますのでこの間に他の端末で今選んだマスタのレコードが更新・削除される可能性に配慮しなければなりません。この場合にも迷子レコードが発生しないようにするには登録の直前にもう一度マスタを確認しなければならなくなります。マスタ確認の前にトランザクションを開始し、ライトロックしてマスタを読み、その後伝票を更新、コミットするという手順を踏まなければいけないわけです。

このような面倒な手間を考えるくらいだったら参照整合性制約に頼ったほうがよいと私は判断しました。

参照整合性制約を使った場合、コード変更はたやすくできなくなります。そのため連鎖更新の機能を使用します。連鎖更新ではマスタを変更すると伝票の方もコードが変更されます。また、使用頻度は多くありませんが連鎖削除という機能もあります。

連鎖更新・連鎖削除は便利な機能ではありますがデータベースエンジンの制限に気をつけなければなりません。Microsoft SQL Server2000では制限がきつく、例えば売上伝票に得意先コードと納品先コードがあり、そのどちらも取引先マスタを参照していると、売上伝票と取引先マスタの間に2つの参照整合性制約をつけることになるのですが、連鎖更新をこの2つの制約に付加することができません。連鎖更新をあきらめるかデータベーストリガで連鎖更新をエミュレートすることになります。意外にもMDBやオープンソースのPostgreSQLにはこのような制限がありません。

デザインパターン 装置制御への応用

とある検査装置を制御するプログラムをてがけました.。開発ツールはBorland C++ です。

この検査装置ではステージや測定器等のデバイスを、パラレルIO、シリアルIOで制御する必要がありました。また、待機中・測定中・復帰中などの状態管理が必要でした。

さまざまなインタフェースの入力を監視してそれに応じた動作を行なわなければいけないので、これをすっきり記述するため、Observerパターンを使用しました。大まかには、別スレッドで定期的にポーリングを行なうクラス→各種入力を監視するクラス(I/O毎にクラス作成)→状態を管理するクラス→画面表示クラスといった具合に連鎖的にイベントを起こすのです。クラス間の誘導可能性(クラス図に記述する矢印)はちょうどこれと反対の向きになります。

具体的な記述方法で工夫した点はObserver役にクラスではなくクロージャを使用したこと、Observer役の管理に標準テンプレートライブラリのvectorを使用したことです。クロージャを使用することで必要なクラスの数が減ります。vectorを使うことでTListを使うよりも型安全になります。VBや.NETではイベントの機構を使用すればObserver役の管理をランタイムルーチンがやってくれるのでもっとすっきりと記述できます(VBに関しては他の面でできないことも多いですが)。

Observerパターンを全面的に使用することで無駄なポーリングを回避でき、クラス間の関係がすっきりと記述でき、インターフェース毎のクラスの独立性が高いため入出力インタフェースの追加・変更も簡単になりました。

状態の管理はStateパターンを使用しないでswitch caseで判別しました。Stateパターンは状態1つにクラス1つ必要でクラス内のメソッド数が2個と少なくデメリットのほうが上回っていたからです。この部分のデバッグは非常に煩雑でわかり難く問題点として残りました。状態の管理はFiber(マイクロスレッド)や排他処理+通常のスレッドを使ったほうがよいかもわかりません。その場合異常状態をどのように表現するかがポイントなるものと考えています。

型総称性

オブジェクト指向に基づくプログラミングでは数多くのクラスを扱います。これらのクラスは個々のさまざま処理を実現するために必要となってくるものです。一方、さまざまなクラスに対して同じような処理を行いたい場合があります。リスト・スタック・キューなどは、殆どのクラスのオブジェクトがこれらの処理対象になりえます。

例えばリスト処理をDelphiで実現したければ、大元のクラスTObjectに対してリスト処理するクラスTObjectListを定義しておけば、継承による多態性が利用できるので、すべてのクラスに対してリストが使えるようになります。ここで特定のクラスTMyObjectについてリスト処理する必要があったとします。その場合にもTObjectListをそのまま使うことができますがTOjectListはTMyObject以外のものも扱える点が問題になります。例えば別のクラスTOtherObjectのオブジェクトを誤ってリストに加え、TMyObjectのつもりで取り出すと実行時エラーになります。できればコンパイル時にチェックしたいものです。

そこで、一つの解決策としては改めてTMyObjectのみを扱うクラスTMyObjectListを定義することが考えられます。しかしこれではクラスごとにリスト処理するクラスを定義しなければならず、単にタイプセーフにするためだけの代償としては大きいと感じます。

タイプセーフを保持しながらリスト処理の記述を一回限りで済ませたい。そういった要望に答えるのが型総称性です。古くはAdaのGenericにもありますが、C++ではテンプレート、JavaではJava
Genericがこれに相当します。

オブジェクト指向ではない言語Adaにもあることからもわかるように、型総称性はオブジェクト指向とは異なるパラダイムです。しかしオブジェクト指向で発生する多くのクラスを型総称性で統一的に扱えるという位置づけをするとこの2つは車の両輪となって働くものです。今後型総称性は台頭してくると考えられます。

キーワード:型総称性テンプレートJava genericパラメタライズドクラス

VB.NET vs Delphi

当方では普段はAccessを開発ツールとして使用しています。今回パッケージの開発を計画しており、開発ツールをどれにするか選定する中でVB.NETとDelphi 6を比較検討してみました。

VB.NETとDelphi比較
項目 備考
オブジェクト指向

どちらも、継承などの基本的な機能は揃っている。VB.NETにはデリゲート・マルチキャストイベント・ガーベジコレクタがある。Delphiにはクラス参照型がある. VB.NETは後発だけあって軍配が上がる。

ライブラリの配布

DelphiではVCLをスタティックリンクして単一実行ファイルにすることができる。それに対しVB.NETでは.NET FrameworkがダイナミックリンクとなりExe以外にDLLの配布に配慮しなければならない。.NET FrameworkはSide by Sideで配布できないためDLL HELLに注意が必要。

ライブラリソース Delphi Professional以上を購入するとVCLソースが付属する。ツール類の開発ではライブラリのソースに助けられることが多い。VB.NETにはソースが付属しない。

現在では主に C#, PHP を使用しています(2024/2/17)。

イノベーションを起こさないソフトウェア

当方は普段受託開発をしていますが、2002年3月には受託ではなくSI様常駐で生産管理システムTPiCSの外付けシステムのプログラム作成に携わりました。

このパッケージは資材所要量計算(MRP)に改良を加えたf-MRPの機能を持っています。TPiCS導入の動機は短納期化・在庫削減など経営レベルのメリットの追求にあると思います。システムに関連する部署も営業・生産計画・生産部門など広範囲に渡り、システム導入にはこれらの部署の連携が欠かせません。

ひるがえって当方が普段受注しているシステムはどちらかと言えば現場レベルのメリットを追求するものが多いです。色々なお客様に導入させていただいていると、仕事が半分に減った(効率が2倍になった)と喜んでいただいている事例もあり、お客様の業態に合わせてシステムを提案し構築することが効果をもたらすことを実感しています。

「業務をソフトウェアにあわせる」と言われることがあります。パッケージの特性を生かして経営的なメリットを出すと言う意味ではそのとおりなのですが、このことがパッケージを「ノンカスタマイズで導入すべし」ということに即つながるわけではないと思います。当方のような導入経験をしていると現場レベルのメリットを切り捨てると無視できない損失を生むであろうことが想像できます。経営のイノベーションを起こさないソフトウェアもそれなりに重要です。

TPiCSの導入ではパッケージで経営メリットを追求し、外付けシステムで現場メリットあるいは現場の損失カバーを追求するのがよりよいかたちではないでしょうか。

C++

久しぶりにC言語による開発をしました。といっても前回の開発はソフトハウスとしてではないので、開業以来初めてということになります。

C++は言語仕様が古臭いと思っていましたが、私の知らない間に少し改善されていたようで、テンプレートやネームスペースの機能が追加されていました。例外処理はtry
catchが導入されており、例外のために冗長なコーディングをしなくて済むようになっています。

今回は生産設備に使用するソフトをチーム開発し、私はDLLのコーディングを担当しているのですが、システム設計的には関数インタフェースとなっていてC言語の域を出ていません。オブジェクトインタフェースとすることでライブラリ化しやすいのでしょうが、緻密な設計とメンバーのスキルが要求されるため、なかなか採用できないのかもわかりません。

C++に関して興味を引いたのは標準テンプレートライブラリ(STL)と呼ばれるもので、デザインパターンにつながるものがあり、これを学習しようと考えています。