Nov 15, 2012

MVCパターンの適用限界を考える(4)

1つ前のエントリーで、いわゆる"Model 2"がMVCパターンの用語で説明されるようになったことが、人々のMVCの理解をバラバラにする原因になった、と書いたが、そのことは、前エントリーの参考リンクの先のページでも見られるように、多くのWebページで触れられていることでもあるが、筆者はむしろ、いわゆるGoF本に書かれているMVCの説明が最大の混乱の元だと思っている。"Design Patterns: Elements of Reusable Object-Oriented Software"(邦訳タイトルは「オブジェクト指向における再利用のためのデザインパターン」)、いわゆるGoF本の§1.2にある以下の記述がそれである。

MVC consists of three kinds of objects. The Model is the application object, the View is its screen presentation, and the Controller defines the way the user interface reacts to user input.

MVC also lets you change the way a view responds to user input without changing its visual presentation. You might want to change the way it responds to the keyboard, for example, or have it use a pop-up menu instead of command keys. MVC encapsulates the response mechanism in a Controller object.

A view uses an instance of a Controller subclass to implement a particular response strategy; to implement a different strategy, simply replace the instance with a different kind of controller. It's even possible to change a view's controller at run-time to let the view change the way it responds to user input. For example, a view can be disabled so that it doesn't accept input simply by giving it a controller that ignores input events.
(注:ここでは"disabled"はボタン等が操作できない状態のことを指す)

GoF本の1.2節を開いてまず目を引くのは、図にControllerが無く、1つのModelと複数のViewのみで構成されていることである。上記の引用文にある通り、これは、ControllerがViewの一部として扱われているからである。
MVCの最大の目的はPresentation logicとBusiness logicの分離による安定性の向上(変化する部分と変化しない部分の分離)である、というのはOriginal MVCから一貫した理念であるが、システム全体を(Controller抜きで)ViewとModelに分離すると書くことによって、それを最大限に強調しているのである。これはMVCの本質をわかりやすく表していると思う。

しかし、それから少し読むと違和感を覚えるのが、ユーザー入力を受けたViewがどう振る舞うかを、そのViewのControllerが決める、という記述である。このようなことは、Original MVCには書かれていない。また、Original MVCに書かれている、ControllerからViewへのメッセージ送信についても、GoF本には明確な記述が無い。
もしこれが、Original MVCのControllerの定義にある

Conversely, a view should never know about user input, such as mouse operations and keystrokes. It should always be possible to write a method in a controller that sends messages to views which exactly reproduce any sequence of user commands.

と矛盾しないとすれば、ユーザー入力がViewからControllerへ伝搬されるというのは、"views which exactly reproduce any sequence of user commands"の部分を拡大解釈したものであり、Controllerは入力デバイスのドライバーやウィンドウシステムの役割のみを担うものとしていると解釈するのが妥当であろう。
早い話が、JavaのSwingやQt, GTKなどのイベントドリブンなツールキットを用いて実装されるGUI全体をViewだとしているのである。
図にすると次のようになる。

Class diagram of MVC in the GoF design pattern book
図5-1: GoF本のMVCのクラス図(静的構造図)

Communication diagram of MVC in the GoF design pattern book
図5-2: GoF本のMVCのコミュニケーション図(動的構造図)

ポイントとしては、Viewがユーザーイベントの配信を制御することが可能になっており、Original MVCではV-C間の関連におけるControllerの役割だったものの大部分がViewに移動している。また、ViewはControllerより先に存在する前提であり、Viewが表示されていない状態からControllerがView上にメニュー画面の表示を要求するようなことは想定されていない(Swing等のGUIフレームワークを使えば、最初からGUIが何か表示されているのが普通だから、想像は難しくない)。

ViewとControllerの関連の向きがOriginal MVCと逆であることをわかりやすくするため、Controllerのデバイスドライバー/ウィンドウシステムとしての役割を省いたのが、次の図である。

Communication diagram of MVC in the GoF book
図5-3: GoF本の記述に沿ったMVCのコミュニケーション図(簡略化版)

このMVCのモデル(以下、GoF MVCとする)は、GoF本のテーマにはよく乗っており、同書の以下のデザインパターンが使われていると書かれるのは非常に納得できる。
・Observer
・Strategy
・Composite
・Factory Method
・Decorator

Observer patternが使われることは言うまでもないが、Modelが変化すると、observerとして登録されている全てのViewに何か変化があったことが通知されることを実現するのに使われている。
Observer pattern in GoF MVC
図5-4: GoF MVCにおけるObserverパターン

Strategy patternは、ユーザー入力に対するViewの振る舞いを決めるControllerが、Viewのインスタンス毎に切替可能であることを実現するのに使われている。多くのGUIフレームワークでは、イベントのコールバック関数(リスナー)が動的に登録可能であることに相当する。ここではControllerがViewの一部なのは、上述の通りである。
Strategy pattern in GoF MVC
図5-5: GoF MVCにおけるStrategyパターン

Composite patternは、View同士が包含関係になり、親Viewが受けたイベントを、内包するViewに伝搬することにより、複合ViewをViewと同じように扱うことを可能にするのに用いられている。GUIフレームワークでは、例えばコンテナの中にコンテナを含めることに相当する。Original MVCの"The Model-View-Controller (MVC) Its Past and Present"のP-9にある"Tool as a composite"も、大体同じような内容である。
Composite pattern in Gof MVC
図5-6: GoF MVCにおけるCompositeパターン

Factory Methodパターンは、各Viewのdefault controllerを得るのに用いられる、とされる。これによって具象Viewから具象Controllerへの依存を無くすことができ、例えば具象ViewがControllerのいずれかのサブクラスを指定してインスタンスをcreateする必要が無くなる。GUIフレームワークでは、起動時のパーツの配置や初期設定をアプリケーションのmainクラスがまとめて保持することが、強いて言えばこれに相当するだろうか。(世の中にはあるのかも知れないが、筆者はあまりControllerのFactoryというのを見たことがない)
Factory Method pattern in GoF MVC
図5-7: GoF MVCにおけるFactory Methodパターン

Decoratorパターンは、Viewのサブクラスに共通する機能の追加を、それぞれのサブクラスを新たに作ることなく、委譲を用いて実現するのに用いられる。GUIツールキットでは、例えばGoF本には、各コンポーネント(クラス)にスクロール機能を追加する時に用いられると書かれている。
オブジェクト指向の経験則として、既存クラスの拡張や既存クラスへの機能追加は、派生クラスを作成するより、AdapterやDecoratorなどを使った委譲によって行う方が良いことが多いと言われることがある。小規模な機能拡張で一々、それぞれの機能の有無毎に派生クラスを作成するのは面倒で非効率であることを考えば、納得できる。
Decorator pattern in GoF MVC
図5-8: GoF MVCにおけるDecoratorパターン

世の中にC→Vの関連が無くV→Cの関連があるMVCパターンの図が存在するのは、GoF本の記述が原因であろう。
また、MVCのデザインパターン的な側面を語られる時は大体、V-C間の関連はViewからControllerへの関連であることも興味深い。V-C間の関連がControllerからViewである、Original MVCに則してGoFのデザインパターンが語られることはほとんど無い。
これは、既存の多くのGUIフレームワークに当てはめてMVCを解釈するには悪くないし、MVCが将来そのような考え方に置き換わっていくのは必然なのかも知れないが、それは筆者にはどうしてもMVCの拡大解釈であり、MVCとは別の名前を付けるべきだったように思える。

GoF本に書かれているMVCは、極端に言えば、Controllerが無くても動作可能なMVCアーキテクチャーである。ウィンドウシステムがミドルウェアに、データモデルや表示系アプリがアプリケーション層にあれば、アプリケーション層だけを見るとControllerが存在しない。
実際、GoF本には、何もしないControllerをViewに与えることによって…という記述があるし、Original MVC的にはViewからのModelの更新はあり得る訳だから、システム全体としてController無しでMVCパターンを実装することも可能だということになる。

ControllerはViewとModelの橋渡しに過ぎず、MVCモデルはViewとModelが主役、というのはMVCの本質であり、それを強調することは悪いことではないと思うが、Controllerの役割やC-Vの関連についてほとんど触れられないのは如何なものであろうか。MVCの本質でなくても、Controllerの位置付けはMVCの必須構成要件である。
オブジェクト指向的に考えると、Controllerのオブジェクトは本質的には不要なので、どの方向から考えても結局はControllerの責務は最小にされるべきだという結論になるからだ、としても、ControllerがViewの一部であるように表現してしまうのは、MVCの説明としては問題があるのではないか。

筆者は、GoF本の記述は、テーマ的にMVCについて触れない訳にはいかないから、同書の内容、あるいは現存するGUIフレームワーク(筆頭著者のErich Gamma氏はJavaとの結び付きが強いからAWTだろうか)と整合する為に、このようにされたのだと考えるべきものだと思う。

See more ...

Posted at 17:16 in PC一般 | WriteBacks (0)
WriteBacks

Oct 21, 2012

MVCパターンの適用限界を考える(3)

さて、2つ前のエントリーに書いた図1-1図1-2の最もシンプルなMVCパターンでは、すぐに行き詰まることを多くの人が知っている。というか、これではControllerがメニュー画面を出すことすらできない。Controllerの定義から、メニュー画面はControllerそのものであるが、画面表示するにはModelを変更するしかないからである。

まさか、ControllerがViewに依存しないように、メニュー画面のイメージをModelに送るようにすることはあり得ない。多少はViewの使い方を知っていてViewに対するメッセージをModelに渡すなら、それはControllerがViewに依存していることになるので無駄である。Viewへのメッセージの文法を知っているなら直接Viewにメッセージを送れば良い。

ViewとControllerとの間に関連も依存も無いMVCが最も美しい、と考えるあまり、メニュー画面の動作をもModelに含めるのが正しい、突き詰めると、アプリケーションの動作は全てModelにあるのが正しい、と考える人は少なくないが、それは本来のMVCパターンではない。
実は、originalなMVCに答えがある。

"MODELS-VIEWS-CONTROLLERS"のControllerの定義より

It provides means for user output by presenting the user with menus or other means of giving commands and data. The controller receives such user output, translates it into the appropriate messages and pass these messages on to one or more of the views.
(訳:Controllerは、ユーザーにメニューを見せること、または他のコマンドとデータを渡す手段により、ユーザーからの出力手段を提供する。Controllerはユーザーからの出力を適切なメッセージに変換し、そのメッセージを1つ以上のViewに渡す。)

つまり、元々、メニュー画面のようなControllerの表示は、Controllerが直接Viewにアクセスすることによって行うという考え方なのである。図にすると次のようになる。

Class diagram of MVC with V-C connection
図2-1: Controllerの表示が可能なMVCのクラス図(静的構造図)

Communication diagram of MVC with V-C connection
図2-2: Controllerの表示が可能なMVCのコミュニケーション図(動的構造図)

図1との違いのポイントを挙げる。

  • ConcreteViewにControllerによる表示のためのI/Fがある
  • ConcreteControllerからConcreteViewへの関連がある
  • ControllerのViewのみの変更は、C-Vに閉じて完結される

この具象Controllerと具象Viewとの関連は必須ではない(全てのViewがControllerの表示を行う必要はない、CUIやリモート制御だとModelのViewがControllerのUIを表示しないこともある)ので、抽象Controllerと抽象Viewとの間には現れない。

C-V間の関連が無いMVCは、抽象レベルだけを取り上げた、MVCの一部のエッセンスだけを示したものである。それで済む時もあるが、それが最も美しいということではない。
ソフトウェアのデザインパターンをかじった人ならわかると思うが、抽象クラスだけで構成されるパターンは存在しない。というより、抽象クラスを含むパターンは、オブジェクト指向の「依存関係逆転の原則」の存在箇所の明確化のためにも、最低どれか1つの具象クラスが無いと成り立たない。具象クラスまで含めてのデザインパターンである。

ディスプレイに表示するViewがあって、UIがそのディスプレイ上に無いシステムは、MVCパターンとして考える意味が無い(何の為にViewを分離しているのかわからない)。MVCパターンは元々GUIのあるシステムを対象に含めているので、その要素を省いて語るべきではないと、筆者は考える。

"MODELS-VIEWS-CONTROLLERS"のControllerの定義には、他にもV-C間の具象レベルでの関連が示されている。


Conversely, a view should never know about user input, such as mouse operations and keystrokes. It should always be possible to write a method in a controller that sends messages to views which exactly reproduce any sequence of user commands.
(訳:同様に、全てのviewはマウス操作やキー入力といったユーザー入力には一切関知しない。ユーザーコマンド列を再生成するだけのviewにメッセージを送るmethodをControllerに書くことは可能なだけである。)

この文書には他に"user output"という表現もあって、"user input"と"messages"と"user commands"の違いがわかりにくい(はっきり言って、筆者にはわからない)が、"THING-MODEL-VIEW-EDITOR"のVIEWのEXAMPLE 1の所に
It understands a message asking it for an item that is positioned at a given point within its frame, and a message asking it to select a given item.
(筆者注:この文脈ではframeはControllerによる表示のこと。"a given point within its frame"と書くことにより、"user input"そのものではなく、抽象的な意味合いの位置情報であることを強調しているものと思われる)
One possible sequence for selection is that the Editor reacts to redbug and asks the ListView for the item that is pointed to. The Editor then asks the ListView and any other Views to select that item.
(筆者注:この文脈ではEditorは具象Controller、ListViewは具象Viewのこと)
とあるので、Controllerは、現在Viewがある場所に何を表示しているかを問い合わせることができる、という意味だと解釈するのが妥当であろう。マウス操作による画面上のクリックなど、ユーザーがView上の表示に依存する操作を行った場合、Viewがその位置に何を表示しているかがわからない限り、Controllerはユーザーの指示を解釈できないが、その問題を解決する為の必要最小限のサポートをViewが(あくまでユーザー入力の生データには関知せずに)行うべきだということである。

また、その少し後ろのEXAMPLE 4の所に、


In addition, it will need some operations on the View itself, they have to do with the positioning of the symbols in the diagram.

つまり、Modelの要素の表示位置を変えるなど、Modelの変更とは無関係にModelのViewが変化する場合は、そのためのI/F(呼び出すのはController)がViewに必要になるということである。

また、"MODELS-VIEWS-CONTROLLERS"に戻って、VIEWの定義に、次のような具象M-V間の関係が書かれている。

A view ... gets the data necessary for the presentation from the model by asking questions. It may also update the model by sending appropriate messages. All these questions and messages ...

つまり、ViewがModelを変更することもあり得るということである。これだけでは何のことかさっぱりわからないが、"THING-MODEL-VIEW-EDITOR"のVIEWの所の冒頭に
A View is also able to perform such operations upon the Model that is reasonabely associated with that View.

とあり、その後ろのEXAMPLE 6の所に

It is also able to pass on operations on the network and its activities that are related to this particular View. Typical operations have to do with modifying the current schedule.
(筆者注:ここではnetworkはModel、activitiesとscheduleはnetworkの要素)

とあるので、(Controllerを介して)Modelの要素に十分に直結したView上の変更が発生すれば、それに対応する変更を、Controllerに関係なく(Controllerを介さずに)、Viewが直接Modelに対して行っても良いということなのである。

これらを含め、MVCパターンは次のように図示できる。

Class diagram of MVC without V-C connection
図3-1: MVCのクラス図(静的構造図)

Communication diagram of MVC without V-C connection
図3-2: MVCのコミュニケーション図(図2-2からの差分のみ)

ところで、以前のエントリーに、J2EEやASP.NETなどがMVCを参考にしていると書いたが、これらで使われているのは元々のMVCパターンとは異なる、"JSP Model 2 architecture"または単に"Model 2"と呼ばれるアーキテクチャーである。
一応、Webサーバー側のMVCパターン実装、ということになっているが、基本的にはサーバー側はconnectionlessかつstatelessであり、Viewの出力はHTTP requestに対するresponseとしてユーザーに出力するため、Modelの状態が変化した時にViewが表示を更新する、ということができず、M-V間にObserverパターンの関係が無いのが特徴である。

Class diagram of "Model 2"
図4-1: "Model 2"のクラス図

Communication diagram of "Model 2"
図4-2: "Model 2"のコミュニケーション図

元々、"Model 2"が最初に現れた時にはMVCの用語は使われていなかったが、次第にMVCの用語を用いて説明されるようになった(下記参考リンク参照)。それにより、MVCと"Model 2"がごっちゃにされ、人々のMVCの定義がバラバラになり、MVCをややこしくした。
筆者は、ViewがObserverでないMVCはあり得ないと思うので、"Model 2"もMVCではないと思う(し、結構多くの人に賛同頂けるものだと信じている)が、特にWeb系ではそんなことを気にしない人も少なくないようである。
"Model 2"を含めると本当に話がややこしくなるので、このサイトでは"Model 2"はMVCパターンに含めない。

●"Model 2"の参考リンク

  • Understanding JSP Model 2 architecture - JavaWorld --- 現時点での事実上の原典
  • MVC, Model 2, Java WebApps (Brian's Waste of Time) --- "Then the Web happens and Sun starts talking about Model2 in terms of MVC."("Model 2"がMVCの用語を使って語られるようになり、Sunもそれに乗っかった)ということが書かれている。
  • Java EE 5のTutorialの中の1ページ --- Webアプリの世界ではMVCはModel-2と同じものだとみなされることがしばしばある、と書かれている。
    Note - When employed in a web application, the MVC architecture is often referred to as a Model-2 architecture. The bookstore example discussed in Chapter 4, Java Servlet Technology, which intermixes presentation and business logic, follows what is known as a Model-1 architecture. The Model-2 architecture is the recommended approach to designing web applications.
  • How Struts Implements Model 2 --- "The Origins of Model 1/Model 2"という章に、MVCとModel 2との関係が書かれている。
  • ASP.NET Presentation Patterns --- The original MVCと"Model2"と"ASP.NET MVC Framework"との関係が解説されている。

See more ...

Posted at 19:10 in PC一般 | WriteBacks (0)
WriteBacks

Oct 08, 2012

MVCパターンの適用限界を考える(2)

前エントリーに引き続き、MVCパターンについて、その利点と、何が崩れるとそれが失われるかを考えてみる。以下、一部に図1-1図1-2には無いV-C間の関連が前提になっている記述があるが、それについては次のエントリーで説明する。

  1. Viewの追加/変更がViewクラスに閉じ、Modelに影響しない
  2. ModelはView/Controllerへの依存が無く、安定度を上げることができる
  3. ModelとViewとをそのままにして、Controllerを差し替えることができる
  4. ModelはViewの表示状態に関係なく動作することができる
  5. Model+ControllerとViewを並列実行することができる

(1)は、物理モデルやビジネスロジックと比較して、アプリケーションの表示系(Presentation)は変更が入りやすいことが前提となっており、変更されやすい部分、すなわちViewと変更されにくい部分、すなわちModelとを分離することによって、Viewの変更の影響が及ぶ範囲を限るのが、そもそもMVCパターンの発想の原点であり、第1の目的である。
元々、ControllerはViewとModelとを繋ぐ役割で小規模だと考えられており、Viewの変更がControllerに影響することについては論じられることが少ないが、Controllerが肥大し、変更が入りにくいロジックを含むようになって、しかもControllerがViewに依存するようになると、Viewの変更の影響が変更されにくいクラスに及ぶようになり、この第1の目的に合わなくなる。

(2)は、Modelの品質は特に重要であることが前提にあり、できるだけ他への依存を無くし、個別に重点的にテストして、変更されにくいコードとして確立することを目的とした結果である。もちろんViewやControllerもきっちりテストしてバグの少ないのものに仕上げるべきであるが、ソフトウェアのバグを完全に無くすのは確率的に不可能であることを理解し、特に重点的に品質確保するコードの範囲を選択して絞るのは、現実世界では絶対に必要なことである。
一般に、自パッケージからの依存数をCa、他のパッケージへの依存数をCeとした時にCe/(Ca+Ce)と計算される、クラスの不安定度(Instability)は、0か1に近い方が良いとされるが、M,V,Cそれぞれをパッケージとして、Model内のクラスの不安定度をほぼ0(ViewのObserver I/F以外には外部パッケージへの依存が無い)にできるのがMVCパターンのメリットである。ModelがViewに依存するようになって安定度が下がったり、Controllerに安定度が高いクラスが混ざってController全体の安定度が中途半端になると、このメリットが損なわれることになる。

(3)は、ビジネスロジックやドメインモデルをユーザーインターフェースから分離することによって、ビジネスロジックやドメインモデルに対する同じ操作を複数のUIから実行できる(例えば、キー入力でもマウス入力でも同じ操作が実行できる、異なるメニュー画面から同じ操作が実行できるなど)構成がスムーズに実装できることを意味する。当然、MVCパターンはUIが複数あるソフトウェアを対象にしていることになる。
この利点により、ControllerをModelのテスト用のUIまたはテストコードに置き換えることによって、Modelを全く変更せずにテストすることが可能になる。GUIを持つアプリケーションにテスト用のCUIを追加したり、Modelに対して連続してメッセージ送信する自動テストプログラムを追加することも可能である。
プログラマーの長年の経験則として、結局ソフトウェア開発はいかに効率よくテストすることを可能にするかに掛かっている、というのがあり、特にここ10年くらいの流れである。テストドリブン開発やxUnitフレームワークが邪道だと否定される様子はほとんど無い。テストさえすりゃ作りはどうでもいい、ということに繋がる考え方でうまく行く筈が無いだろう、と、まともな科学者や技術者ならそう考えるものだが、現場は感覚的にも実際にも、ある程度以上きちんと作るのは困難になるので、限界を追求すると、きちんと作るよりきちんとテストすることにコストを掛ける方が成功するのが、人間の知力の限界を物語る現実なのである。

(4)は、計算処理中に表示処理を混ぜることがいかにプログラムを保守しにくくしてきたかを多くの人が思い知った結果であり、MVCパターンに限らず、データとプレゼンテーションを分離することは、よくなされることである。
データ及びそれに対する操作(business logic)に比べ、データの表現手段(presentation logic)はよく変更されるので、business logic内に表示用の処理が混ざっているとプログラマーはよく嫌な思いをする、ということもあるが、その前に、business logicとpresentation logicは品質確保の要件や要求レベルが異なることが多いのである。

(5)は、やはりビジネスロジックと表示系に要求される品質が異なる前提が根底にあるために利点と言えるものであり、表示系がメインのシステムでない限りは、出力先が人間である表示系の動作速度は比較的クリティカルではないので、動作速度が問題になると、自然に表示系をビジネスロジックとは非同期に優先度を下げて動作させようということになる。
逆に言うと、MVCパターンは表示系に求められる性能が高い、表示系に対する要求がクリティカルなシステムには適さない可能性がある。極端に考えると、例えば映像効果のためのシステムでは、表示デバイスの制御が問題の中心であり、映像が最終的な出力なので、映像出力系がModel、目的の映像出力を得るためのUIの表示系がViewだと設計することが考えられる。なので、もしUIに表示効果バリバリのフレームレートの高い描画が求められると、その描画系はViewではなくModelとして扱う必要が出てくることになり、本来のModelとViewのためのModelと、2つのModelが現れることになる。その2つのModelの調停をどこが担うか(Controllerが担うか、それら2つのModelを統括するMagager Modelを作るか)という問題もあるし、そもそも変更が入り易いUIをModelから分離するという目的に反してしまう問題がある。

See more ...

Posted at 23:47 in PC一般 | WriteBacks (0)
WriteBacks

Sep 19, 2012

MVCパターンの適用限界を考える(1)

MVC(Model-View-Controller)パターンは、UIを持つアプリケーションのデザインパターンとして非常に有名であり、特にこれを参考にしたJ2EEやASP.NETを始めとするWebアプリのフレームワークが成功を収めてきたことから、その価値は誰もが認める所である。
しかし、これをローカルシステムのGUIアプリケーションの設計に適用しようとすると、妙に難しいと感じた人が少なくないのではないだろうか。

MVCパターンは、元々Smalltalk-80の開発において考案され実装されたものらしいが、MVCの考案者によると、


MVC was created as an obvious solution to the general problem of giving users control over their information as seen from multiple perspectives.
("The original MVC reports"より)すなわち、複数の視点で表現されたユーザー情報を制御することを可能にする、という一般的な問題に対する自明な解として考案したものとのことであり、Smalltalk専用に考案されたものではなく、ネットワークシステムの為に考案されたものでもない。実際、MVCの実績はSmalltalk以外の言語で上がっており、MVCのアイデアに通信ネットワークの存在を仮定するようなものは見当たらない。であればどんなGUIアプリケーションでも有用なはずだ、と推測するのは全く自然なことである。

筆者も、当然MVCはローカルシステムでも有効なはずだと10年前から信じており、MVCの適用がうまく行かないのはMVCの理解が間違っているからだ、と思ってきた。しかし、この10年間でたぶん4回くらい、仕事でMVCを取り入れようとしたソフトウェア開発のプロジェクトの様子を知る機会があったが、いずれもほとんど成果が上がっていないようだった。見た感じ、MVCの理解が間違っているような気がすることも多かったが、それにしてもそんなに効果が出ないものだろうか。
実は筆者も、プライベートで何回か、MVCパターンを意識してプログラムを作ってみたことがあるが、MVCの価値を感じたことは一度も無かった。そういえば、MVCパターンの利点というのは、根拠のあるもの、具体的で検証可能なものはほとんど聞いた覚えが無い。オブジェクト指向設計と同様、経験的にやりやすかったから、現実にうまく行ってるから、という理由で推奨されているような気がする。それなら、大規模システムに有効なオブジェクト指向設計がミッションクリティカルなシステムに適さないのと同様に、MVCパターンにも苦手な分野があるのではなかろうか。
…ということを最近になってやっと考え始めたので、調べたことや考えたことを少しずつここに書いてみることにした。

MVCを知っていれば、既存のMVCを応用したフレームワーク(アプリケーションの枠組み及びアプリケーションの開発を支援する環境)が使える、というのは現実的なMVCを知るメリットではあるが、そういったフレームワークを使わない場合、MVCパターンをアプリケーションの設計に取り入れるメリットは何だろうか。

まず、筆者がずっと、きっとあると信じてきた、MVCパターンの利点を挙げる。

  1. Modelが再利用可能なので、ViewやControllerが交換可能で、容易に追加できる
  2. システム分割方針が分かりやすくなり、担当者毎、モジュール毎の役割分担が明確になる
  3. ソフトウェア構造としてM,V,Cが分離しやすく、従って独立して設計しやすい
  4. M,V,Cそれぞれのプラットフォーム(実行環境)やコンポーネント(共通モジュール)を別々に設計することも容易である

しかし、実際にはそうはならないことも多い。

(1)については、UIが複雑に、またビジュアル(グラフィカル)で応答性を高めるほど、VはMに限らずCの状態も表示するためにCに依存するようになり、特にポインティングデバイスやタッチバネルの処理で顕著なように、ユーザー入力が表示状態に依存してCがVに依存するようになるため、VとCの結合が強くなり、VやCが単独で交換可能にならなくなる。V+Cのセットでなら交換可能な構造が保てても、そうなると既にV+Cの規模が巨大になっているので、もう1つ別のV+Cを作る労力が、もう1つアプリ全体を作るのと大して変わらないことになってしまう。

(2)については、上述のようにUIが高度になるほどV-Cの関係が密接になって境界がわかりにくくなる他に、元々M-Cの境界がはっきりしないという問題がある。
あるモデル(ユーザーデータとその操作手段)を制御(操作)するアプリケーションは多岐に渡り、極端に言うと無限のバリエーションがあり得るんだから、アプリケーション固有のロジックはCに置くべきだろうという考え方もあれば、有名な"Skinny Controller, Fat Model"のスローガンに代表されるように、ロジックはできるだけModelに置くべきだ、という考え方もあり、システムに機能追加する時にあるロジックをMに入れるべきかCに入れるべきかは、人によって判断が分かれるのである。

(3)は、例えばModelを含めてシステムに機能追加する場合、Modelに追加するI/FはViewを意識して設計せざるを得ないことは普通に起こる。何も考えずにModel内のあらゆるデータを参照可能にする為にI/Fを揃えようとすると開発量が多くなることもあるし、複雑な変換や抽出を必要とする巨大なデータをそのまま渡されるとViewの処理が大変になるので、ある程度ユーザーにとって意味のあるまとまりにして渡してくれ、と言われることもある。
VとCが独立に設計できなくなりがちであるのは上述の通りである。

(4)は、実際にJ2EEではModelのコンポーネントとしてJava Beans(EJB)、ViewのプラットフォームとしてJSP technology、ControllerのプラットフォームとしてServlet technologyがあると説明されることがあり(参考リンク:[@IT 1] [@IT 2] ["Multitiered Applications" in Java EE tutorial])、M/V/C別々にプラットフォームを構築しやすいように見えるが、よく見ると、EJB以外は汎用的すぎて、M,V,Cそれぞれに特化したプラットフォームとは言い難い。Java BeansはVでもCでも使われる仕組みであるし、JSPはMだけでなくCにもアクセスできるし、作ろうと思えばJava ServletだけでMもVもCも作れる。M,V,Cそれぞれのプラットフォームとしても向いている、用途が広いテクノロジーを使っているのである。
むしろ、M/V/Cそれぞれのプラットフォームを作ろうとすると、そこまで汎用的で高度に洗練されたものを作ることになるのだろうか、という疑念すら抱かせる。

大体、一般にソフトウェアのデザインパターンの価値は、設計のノウハウであることよりもむしろ、設計者間の共通言語として使われることにあるのである。大概のパターンは、それを知らなければそういう構造にならないような、思い付きにくいアイデアではない。また、シンプルで普遍的にまとめられているものが多いので、その構造をにすると直ちに多くのノウハウが取り込める訳でもない。デザインパターンのノウハウの本質はその構造にした理由にあるのである。ある設計の基本構造とその構造にする理由とを合わせて一言で伝えるためにデザインパターンはあるのである。理由はおろか、パターンの構造自体も設計者間に共通認識が無くて伝わらなければ、デザインパターンとしての価値が無い、印籠やブランドの類でしかないのである。

さらに考察を進める前に、MVCパターンをおさらいする。
上記MVCの考案者"MODELS-VIEWS-CONTROLLERS"によるModel, View, Controllerの定義は、次のようなものである。


Model
実世界における問題を、特定の視点で抜き出したものを表現するもの。言い換えると、実際の問題や事象をモデル(模式、模型)化するもの。("Models represent knowledge."の"knowledge"は問題や事象に関する知識のこと。)

View
Modelの視覚的な表現。1つのViewはModelに取り付けられ、Modelに対して問い合わせをすることにより必要なデータを取得する。Viewはそれが取り付けられるModelのことを良く知っており、Modelへの問い合わせは全てそのModelで使われる言葉を用いてされる。 ("It may also update the model by sending appropriate messages."についてはややこしくなるのでとりあえず保留。)

Controller
ユーザーとシステムとを繋ぐもの。画面上にそれとわかるように表示することにより、ユーザーに入力手段を提供する。また、ユーザーによって入力されたコマンドをメニュー等によって表示するためのメッセージをViewに送信する手段を提供する。
ControllerはViewを補完せず、Viewの表示に介入しない。同様に、Viewはマウス操作やキーなどのユーザー入力そのものには関知しない。

次に、MVCパターンの最もシンプルな構成を、UMLで表現してみる。
Class diagram of MVC without V-C connection
図1-1: 最もシンプルなMVCのクラス図(静的構造図)

Communication diagram of MVC without V-C connection
図1-2: 最もシンプルなMVCのコミュニケーション図(動的構造図)

上記の"MODELS-VIEWS-CONTROLLERS"の定義にはViewからModel、ControllerからViewへの関連が含まれているが、それらは必須の構成要件ではない(その関連におけるメッセージのやり取りが無くてもMVCパターンは動作する)ので、最もシンプルな構成ではそれらを省いている。

ポイントは、次のように集約される。

  • 1つのModelに複数のViewを動的に登録することができる
  • ModelはViewの存在を知っているが、Viewの具体的な実装を知る必要が無い
  • 1つのModelを複数のControllerから操作することができる
  • ViewとControllerは完全に独立している

なお、1つのViewが複数のModelに関連する、つまり複数のModelの表示を受け持つことも可能だとされることが多い(上記MVCの考案者による"THING-MODEL-VIEW-EDITOR"でも"each View being capable of showing one or more pictorial representations of the Model"という記述がある)ので、M-V, M-Cの多重度は1対多でなく多対多が正しい、と思われるかも知れないが、Modelを交換可能とすることはMVCの本質ではないことと、そのようなViewは役割をModelの数だけ分割し、その数だけMVCのセットがあるのと同じことと、一般にオブジェクト指向設計において多重度が多対多の関連は意味が曖昧であることを理由として、M-V, M-Cの多重度は1対多と書いている。
(続く)

See more ...

Posted at 21:48 in PC一般 | WriteBacks (0)
WriteBacks

Sep 01, 2012

将棋の棋譜再生iアプリを作ってみた

昨年はまあまあ満足できた将棋大会に、今年も参加したら、全く思い通りに指せなかった。
将棋では、相手に押さえ込まれて何もできなくなるというのはよくあることだが、そういう感じではなく、筆者の指し手に相当な自由度、選択肢があったのに、さっぱり頭が回らなかったのである。
何も思い付かないまま、何もできないまま、持ち時間が無くなって、2敗して予選落ちしてしまった。大会規程により、今年はA級で参加せざるを得なかったのだが、あの調子だとB級でも散々だっただろう。
そんなことあり、もうちょっと将棋を勉強しようという気になった。

図書館から定跡本を借りて時々読んでいると、ちょっと気が向いた時に見れないことが多いことに気付いた。筆者は割と外出先にでも本を持ち歩く習慣があるのだが、確実な待ち時間が予想できないと持ち歩かないようであるのと、本は携帯電話と違って片手では開けないことが要因としてあるようである。
それでなくても、携帯電話でアプリを動かしていると、将棋盤を再現できるアプリがあればいいのにな、と思うのは自然なことであろう。

筆者が持っている携帯電話は、昨年の将棋大会の日に買った、ドコモのP-01Cである。iモードアプリしか動かない「ガラパゴス携帯」である。スマホは保有していない。
これで動く棋譜再生アプリが見つかれば良かったのだが、毎月費用が105円とか315円とかかかるものを除くと、適当なアプリが見つからなかった。1つ、棋譜管理に特化した、丁度いいアプリがあった(今はもう見つからない)のだが、大抵のiモード機種に対応してるのに、なぜかP-01Cは対応しておらず、ダウンロードもできなかった。

それに対して、スマホ用の棋譜管理ができる将棋アプリはごろごろ転がっているようである。この機会にスマホを購入しようかと思ったが、それだけの為にスマホを買うのはどうかと思った。

棋譜再生アプリを自分で作ろうか、と、ちょっと思い始めた。しかし、筆者はiアプリの作り方をほとんど覚えていなかった。というより、5年ほど前に簡単なものを1回か2回作って実機で動かすのを試しただけなので、覚えたことがある程でもない。もしやるとすれば、アプリの一挙手一投足に至るまで、DoJaのAPI仕様書を検索しまくることになる。

今や世界シェアの50%以上がスマホというこのご時世に、今更DoJaアプリの開発に挑戦すべきだろうか。今から覚えるならAndroidかiPhoneのアプリの作り方であろう。この機会にスマホを1台買おう、とも思ったのだが、スマホを買うと、上述の通り、自分で棋譜再生アプリを作る必要性が無くなってしまう。

それに、スマホを買うと、きっともうP-01Cを使わなくなってしまうだろう。Javaに思い入れがある者としては、このまま自作のJavaアプリを1つも作らずにP-01Cを使い終わることは、ケーキを半分残して腐らせてしまうくらいに勿体ないことなのである。そうなれば、将来必ず後悔するであろう。その前に、このままでは必ず、廃棄する直前に、供養の為にもiアプリを1つは作って動かさなければならないという強迫観念に襲われるに違いない。もはやあと1回iアプリを作ることは筆者の宿命なのである。

・スクリーンショット
screen shot
※この画面はiDKDoJaのエミュレーターで作成したもので、実機の画面とは異なる。
 実際には、盤面の駒も含めて、文字は機種毎のシステムフォントが使用される。
i-mode用ダウンロードページへのリンク
・ボタン操作
 1-9: 1手進める
 *: 1手戻す
 #: 次の分岐点まで進める、または前の分岐点まで戻す
 0: 初期状態に戻す
・ソースコード(iDKDoJaプロジェクトのsrc/に配置)
 iKifuPlayer.java
 Board.java
 KifTree.java
・作成者
 maruinen(筆者のネット将棋でのハンドル)
・棋譜データの例(iDKDoJaプロジェクトのres/に配置)
 Kifs.txt
 空行は1つの棋譜の終わりとして扱われる。ファイル名は固定。
・棋譜データの入れ替え方法
 iDKDoJaと上のソースコードを使って、アプリ自体を作り直すしか無い。

See more ...

Posted at 22:31 in Java | WriteBacks (0)
WriteBacks

Aug 17, 2012

(Mac)Win2000+JDK6のインストール@VirtualBox

今更ながら、VirtualBoxという無料の仮想マシンソフトがあることを知った。
昔からWindows PCではFreeBSDを動かす為にVMWareを愛用していたし、MacではUNIX系のツールが一通り使えてFreeBSDは必要なかったので、あまり興味が無かった。
今週、ちょっとMacでWindowsを動かしたいと思ったので、調べたら真っ先に見つかった。4年前にMacBookを買った時は、そういう時はBoot Camp(HDDの起動パーティション分割ツール)かParallels Desktop(仮想PCアプリ)を使うのが定番だったし、個人的にはCrossOver Mac(OS不要でWindowsアプリを動かすランタイムライブラリのようなもの)に注目していたし、今年買ったMacBook Airには最初からBoot Campが入っているようなので迷ったが、迷わずにVirtualBoxを使ってみることにした。

今回インストールしたのは、
Windows 2000 Professional
Microsoft Office 2000
柿木将棋VI
Java SE 6 JDK (JDK6)
である。この中で、特にJDK6のインストールにてこずったので、そのことを中心に記録する。

●仮想マシンへのWindows 2000のインストール

  1. インストールCDを、何らかの方法で、ディスクイメージファイルにしておく
    筆者はMacBook(CDドライブあり)の「ディスクユーティリティ」で、CDドライブを選択して、「ファイル」→「新規」→「"..."からのディスクイメージ」で作成した。
  2. VirtualBoxを起動して、仮想マシンを新規作成する
  3. インストールCDを仮想マシンのCDドライブに挿入する
    1つの方法としては、VirtualBoxマネージャーの画面で、新しい仮想マシンを選択して、「設定」→「ストレージ」の画面でCDドライブを選択して、「属性」欄のCDアイコンをクリックして、「仮想CD/DVDディスクファイルの選択」を選択して、インストールCDのディスクイメージを指定する。
  4. Windowsのインストール
    仮想マシンを起動するとWindows 2000のインストーラーが起動するので、それに従ってインストールする。
  5. Guest Additionsのインストール
    Windowsを起動して、VirtualBoxのメニューから「デバイス」→「Guest Additionsのインストール」を実行すると、それのインストールCDが挿入されたことになって、インストーラーが起動する。
  6. Service Pack 4のインストール
    SP4は今でもMicrosoftのページから入手できる
    これをインストールしないと、JDKのインストーラーが動かない。
    筆者の環境では「高速インストール」版はエラーになったので、「ネットワークインストール」版を推奨する。

●仮想マシンへのOffice 2000のインストール

  1. インストールCDを、何らかの方法で、ディスクイメージファイルにしておく
  2. インストールCDを仮想マシンのCDドライブに挿入する
    仮想マシンが起動していれば、ウィンドウ右下のCDアイコンをクリックすれば、「仮想CD/DVDディスクファイルの選択」という選択肢を含む、CDドライブのメニューが出てくる。
  3. Windows内でCDドライブからOffice 2000をインストールする
  4. Service Pack 3のインストール
    SP3は今でもMicrosoftのページから入手できる
    Office 2000 Service Release 1 (SR-1)がインストールされていなければ、先にSR-1をインストールする必要がある。入手元はわからなくなったが、SR-1のインストーラーのファイル名はo2ksr1.exeであり、128kのネットワークインストーラーでも、現在も使用できた。

●仮想マシンへの柿木将棋VIのインストール
押し入れからWindows 2000やOffice 2000のCDを発掘したついでに見つけたので、インストールした。
手順は同じで、CDのディスクイメージを作成し、マウントし、仮想マシン内でCDドライブからインストールした。

●JDK6のインストール
現在、SunじゃなくなってOracleのサイトからJava SE 6 Update 33 (jdk-6u33)がダウンロードできるが、これをWindows 2000にインストールするのは極めて難しい。このインストーラーを実行すると、

エラー 1721。この Windows インストーラパッケージに問題があります。

とか
このWindows インストーラ パッケージには問題があります。このインストールを完了するのに必要なDLLを実行できませんでした。

とか
インストールに失敗しました
このウィザードは、Java(TM) SE Development Kit 6 Update 33のインストールを完了する前に中断されました。改めてインストールする場合は、再度セットアップを実行してください。
「完了」をクリックして、ウィザードを終了してください。

とかいうエラーが出て、先に進めないのである。
共通するのは全てWindowsのInstallShieldの問題であり、webで検索すると、Win2000 SP4ではInstallShieldがゴミを残すことがあり、それを消すと解決したとか、ウィルス対策ソフトが邪魔していたとか、様々な情報及び報告があり、実際何かするとインストーラーの挙動が変わったりするので、これだけ情報があればきっと解決するだろう、あと一息だ、と思わせるのだが、その何らかのトラブルがあり失敗しました、原因はわかりません的なメッセージを読む度に絶望を感じるのである。
「Win2000 SP4の更新プログラム ロールアップ 1」Windows Installer 3.1は何らかの変化をもたらし、期待させたが、同じだけ絶望感ももたらした。
筆者は既にそこまでにのべ丸2日の時間と集中力を潰しており、これ以上取り組むと無駄になる予感と、諦めるとそこまでに費やした労力が完全に無駄になってしまうジレンマがあった。そのジレンマと向き合うことにより、ようやく、JDKのバージョンを下げて解決すればそれでいいという妥協の仕方があることに気付き、多数のJDKをダウンロードしながら情報を探した所、Win2000でjdk-6u24のインストールが静かに中断されるというスレッドを発見した。それは個人的に世紀の大発見であった。それに呼応してjdk-6u23のインストーラーを入手して実行すると、何のトラブルも無くインストールに成功した。

jdk-6u23は、オラクルのアカウントの作成が必要(Sun Java siteのアカウントではサインインできなかった)など面倒だが、Oracle Java ArchiveJava SE 6 Downloadsのページから入手することができる。

See more ...

Posted at 15:36 in PC一般 | WriteBacks (0)
WriteBacks

May 28, 2012

VMWareでのFreeBSD 8.3のXの設定

たまにVMWare上で使っていたFreeBSD 7.3がFTPサイトでもサポートされなくなり、パッケージの追加が困難になったので、久々にFreeBSDをバージョンアップすることにした。

とりあえずFreeBSD-8.3-RELEASE-i386-bootonly.isoをダウンロードして、CD-ROMとしてマウントして起動してインストーラーからUpgradeを選んだら、あっという間に8.3にバージョンアップができたのだが、X Windowを起動すると、以前と同じように使えたが、CPU使用率が常に100%になるようになってしまった。
topで見ると、haldというのが常に走り続けていた。ここへの書き込みによると、haldがlibusbに依存するように作られていると、8.xのカーネルのUSBドライバーと不整合が起こって、このようになるらしい。haldをlibusb無しでmakeすれば直るらしいのだが、どうせこの状態だとsysinstall→Configuration→Packagesからのパッケージの追加が困難(関連パッケージのバージョン違いが多発する)なので、やっぱり仮想マシンをもう1つ作って1からインストールすることにした。

何も考えずにインストーラーからStandardを選んで、インストールが完了したらxorg-serverとxf86-video-vmwareととxf86-input-vmmouseと適当なXアプリやら何やらを追加して、いざ再起動してXorg -configureしてXorg -config xorg.conf.newしたら、 画面が真っ暗になってしまった。
最近のFreeBSDは、バージョンアップする度に、Xの設定方法が変わっていてトラブルが起こる。XサーバーがX.OrgじゃなくてXFree86だった頃は、長年使ってたこともあって、なんとなく勘があったのだが、Xorgになってから、さっぱりわからなくなってしまった。FreeBSDを7.2から7.3に移行した時も、haldやらdbusやらxfbdevやらの設定が必要になったりして、相当手間取った。
今回も、またか、と、げんなりしながらWebで色々調べて、

Xorg -config xorg.conf.new -retro
とすると、Xが起動成功した時に真っ黒にならず、マウスカーソルも表示されて、やれやれ、と思いながら、xorg.confを/etc/X11に置いてstartxとすると、Command not found.になってしまい、さらにげんなりした。何でこんなにちょくちょく色々あれこれそこもかしこも何やらかんやら変わるんだろうな、全く。

最終的には何とかその日の内にXが使えるようになったのだが、これも要るのか?これもか?とパッケージを追加しまくって、明らかに無駄だったので、もう一度整理してインストールし直した。結局、以下のようにして、VMWare 5.5及びVMWare Player 5でFreeBSD 8.3のXを動かすことができた。

■インストールしたパッケージ
xorg-minimal-7.5.1
xf86-video-vmware-*
xf86-input-vmmouse-*
xkbcomp-1.2.3
xorg-fonts-7.5.1

■xorg.confの作成と動作確認
X -configure
X -config xorg.conf.new -retro

■入力デバイスの設定
・/etc/rc.confに次の1行を追加

hald_enable="YES"

・再起動後、コンソールにメッセージが出続けるので、次のコマンドを実行
hal-disable-polling --device /dev/acd0

・日本語キーボードの設定
以下を/usr/local/etc/hal/fdi/policy/policy10-keyboard-jp106.fdiとして作成
<?xml version="1.0" encoding="UTF-8"?>
<deviceinfo version="0.2">
<device>
<match key="info.capabilities" contains="input.keyboard">
<merge key="input.x11_options.XkbRules" type="string">xorg</merge>
<merge key="input.x11_options.XkbModel" type="string">jp106</merge>
<merge key="input.x11_options.XkbLayout" type="string">jp</merge>
</match>
</device>
</deviceinfo>

参考:http://freebsd.xo-ox.net/x11/xorgconfnv160

See more ...

Posted at 23:38 in PC一般 | WriteBacks (0)
WriteBacks

May 15, 2012

「課題」とは何ぞやという課題

ソフトウェア開発の現場では、よく「課題管理」という言葉を目にする。筆者はソフトウェア開発(間接業務含む)以外の仕事をしたことが無いので、他の業種ではどうかわからないが、特にソフトウェア開発の世界では、「課題管理」という言葉が多用される、というか、安易に使われているような気がする。まるで、ソフトウェア開発というものは、計画通りの進捗を阻害する要因は全て「課題」であり、「課題」を見つけて1つ1つ「解決」していけばいいだけのもの、と言わんばかりである。実際、現場から離れて「プロジェクト管理」なる仕事をする者の中には、本人の業務はMS Projectなどを使って進捗をグラフィカルにするだけの「進捗管理」と、MS Excelなどを使って「課題管理表」に行を追加して「状態」列を「完了」にして行を灰色または非表示にするだけの「課題管理」の2つしかしない者も少なくない。

筆者はこれまでに10以上のプロジェクトを見てきたが、大体どこのプロジェクトにも開発計画と「課題管理表」があり、解決していない「課題」が残っていると上司に追及されるような、外部報告用の「課題管理表」に挙げられた「課題」はどんどん「解決」して行くのに、現場で管理する本当の課題管理表に挙げられた「課題」はどんどん溜まって行く、という傾向があるようである。

少し気の利いたプロジェクトなら、報告用でない現場の課題管理表は、解決方法がわかっている「課題」とそうでない「課題」とを分離して、さらに前者の内作業計画もされたものは計画に、計画無しで作業するものは「TODO」に移動するなど工夫するので、ある程度、何らかの似たような性質のある「課題」に分類されるのだが、それでも、感覚的に粒度や問題の次元が異なる「課題」が同じ管理表にあることがほとんどである。

筆者も一応プロジェクト管理の仕事をしていたことがあり、延々と課題管理表を作っていたある時、「課題」って一体何だろうと思って、相当思いふけったことがある。その時には自分なりの答えを出したのだが、先週、再び混乱することがあったので、もう一度自分なりの考えを整理する。

実際の仕事上で「課題管理」を目的とする時の「課題」の意味は結構広く、緊急のトラブルやバグを除くと、イレギュラーに発生する、計画通りの作業遂行の障害となり得る要因は全て「課題」と呼ばれることがあると思う。筆者が管理していた課題管理表はそのような状態だったので、それらの課題を、管理表上で以下のように分類した。


問題
解決案が想像もできていない課題。

テーマ
長期プランで対応する課題。

調整事項
外部と調整して対応する課題。

調査事項
内部で調査する課題。分析事項含む。

検討事項
内部で検討して対応する課題。決定事項含む。

ToDo
単なる作業、またはすることが明確な課題。

A.I.(アクションアイテム)
ToDoと同義。会議に関係する文脈において使用され、物事を一歩進める意味合いを強めるために使用される。または、会議したことによって明確になったこと、その会議をしたことに意義あったことを強調するために使用される。

リスク
課題となる可能性のあるもの。

mystery
不思議なこと、謎なこと。


もし、課題管理表とは別にToDoリストとリスク管理表があれば、上記の内、ToDoとA.I.とリスクはそちらに移動されていたであろう。

これは7年くらい前に考えたことだが、今見てもまあまあ的を射ていると思っている。しかし、世の中にはそれでは「課題管理」とは呼べない、それでは「課題管理」ができないと言う人々が居る。曰く、「次のアクションが明確になっていないものは『課題』ではなくて『問題』である」なのだそうだ。
課題表の「対策」欄に次に行うべきことを具体的に書けないのなら「課題」じゃないから課題管理表に書くな、課題管理表に記入するのならその対策を具体的な「アクション」に「落とし込」んでからにしろ、という訳である。

「次のアクションが明確になっていないものは『課題』ではなくて『問題』として区別する」という考え方が広く出回っていることは、筆者も知っている。大体、


リスク
まだ具体的な障害になっていないが、障害になり得るもの

問題
具体的に発生した障害

課題
対処すると決まり、対処方針が明確になった問題

ToDo
担当者と期日が明確になった課題


というような定義で、「リスク」の一部が「問題」になり、その一部が「課題」になり、その一部が「ToDo」になる(または、そのように「細分化」/「break down」する)というようにして、「リスク」と「課題」と「ToDo」の区別を明確にしようとか、解決できないままで残る「課題」を発生させないようにしようとする発想によるものだろう。

しかし、このような考え方は机上の空論だと筆者は考えている。その理由は以下である。


  • 大抵、「課題」ではなく「問題」だとして「課題管理表」から除外されたものをどのようにして管理するのかについて言及されない。
    そもそも、「問題管理」などという言葉は見たことも聞いたこともない。課題管理において解決困難な課題が存在するからと言って、難しい課題への対策を考えるのではなく、難しい課題を「問題」と言い換えることによって、「課題管理」だけをやりやすくしようとしているだけである。
    もし「問題管理表」が別に作られたら、それらが「課題管理表」にあった時と同じ問題が「問題管理表」に生じるだけである。

  • リスクマネジメント上のリスクが現実の問題となっても、リスクは依然リスクである。
    あるリスク要因が現実の問題となるのが1度きりとは限らないし、実際には発生しなければそれがリスクでは無かったことにはならない。リスクは課題の確率事象であり、リスクかどうかと実際に発生したかどうかは関係がない。
    リスクが課題管理表やToDoリストに(コピーされたのではなく)移されたのなど見たことがない。もし「リスク」が「問題」にbreakdownできてしまうなら、「リスク」はゼロにできるということになってしまい、リスク管理というものが意味を失うだろう。

  • 次のアクションが明確でなければ「課題管理表」に書くべきでないというのは、上司が部下に対して、問題を報告するなら解決方法も一緒に持って来い、と言ってるようなものである。
    それはつまり、解決方法がわからないなら問題を報告するなということであり、解決方法を考える責任を自分で負いたくないということである。

  • そもそも、通常の会話で、「課題」とか「問題」とかいう言葉を次のアクションが明確になっているかどうかで使い分けることは考えられない。「それは今後の課題だ」「難しい課題だ」と言う場合、対応方針が明確になっていることは稀であるし、「それは問題だ」と言うのは、不正や否定のニュアンスを含むこともある。
    もしアクションが明確かどうかで用語を変えたいのなら、「課題」や「問題」という単語を使うべきではないと思う。(そのような呼び分けに興味が無いので、それらに代わる造語を考える気もしない。)

  • 次のアクションが明確になっているかどうかは、人による。
    能力や権限がある人ならすぐに何らかの行動が起こせる課題でも、人によっては他人に依頼するか、行動を起こす前の準備が必要な場合がある。
    対処できる人に対処する暇があるかどうかで「課題」か「問題」かが異なるのでは、「課題」の定義として適当だとは到底思えない。

例えば、「仕様書の完成が遅れているため、詳細設計に手戻りが生じている」というのは、次のアクションが明確でないから、それは(課題表に書くべき)「課題」ではなくて「問題」であり、「仕様書の完成が遅れている原因を分析してボトルネックを特定する」や「仕様書の完成を急ぐ為、誰々に応援を要請する」は次のアクションが明確だから「課題」だと言う。
しかし、「ボトルネックを特定する」は明確なアクションと言えるのだろうか?なぜ原因を分析する前にボトルネックの存在がわかるのだろうか。単純に担当者の作業量が増加しているのかも知れない。分析してみないとわからないのではないか。もし「ボトルネック」の定義の違いだ、作業遅延の原因となり得るものは全て「ボトルネック」と呼ぶことが可能なのだ、と言うのなら、「ボトルネックを特定する」は「分析する」と同じ意味になり、その文に意味が無くなってしまう。
そして、「仕様書の完成が遅れている」のが課題なら、自動的にその原因の分析が次のアクションに含まれるのではないだろうか。つまり、課題管理表に「仕様書の完成が遅れている原因を分析してボトルネックを特定する」と書かれていても「仕様書の完成が遅れている」と書かれていても、何ら変わりはない。
課題を表す文章が「すること」を表していないから問題、とかいう国語的な話は興味が無いが、日本語の語尾が「〜する」だったら次のアクションが明確だとは限らないし、「〜が無い」とかでも十分に明確なこともあるだろう。むしろ、「課題」管理表に「すること」が並んでる方が、筆者にとっては遥かに日本語として気持ち悪い。

もし、具体的な次のアクションが決まらない限り課題管理表に挙げられないのなら、それまではその「問題」はどのようにして管理する(最低、記録しておく)のだろうか。
ひょっとして、どんな課題でも、即座に次のアクションを決めながら課題管理表に記入できるという前提があるのだろうか。
「課題」は解決の仕方、対処方法が1つとは限らないし、解決するコストが高ければ回避するしかないこともある。そんな場合、その課題は回避すると決まるまで課題管理表に書かないのだろうか。
課題というのは、対策をこれから考えるために、対策が空欄のままでも存在するのが当たり前だと思う。次のアクションが決まっていなくても、課題が報告、管理されないよりは報告、管理される方が良いと考えるのが普通であろう。
それに、次のアクションが明確になっていれば、それは課題ではなくてToDoなのではなかろうか。

という訳で、次のアクションが明確でなければ「課題」ではなくて「問題」だとか言うのは問題のすり替え、問題の先送りでしかないと思うのである。

See more ...

Posted at 23:55 in 雑記 | WriteBacks (0)
WriteBacks

Apr 14, 2012

Macのマルチ画面環境の設定

11.6インチ液晶のMacBook Airを買って、13.3インチ液晶の白MacBookをお蔵入りさせた(CD/DVD-ROMドライブが必要な時は引き続き使うのであるが)ら、やっぱり11.6インチのLCDは小さく、使用頻度は激減したが一応まだ残しているWindowsデスクトップPCの17インチのモニターに繋ぎたくなった。
しかし、MacBook Airからケーブル1本でMini DisplayPortからTVにHDMI出力できるらしかったので、やっぱり26インチの液晶TVに接続することにした。

ケーブルを入手して実際に繋いでみると、一発でMacBook Airと同じ画面がTVに表示されて、「ディスプレイ設定」画面が表示された。出力画面サイズを変えたりTVの設定を変えたりしてもどうしても文字がぼやけるが、それでも画面が大きいのは快適であることがわかった。
出力解像度は1600x900(LCDとのミラーリング無し)、TV(Panasonic TH-26LX60)側の設定は、試行錯誤の末、
バックライト「+10」(TV番組受信用は+30)、
ピクチャー「+25」(TV用は+30)、
黒レベル「0」、
色の濃さ「0」、
色あい「0」、
シャープネス「±0」(TV用は+10) 、
色温度「中」(TV用は高) 、
エッジ補正「中」(以下TV用は無設定)、
細部補正「弱」、
ガンマ補正「中」、
黒伸長「+8」、
白文字補正「+8」
にした。これでLCDが狭いことは全く問題でなくなった。13.3インチモデルを買わなくて良かった。

さて、ケーブルを抜き差しして気付いたのが、外部ディスプレイの解像度やLCDをミラーリングするかどうかの設定はずっと残るのに、外部モニター使用時のウィンドウの配置は残らないことだ。何かウィンドウを外部モニターに移動し、ケーブルを抜くと外部モニターにあった全ウィンドウがLCDに移動するが、もう一度ケーブルを挿しても全てLCDに残ったままなのである。これは、特にHDMIの場合、テレビの電源を切ると、ケーブルが接続されたままでも、外部ディスプレイと切断されたことになってしまうので、その状態を保つようにPCをスリープして復帰させるには、

  1. スリープ
  2. TV OFF
  3. TV ON
  4. 復帰
という手順を守らなくてはならず、これを間違えると、せっかく外部モニターに移動した全ウィンドウがLCDに戻ってしまう。これでは外部ディスプレイを使いにくい、というか使えない。

それを解決するアプリケーションとして、Forget-Me-NotStayを見つけた。"Stay"の方が高機能で、外部ディスプレイの着脱に関係なく、ウィンドウの位置を保存することができるツールであるが、明示的にウィンドウ位置の記憶を指示する必要がある(ケーブル差しで自動的に復帰するがケーブル抜きで自動的に保存されない)のと、有料ソフトなのがいまいちである。Forget-Me-Notは、作者のページの見た目が誤解を招くが、フリーソフトであるし、ケーブルを抜いた時にウィンドウ位置が記憶されるので、Forget-Me-Notを導入した。

さてさて、LCD側にJava API Referenceを表示して、EclipseのウィンドウをドラッグしてTV側に移動して、何かJavaアプレットのコードを開いて、これぞ省スペースで理想的な開発環境、と満足してデバッグを開始すると、アプレットのウィンドウがLCD側に表示されてしまうのが非常に面倒であることがわかった。その時大体マウスカーソルがTV側にあるので、アプレット上のボタンを触るために、マウスカーソルを長距離移動しないといけないのである。
それを解決するために、色々調べた。こういう時は、
(a) アプリケーション毎に出力先ディスプレイを設定する
(b) 外部モニターを主にする
(c) キーボード操作でウィンドウを別モニターに移動する
といった方法が常道である。他には、(d)アクティブウィンドウが切り替わった時にマウスポインターがウィンドウ上に無ければ自動的にアクティブウィンドウに移動させるという解も考えられるが、自分が意図しないタイミングでダイアログとかが開いて、ポインターが勝手に移動するのは不便であろう。それに、フォーカスロストした時にワンクリックでフォーカスを復帰させることができなくなるのは不便に違いない。

(a)については、前のMacに最初から入っていたSpaces(仮想デスクトップ)なら、アプリケーション毎にどのデスクトップで開くかを設定できたのだが、新しいMacにはSpacesが無く、代わりにMission Controlというのはあったが、同じような設定をする方法がわからなかった。

(b)は、割と意外な方法で実現できる。「ディスプレイ環境設定」の「調整」ペインを開くと、外部ディスプレイとLCDとの位置関係と、メニューバーをどちらに置くかを決められるが、このメニューバーがある方がメインディスプレイになり、メニューバーを外部ディスプレイに置くと、新たに開くウィンドウは全て外部ディスプレイで開くようになるのである。上記の問題はこれであっさり解決した。

(c)は、ジェスチャー入力を拡張するフリーの超人気ソフト、BetterTouchToolで実現できるので、他の方法を探すまでもない。筆者は元々ジェスチャー入力でなくキー操作でのウィンドウのモニター間移動に病みつきなので、上記の問題とは別に、

Command+Opt+C
Move Window to Next Monitor(隣のモニターへ)
Command+Opt+E
Maximize Window to Next Monitor(隣のモニターで最大化)
という設定にした。

(d)については、意外と方法が見つからず、LazyMouseという、ダイアログが開く時にマウスカーソルを自動的にデフォルトのボタンに移動させる有料ソフトがあったのと、java.awt.Robotクラスを使ってマウスカーソルを移動させるJavaアプリを自分用に作って常駐させている人が世の中に存在するらしいのをどこかで目にしたくらいだった。

See more ...

Posted at 23:34 in PC一般 | WriteBacks (2)
WriteBacks

はじめまして Emacsのフォント設定の情報を参考にさせていただきました。 有用な情報提供ありがとうございます。 ささやかなお返しですが、テレビ接続時に  "出力画面サイズを変えたりTVの設定を変えたりしてもどうしても文字がぼやける" 件について、 Macに外付けディスプレイを接続すると、それが液晶であってもブラウン管用のアンチエイリアスが 適用されるというバグがあり、MacFanという雑誌のサイトに対策が出ています。 http://macfan.jp/guide/2009/09/02/snow_leopard.html OSX 10.5 Leopardまではシステム環境設定に設定メニューがあった(上記記事参照)のですが OSX 10.6 SnowLeopard以後はなくなってしまい、terminalからコマンドを使うか xcodeのplist editor(あるいは直接エディタで編集)を使わないと設定ができなくなっています。 ややこしいことに、Mac本体ディスプレイと外付けディスプレイの2画面状態では 液晶用のアンチエイリアスになり、Mac本体のディスプレイを閉じて、 外付けディスプレイ単独にするとブラウン管用アンチエイリアスになるという症状となってます。 私はバグだと思っていますが、Appleはありえない使い方と考えているのかもしれません。

Posted by ふるき328 at 05/21/2012 11:51:16 AM

専門的な情報ありがとうございます。「滑らかな文字のスタイル」の設定の意味を理解できていないこともあり、今ちょっと試しただけでは違いがわかりませんでしたが、次にもう一度本気で調整する時にヒントになりそうです。

Posted by ynomura at 05/21/2012 11:04:04 PM

Apr 07, 2012

Dia 0.97.2の半円状の矢印が消える問題の修正パッチ

Dia 0.97.2-1には、ジグザグ線の先端の矢印の形状が開いた半円の場合、先端のY座標によっては表示されなくなってしまう不具合がある。例えば、UMLシートのReceptacle(要求I/F、Required I/F)の先端を上向きか下向きにして、Y座標が10〜50の間に先端を持って行くと、先端が消えてしまう。これは表示上だけの問題ではなく、その状態で図を画像ファイルとしてエクスポートすると、Diaがクラッシュしてしまうこともある。

筆者はMacPortsのDia 0.97.2-1とFreeBSDの0.97.1とでこの現象を確認した。

それを修正するパッチを作ったので、公開する。
patch-arrow.diff(MacPortsでのみテスト済み)

MacPortsでこれを使うには、
/opt/local/var/macports/sources/rsync.macports.org/release/ports/gnome/dia/Portfile

patchfiles patch-arrow.diff

という行を足して、Portfileがあるディレクトリにfilesというサブディレクトリを作って、そこにこのパッチを置く。

See more ...

Posted at 21:34 in PC一般 | WriteBacks (0)
WriteBacks

Apr 01, 2012

MacPortsでPython対応Diaをインストールするには

先週、今更ながらPythonでDiaのプラグインが作れることを知ったので、早速試してみようと思ったら、MacPortsでインストールしたDia-0.97.2ではPythonが使えないことがわかった。
ソースコードからDiaをmakeする時に、configureスクリプトに--with-pythonを渡す必要があるのだが、MacPortsのDiaのPortfileがそうなっていないのである。そのようにするMacPortsのパッチは既に作られているが、これはかなり昔のMacPortsに対するパッチで、現在ではそのまま当てられないのはもちろん、それに従って手でパッチを当てても、configureがエラーになるだけだった。

エラーログを見ると、

:info:configure checking for libpython2.7.a... not found config
:info:configure configure: error: could not find files required to build python plugin

というように、どうもインストールされたpy27-gtkにうまく対応できないようだ。

試行錯誤の末、かなり強引だが、以下のようにすると成功した。
(このブログが提供するPortfile-dia.diffpatch-configure.diffが~/Downloadsにあるとする)
1. libpython2.7.soの作成
(MacPortsのpython27はインストール済だとする。)

cd /opt/local/lib
sudo ln -s libpython2.7.dylib libpython2.7.so

2. Portfileの修正
cd /
sudo patch -p0 < ~/Downloads/Portfile-dia.diff

3. configureのパッチの用意
cd /opt/local/var/macports/sources/rsync.macports.org/release/ports/gnome/dia/
sudo mkdir files
sudo cp ~/Downloads/patch-configure.diff files/

4. port install dia
sudo port install dia

See more ...

Posted at 22:58 in PC一般 | WriteBacks (0)
WriteBacks

Mar 25, 2012

Mac間のデータ移行

物書き用に使っていた白いMacBook MB402J/Aは、使い心地も外見もとても気に入っていたのだが、半年くらい前から、トラックパッドのボタンが固くなって、クリックする度に、引っ掛かった金属片が弾かれたようなガチッガチッという音がするようになり、さらに、たまに固くて押せない(押す位置を変えると押せる、押せないのはどの位置でも起こる)こともあり、ストレスを感じるようになってしまった。
MacBookのバッテリーパックが膨らんで、真上にあるトラックパッドを押し上げて異常が出るというのはよくあることらしく(参考リンク [1] [2] [3] [4])、筆者のMacBookもそれが原因のようで、バッテリーを外して使うと問題が起こらなかったのだが、MacBookは安全設計で電源プラグが軽く外れるようになっているので、バッテリー無しで使うのは危険すぎる。かといって、トラックパッドでクリックしにくい問題だけのために1万円以上かけてバッテリーを交換する気にはなれない。デスクトップ用の外付けのトラックパッドであるMagicTrackpadを購入するという方法もあるが、6,800円するし、もう少しバッテリーが膨らむと全くクリックできなくなるらしいので、そうなったらMagicTrackpadを購入したことを後悔するだろう。マウスを買うのも同じリスクがあるし、筆者は元々マウスというデバイスが苦手なのである。

半年悩んだ末、悩む時間が勿体なく思うようになったので、携帯性を高めるため、自分への誕生日プレゼントと自分に言い訳して、ついにMacBook Air MC969J/Aを買ってしまった。本当はMC968J/Aを買おうとしたのだが、誕生日の前日に慌てて買いに行った日本橋のどこにも在庫が無かった。

新しいMacの電源を入れたら、セットアップウィザードの数画面先で、別のMacからのデータ移行のメニューが出てきた。ネットワーク経由で別のMacから直接コピーするか、外付けHDDのTimeMachineバックアップから取り込むかを選択できたが、ネットワーク経由でコピーし始めると「残り8時間」とか出てきたので、中断してTimeMachineバックアップから取り込んだ。30分くらいで終わった。すると、アプリケーションからDashboardやDockメニューの配置からFinderのショートカットから「最近使ったフォルダ」に至るまで、前のMacの環境が完璧に再現された。起動したら、多用しているスティッキーズのメモが前のMacと全く同じ位置に現れて、感動した。暫くEmacsやらEclipseやらを触っていたが、キーボードとトラックパッドの違い以外、全く違和感を感じなかった。

しかし、1つだけ問題が起こった。iTunesを起動すると、データも設定も全く引き継がれていなかった。DRM(著作権管理)か何かの都合で、パソコン間で自動的にコピーされるのは問題があるのだろうか、と思ったが、ネットで調べてもそんな情報は見つからない。むしろ、どこを見ても、iTunesライブラリもiPodの設定も全て自動的に引き継がれた、「移行アシスタント」完璧すぎる、と書いてある。最初のネットワーク経由の移行アシスタントを電源断で中断したのが悪かったのだろうか、と、Command+Rを押しながら電源ONでMacOSXユーティリティを起動してTimeMachineバックアップからのリストアをやり直したり、Lionの再インストールをやり直したりしたが、同じだった。

5回目の移行アシスタントにはネットワーク経由でユーザーアカウントを移行させたら、iTunesのデータが完璧に引き継がれた。とりあえず全て移行できたので、そういうもんか、と思って、それ以上追究するのを止めたが、数日後、同じ外付けHDDを新しいMacのTimeMachineバックアップにしようとして、原因がわかった。外付けHDDの容量が少ないので、前のMacでも、~/Music/iTunesをTimeMachineバックアップの対象外にしていたのだった。

See more ...

Posted at 21:34 in PC一般 | WriteBacks (0)
WriteBacks

Mar 21, 2012

αβ探索だけのオセロの思考ルーチン作成

前回作りかけだったオセロの思考ルーチンの核として、とりあえず、最も基本的なゲーム木の探索アルゴリズムであるαβ探索を実装してみた。
正確に書くと、前回もαβ探索をしていたつもりだったのだが、バグが取れず、うまく動かなかった。

今日、このページのコードのような形に書き直したら、一発できちんと動いた。
すると、それだけで、特にオセロが得意でない筆者は全くコンピューターに勝てなくなってしまった。

Swingオセロの起動ページ

ソースコード
OthelloApplet.java
State.java
GameTreeNode.java
Evaluator.java

単純なゲーム木の探索ではまともな結果は得られないと思っていたので、局面の良し悪しを決める評価関数をうまく作る取り組みが次に必要だと覚悟していたのだが、今回の目的は強いプログラムを作ることではなくきちんと動く思考ルーチンを作ることであり、筆者にはこれ以上コンピューターの手の良し悪しが分からないので、これで良しとする。

一応、この後評価関数の改良として取り組む予定だった内容をメモしておく。今は隅が取れてたら点数が高いだけの評価関数である。


  • 辺の評価
    "□□●●●●□□"や"□●●●●●●□"は良くて"□●●●●●□□"や"□□□○□□●□"は良くない(最後のは黒の左側に白を置かれると必ず隅を取られる)とかいうやつである。
    辺の攻防で優勢になることを狙う思考ルーチンにしたかったので、辺の状態について細かく評価値を設定したかったが、計算式で書くと結構複雑なので、今回は見送った。
    辺の状態は3^8=6561種類しかないので、事前にテーブル化しておくことが可能であり、計算するのに時間がかからないのである。
  • 序盤と終盤とで思考ルーチンを分ける
    終盤は勝ち負けを読み切ればいいだけなので、細かい評価は要らないのである。
  • 着手可能な選択肢が少なければ評価を下げる
    強い人は相手にパスさせようとすると聞いたような気がする。
  • 「偶数理論」
    よく調べてないが、最後の石を打てると有利という発想によるもので、自分の手番で残りマス数が偶数だったら評価を下げるとか、連続する空きスペースが偶数だったら評価を下げるとかいうことではないかと推測している。
  • 「開放度理論」
    周囲に空きスペースが多い石は、ひっくり返される確率が高いので、評価を下げるという考え方だったと思う。
  • 乱数を加える
    どうせそんなに強くならないだろうから、たまたまいい手を打つ確率が0でないように、おまじないを掛けようと思った。
  • コンピューター同士の対戦
    評価関数の良し悪しを決める方法は、2つの評価関数を対戦させることしか思い付かない。
    乱数の要素も加えて、いくつかの変化可能なパラメーターを用意して、何通りかのパラメーターのセットを用意して、総当たりで何回か(先後入れ替え含め)対戦させて、弱かったセットを捨てて、強かったセットに乱数を少し加えたものを新たな候補として用意し、また総当たりさせる、というのを繰り返していいパラメーターを探す、GA的な方法を考えた。
  • 展開しながらの探索中に全てのノードを残さない
    あまり探索木を広げるとメモリ不足で落ちるので、今はノード数がある程度に達したら計算を打ち切っているが、全体的に評価の悪い部分木は取り払うとか、途中のノードは評価値だけ残して盤面は消すとかして、メモリを節約する方が良い。
  • 未評価の盤面の扱い
    今は評価値の初期値を0にしてるので、評価した局面の評価値が全て負の値だと、反復深化で読み直す時に未評価の手を優先してしまう、怪しい動作になっている。

See more ...

Posted at 22:14 in Java | WriteBacks (0)
WriteBacks

Feb 19, 2012

Swingでオセロを作成中

そういえば、これまでの人生において、思考ルーチンというやつを完成させたことがないことに気付いたので、一応、簡単に履修しておくことにした。

…ら、GUIを作るのだけで結構手間取ってしまった。マウス入力とアニメーションと思考ルーチンを並列動作させようとしたのが、思ったより大変だった。

肝心の思考ルーチンはほとんど空だが、とりあえず最後までゲームができるようになったので、一旦ここに置く。

Swingオセロの起動ページ

ソースコード
OthelloApplet.java
・残りは作成中

See more ...

Posted at 23:54 in Java | WriteBacks (0)
WriteBacks

Jan 10, 2012

統計学復習メモ19: 分散分析の種類

分散分析というと、その名前自体によく「一元配置」「二元配置」とか「対応あり」「対応なし」とか「繰り返しのある」とか「繰り返しのない」とかいう言葉がついて回る。統計学の書籍でも、「一元配置分散分析」と「二元配置分散分析」は項目を分けて説明されることが多い。さらにそれぞれが「対応あり/なし」「繰り返しあり/なし」等で分けられると、目次に「分散分析」がたくさん並ぶことになる。Excelの「分析ツール」のメニューにも分散分析の項目があるが、

  • 分散分析: 一元配置
  • 分散分析: 繰り返しのある二元配置
  • 分散分析: 繰り返しのない二元配置
などと分かれており、試しにやってみようと思っても、それらの違いがわからないとどうしようもない。同じ分散分析でも、それらの選択によっては、結果が的外れな意味を為さないものになる可能性があるのだ。その「分散分析」に付加される言葉の種類の多さに圧倒されて、分散分析は覚えることが多いと思って勉強する気を失ったのは、筆者だけであろうか。

筆者は未だに、どういう時にどの種類の分散分析を使うべきなのかよくわかっていない。 頭の中を整理するために、いつものように体当たり的に、

  • 一元配置(対応なし)
  • 一元配置(対応あり)
  • 二元配置(繰り返しなし)
  • 二元配置(繰り返しあり、対応なし)
  • 二元配置(繰り返しあり、1要因対応あり)
  • 二元配置(繰り返しあり、2要因対応あり)
のそれぞれの適用例を、筆者の職業に関係のあるソフトウェア開発を題材にして、考えてみることにした。

以下のデータは、全て架空のものである。
筆者はExcelを持っていないので、分散分析の計算にはRを使う。Rにも分散分析の関数はoneway.test(), aov(), anova(), lme()など色々あるが、今回は上記の全てをカバーできるaov()を使う。


最もシンプルな、一元配置(対応なし)の例として、次のようなデータを考える。

一元配置(対応なし)の例(1)
要因:モジュールの階層
アプリミドルドライバ
生産性
(steps/人月)
490
410
590
500
460
690
750
500
770
720
730
480
650
310
450
530
550
350
460
370
610
240
500
この生産性はモジュールの階層によって差があると言えるかどうか(差が無いなら滅多にこうはならないこと)を、分散分析で調べる。
> sample1 <- read.table("anova01.dat", header=TRUE)
> summary(aov(productivity ~ module, data=sample1))
            Df Sum Sq Mean Sq F value  Pr(>F)  
module       2 120939   60470  3.5656 0.04738 *
Residuals   20 339182   16959                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
> 
"Df"が自由度、"module"の行の"Sum Sq"が群間平方和(ここではモジュールの階層の違いによる効果)、"Mean Sq"が群間の不偏分散(モジュールの階層の違いによるばらつきの分散値)、Residuals(残差=群内のばらつき=全体に共通するはずのばらつき)の行のMean Sqが群内(全群共通)の不偏分散、"F value"が(群間/群内)の分散比、"Pr"(P値、そのF値以上が起こる確率)の右の'*'が有意水準0.05で有意であることを表す。
分散分析の帰無仮説は「全ての標本は同じ分布に従う母集団から得られたもの」→「各群の分布が同じであること」→「群間にばらつきがないこと」なので、分散比が有意であれば、「全体のばらつきに対して群間にばらつきがないとは言えない」、細かいことを言わなければ、群間にばらつきが認められ、各群の分布は同じでないことになる。
この例では、生産性はモジュールの階層によって違いがありそうということになる。

次の例は、同じ観測値を別の要因(観点)で分けたものである。

一元配置(対応なし)の例(2)
要因:所属
A社B社C社
生産性
(steps/人月)
490
410
460
590
500
370
460
690
750
480
500
650
770
610
310
720
450
530
240
550
350
730
500
> summary(aov(productivity ~ company, data=sample1))
            Df Sum Sq Mean Sq F value Pr(>F)
company      2  68444   34222  1.7475 0.1998
Residuals   20 391677   19584               
>
測定値が同じであることを明確にする為に、データファイルを同じsample1にしている。'*'のマークとその説明が出力されていないので、所属別では群間に有意な差が無く、このデータでは所属によって生産性にばらつきがあるとは言えないことになる。

もう1つ例を考えた。

一元配置(対応なし)の例(3)
要因:仕様書のファイル形式
.doc.txt.xls.ppt
システムテスト
不具合数(/kstep)
3
10
8
2
2
5
4
4
6
0
11
11
7
1
3
7
5
10
4
10
11
12
5
10
14
11
> sample2 <- read.table("anova02.dat", header=TRUE)
> summary(aov(error ~ format, data=sample2))
            Df Sum Sq Mean Sq F value  Pr(>F)  
format       3 113.58  37.861  3.1192 0.04672 *
Residuals   22 267.03  12.138                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
このデータでは、システムテストで見つかったバグ数は、仕様書のファイル形式によって違いがありそうということになる。

測定データを一元配置分散分析(対応なし)する時のポイントを整理する。

  • 測定値は正規分布することを仮定できるものであること
  • 差があることを調べる群は、3群以上あること (2群しか無いなら、その差をt検定するのと変わらない)
    つまり、要因(群の分け方)はその水準(標本が属する群を決めるもの、値やID)が3つ以上あるように選ぶこと
  • 各水準のデータ数は同じでなくても良い
  • 水準はなるべく定量的な数値でないこと、数値であれば、なるべく測定値の従属変数でないことが明確であること
    (要因と測定値に明確な関係があっても、群間のばらつきが十分に大きくないと有意差が検出できないため。また、水準(値の範囲)の取り方によって結果が変わってしまうため。例えば測定値と線形の関係にあるなら、無相関の検定をする方が良い)
最後の1点は、この記事を書くにあたって色々な例を考えてみた上での筆者の感想であり、そういうことを何かで読んだ記憶も無く、必ずしもそうではないかも知れない(温度の範囲などを水準に取る例も見かける)。


一元配置(対応あり)の例として、次のようなデータを考える。
各開発者がその4種類の開発プロセス(作業手順)を経験した時の、それぞれの開発プロセスにおける成績が、次のように得られたとする。

一元配置(対応あり)の例(1)
生産性
(steps/人月)
要因:開発プロセス
WaterfallSpiralIncrementalTDD




開発者A490750450610
開発者B410480530780
開発者C460500240680
開発者D590650550880
開発者E500770350520
開発者F370610730600
開発者G460310500400
開発者H6907206401030
Waterfallは要求分析、設計、実装、テストをこの順で1回ずつ行うやり方、Spiralは設計以降の作業を何回かに分けて設計、実装、テストを繰り返して積み上げ式に開発する方式、Incrementalは要求分析も含めて全体を繰り返す、1サイクル毎に一通り動くものができる機能追加方式、TDDは要求分析や設計をテストプログラム作成に代え、以降の作業はそのプログラムがOKを返すことだけを目的に行う、結果良ければ全て良し方式のことである。
開発者の能力や向き不向きには個人差があることを前提とし、それを計算に入れて、開発プロセスは生産性に影響するかどうかを、分散分析で調べる。
> sample3 <- read.table("anova03.dat", header=TRUE)
> summary(aov(productivity ~ process + Error(developer), data=sample3))

Error: developer
          Df Sum Sq Mean Sq F value Pr(>F)
Residuals  7 337872   48267               

Error: Within
          Df Sum Sq Mean Sq F value  Pr(>F)  
process    3 201184   67061  3.8348 0.02464 *
Residuals 21 367241   17488                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
"Error: developer"の部分は開発者の個人差による(開発者の違いを誤差要因とする)ばらつきに関するものであり、以降の計算はそれが除外されていることを示す。"Error: Within"の部分が、群内変動を誤差と見なして群間変動を検定するものである。
このデータでは、個人差の影響を差し引くと、開発プロセスは生産性に影響する要因と言えることになる。
もし、同じデータを「対応なし」として計算する(開発者の個人差を差し引かない)と、次のように、開発プロセス間に同じ有意差が出ない。
> summary(aov(productivity ~ process, data=sample3))
            Df Sum Sq Mean Sq F value  Pr(>F)  
process      3 201184   67061   2.663 0.06729 .
Residuals   28 705112   25183                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
実際には各開発プロセスを短期間に経験することは難しく、開発者の経年変化や学習効果もあるので、こういうデータは取りにくいが、例えば開発者がソフトハウスのことであり、ソフトハウス内ではばらつきが無いと仮定すれば、各プロセスを同時に行うことも可能であるし、何より、筆者がこれ以上単純明快な例を思い付かないので、これで良しとする。

次の例は、会社による差(観測対象の個体差)はあるものとして、業務分野の違いが生産性に影響すると言えるかどうかを調べる。

一元配置(対応あり)の例(2)
生産性
(steps/人月)
要因:開発分野
組み込み汎用業務システムゲーム




M社330470450320
N社290440370630
O社450550580750
P社320360470460
Q社370320430480
> sample4 <- read.table("anova04.dat", header=TRUE)
> summary(aov(performance ~ product + Error(company), data=sample4))

Error: company
          Df Sum Sq Mean Sq F value Pr(>F)
Residuals  4 102420   25605               

Error: Within
          Df Sum Sq Mean Sq F value  Pr(>F)  
product    3  80080 26693.3  4.0332 0.03379 *
Residuals 12  79420  6618.3                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
summary(aov(performance ~ product, data=sample4))
            Df Sum Sq Mean Sq F value Pr(>F)
product      3  80080   26693  2.3487 0.1111
Residuals   16 181840   11365               
1つ目のaov()が会社間に「対応あり」として計算した例、2つ目のaov()が「対応なし」として計算した例である。このデータだと、会社間の個体差を考慮に入れると、開発分野は生産性に影響する要因だったと言えることになる。

測定データを一元配置(対応あり)にする時のポイントを整理する。

  • 群内の各測定値が、どの個体から得られた標本であるか(または、誤差要因のどの水準に属する標本であるか)がわかること
  • 個体差によるばらつきはあるものとし、差し引いて考えるものであること
  • 個体に有意差があるかどうかを調べる必要は無いこと
  • 一部のデータが抜けていても計算可能
  • 同水準、同個体のデータは1つでなく複数あっても計算可能(但し全てのセルのデータ数が同じであることが望ましい)
つまり、「対応あり」とは、異なる水準間(上の表の列間)でどれとどれが同じ個体から採取された(一般化すると、同じ誤差要因を持つ)標本であるかの対応が取れることである。


二元配置(繰り返しなし)の例として、次のようなデータを考える。

二元配置(繰り返しなし)の例(1)
生産性
(steps/人月)
要因1:開発プロセス
WaterfallSpiralIncrementalTDD
要因2:
座席の形態
大部屋490700450560
自由席410430530730
パーティション460450240630
小部屋590720550830
自宅500600350470
開発プロセスの違いも作業空間(座席の形態)の違いも生産性に影響し、それらの影響が足し合わせれていると仮定して、それぞれの組み合わせについて1つずつデータを採取し、2要因について同時に、その仮定が正しそうかどうかを調べる。
> sample5 <- read.table("anova05.dat", header=TRUE)
> summary(aov(productivity ~ process + workspace, data=sample5))
            Df Sum Sq Mean Sq F value  Pr(>F)  
process      3 141255   47085  4.8533 0.01951 *
workspace    4 121420   30355  3.1288 0.05589 .
Residuals   12 116420    9702                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
よって、このデータでは、開発プロセスの違いによる効果は有意水準0.05で有意差あり、作業空間の違いによる効果は有意差なし(有意水準が0.1だと有意差あり)となる。
ちなみに、開発プロセスと作業空間についてそれぞれ、同じデータを一元配置とみなして計算すると、次のように、同じ水準の有意差は出ない。
> summary(aov(productivity ~ process, data=sample5))
            Df Sum Sq Mean Sq F value  Pr(>F)  
process      3 141255   47085  3.1675 0.05318 .
Residuals   16 237840   14865                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
> summary(aov(productivity ~ workspace, data=sample5))
            Df Sum Sq Mean Sq F value Pr(>F)
workspace    4 121420   30355  1.7671  0.188
Residuals   15 257675   17178               
このデータの特徴は、全体のばらつきを、2つの要因のばらつきと共通のばらつきの3つに分解するからこそ、明確になるのである。

次の例は、何らかの開発成果物の欠陥数を、作業期間と納入した曜日との組み合わせのそれぞれについて1つずつ、データを採取したとするものである。

二元配置(繰り返しなし)の例(2)
欠陥数
/kstep
要因1:開発期間
1週間2週間1ヶ月
要因2:
曜日
月曜1064
火曜696
水曜864
木曜494
金曜864
土曜1099
日曜N/A69
> sample6 <- read.table("anova06.dat", header=TRUE)
> summary(aov(error ~ time_limit + delivery, data=sample6))
            Df Sum Sq Mean Sq F value  Pr(>F)  
time_limit   2 14.360  7.1798  2.8898 0.09803 .
delivery     6 48.861  8.1435  3.2777 0.04224 *
Residuals   11 27.329  2.4845                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
1 observation deleted due to missingness
> summary(aov(error ~ delivery, data=sample6))
            Df Sum Sq Mean Sq F value  Pr(>F)  
delivery     6 51.217  8.5361  2.8213 0.05523 .
Residuals   13 39.333  3.0256                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
1 observation deleted due to missingness
よって、このデータでは、開発期間の違いによる差も考慮すると、納品日の曜日によって有意水準0.05で有意な差があるという結果になる。
2つ目のaov()は、上の例と同様、一元配置では同じ有意差が出ないことを確認したものである。

二元配置(繰り返しなし)にする時のポイントを整理する。

  • 2つの要因の全ての水準の組み合わせについて、データは1つ
  • どちらの要因も何らか影響していると思われ、それぞれの影響を分離して調べようとしていること
  • 一部のデータが抜けていても計算可能
  • 一元配置(対応あり、繰り返しなし)の代用として行うことも可能
表の形が同じことからもわかるように、一元配置(対応あり)と二元配置は計算方法が同じであり、一元配置の対応を決める要因についてのF検定を省くかどうかが異なるだけである。
Excelの分析ツールに「対応のある一元配置」が無いのは、二元配置で代用できるからだろう。


二元配置(繰り返しあり、対応なし)は、表の形としては、繰り返しのない二元配置の各セルに複数のデータが含まれるだけの違いなので、同じような要因のペアが使える。

二元配置(繰り返しあり、対応なし)の例(1)
生産性
(steps/人月)
要因1:開発プロセス
WaterfallSpiralIncrementalTDD
要因2:
座席の形態
大部屋 570
660
450
710
480
570
570
450
620
520
540
740
自由席 570
500
270
590
460
670
510
450
410
620
350
350
パーティション 300
480
580
670
500
710
610
540
540
430
650
600
小部屋 510
530
530
660
620
510
310
550
780
760
400
590
自宅 510
560
530
570
750
590
400
380
520
600
610
580
繰り返しがある(同じセルに複数のデータがある)と、行の効果、列の効果に加えて、交互作用の効果を分離して計算することが可能で、交互作用を分けるか分けないかで計算結果が変わる。
交互作用とは、2要因の水準の特定の組み合わせにだけに影響する効果のことで、例えばその行とその列は大体高い値なのにそのセルだけやたら低い値が多いということを起こす要因である。
aov()で交互作用を分離して分散分析するには、引数において2つの要因を'*'で繋ぐ。
> sample7 <- read.table("anova07.dat", header=TRUE)
> summary(aov(productivity ~ process * workspace, data=sample7));
                  Df Sum Sq Mean Sq F value  Pr(>F)  
process            3  98952   32984  2.4676 0.07599 .
workspace          4  65823   16456  1.2311 0.31308  
process:workspace 12  69657    5805  0.4343 0.93963  
Residuals         40 534667   13367                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
"process:workspace"の行が交互作用に関する行で、このデータでは有意差は出なかった。また、交互作用を分離すると、開発プロセスにも作業空間にも有意水準0.05の有意差はなしである。
交互作用を分離せずに計算するには、aov()の引数において、2つの要因を'+'で繋ぐ。
> summary(aov(productivity ~ process + workspace, data=sample7));
            Df Sum Sq Mean Sq F value  Pr(>F)  
process      3  98952   32984  2.8382 0.04686 *
workspace    4  65823   16456  1.4160 0.24170  
Residuals   52 604323   11622                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
このデータでは、交互作用を分離しなければ、開発プロセスには有意差が認められる(交互作用の効果を分離しない方が有意差が出る)ことがわかる。
ちなみに、繰り返しがないと、'+'でも'*'でも結果は変わらない。

もう1つ例を作る。

二元配置(繰り返しあり、対応なし)の例(2)
結合テストエラー数
/kstep
要因1:仕様書の配布形態
WordExcelPDFHTML
要因2:
設計書の
フォーマット
Word 8
6
2
1
7
5
7
3
9
10
4
11
5
6
3
11
7
14
7
9
Excel 11
5
8
13
8
4
10
6
3
6
5
9
13
6
10
17
12
11
5
10
Text 5
6
6
10
11
6
12
7
9
8
5
12
8
9
6
9
8
13
11
3
HTML 12
14
7
13
12
10
15
6
13
13
12
7
5
13
7
4
10
3
8
9
> sample8 <- read.table("anova08.dat", header=TRUE)
> summary(aov(error ~ given_spec * design_desc, data=sample8))
                       Df Sum Sq Mean Sq F value  Pr(>F)  
given_spec              3   86.5 28.8333  2.9254 0.04043 *
design_desc             3   17.1  5.7000  0.5783 0.63137  
given_spec:design_desc  9  198.4 22.0444  2.2366 0.03056 *
Residuals              64  630.8  9.8562                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
> summary(aov(error ~ given_spec + design_desc, data=sample8))
            Df Sum Sq Mean Sq F value  Pr(>F)  
given_spec   3   86.5  28.833  2.5384 0.06314 .
design_desc  3   17.1   5.700  0.5018 0.68221  
Residuals   73  829.2  11.359                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
1つ目のaov()の結果より、このデータでは仕様書のフォーマットが欠陥数に影響しており、また設計書のフォーマットとの交互作用もある結果となった。 2つ目のaov()の結果は、交互作用を分離しないと、仕様書のフォーマットについても同じ有意差は出ない(1つ前のデータ例とは逆に、交互作用を分離する方が有意差が出る)ことを示している。

二元配置(繰り返しあり、対応なし)にする時のポイントを整理する。

  • 2つの要因の全ての水準の組み合わせについて、複数のデータがある
  • それにより、2つの要因の交互作用が混入する
  • 交互作用によるばらつきを分離して計算するかどうかは場合による
  • 一元配置(対応あり、繰り返しあり)の代用として行うことも可能
一元配置(対応あり)を代用する時に交互作用をどう扱うかが問題になるが、誤差要因と何かの交互作用というのはやはり誤差要因だと考えるなら、交互作用を分離して、誤差要因も交互作用も検定から除外する(aov()なら'*'を使った上で検定中の要因そのもの以外の行を無視する)のが好ましいと思う。


「二元配置(1要因対応あり)」は、「対応のある要因と対応のない要因の二元配置」と書かれることが多いようだが、そう書かれると余計にややこしく見えるのは、筆者だけだろうか。
「対応あり」とは、一元配置の場合と同様、繰り返しのある(セル内に複数ある)データのそれぞれが、他の水準(セル)のどのデータに対応するかがわかる、という意味であり、対応を取るからには、個体差の影響を取り除いて計算する必要があると考えていることになる。
1要因についてのみ対応が取れる状況として、次のような例を考える。

二元配置(繰り返しあり、1要因対応あり)の例(1)
生産性
(steps/人月)
要因1:開発プロセス
WaterfallSpiralIncrementalTDD
要因2:
座席の形態
+ 人
作業場開発者
大部屋 A630660570340
B530430430400
C390640520530
パーティション D400760500680
E550670290890
F650620280820
小部屋 G470680760410
H530580670930
I850980790980
自宅 J610850530310
K400640230400
L530430470330
開発者間には能力の個人差があるのは普通だから、開発者の違いは、標本の対応を取ってその影響を除外して計算するにふさわしい、誤差要因であろう。
前記のように同じ人が4つの開発プロセスを経験するというのは現実的に多少無理があるが、それはなされたとする。
全ての開発者がこれらの全ての作業空間を経験するのは現実的に不可能であろう。と思うので、そういう席替えはなされず、作業場毎に別の人を選んでデータを採取したとする。
つまり、開発プロセスについては、水準間でどれとどれが同じ人のデータであるかの対応があり、作業空間に関しては、水準間でそういう対応がない、というデータである。
> sample9 <- read.table("anova09.dat", header=TRUE)
> summary(aov(productivity ~ process * workspace + Error(developer), data=sample9))

Error: developer
          Df Sum Sq Mean Sq F value  Pr(>F)  
workspace  3 424492  141497  3.8251 0.05734 .
Residuals  8 295933   36992                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 

Error: Within
                  Df Sum Sq Mean Sq F value  Pr(>F)  
process            3 163692   54564  3.0262 0.04914 *
process:workspace  9 391275   43475  2.4112 0.04123 *
Residuals         24 432733   18031                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
このように、開発プロセスの違いによる影響には有意差があり、また開発プロセスと作業空間の間には何らかの交互作用があるという結果になった。
もし同じデータを対応なしの二元配置として計算すると、次のように全く異なる結果になる。
> summary(aov(productivity ~ process * workspace, data=sample9))
                  Df Sum Sq Mean Sq F value   Pr(>F)   
process            3 163692   54564  2.3962 0.086440 . 
workspace          3 424492  141497  6.2140 0.001898 **
process:workspace  9 391275   43475  1.9092 0.086483 . 
Residuals         32 728667   22771                    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
これは、データをよく見るとわかるのだが、小部屋に飛び抜けて成績が良い人が居ることに起因しており、作業空間の違いによる効果だと言うにはかなり不適切であろう。

1要因にのみ対応がある例としてもう1つ、次のようなデータを考える。
開発者A〜Fで構成されるグループが、6つの分野の業務を行った時の生産性を測ったという例である。

二元配置(繰り返しあり、1要因対応あり)の例(2)
生産性
(steps/人月)
要因1:性別 + 人
性別男性女性
開発者ABCDEF
要因2:
業務分野
組み込み(アプリ) 470720550610580550
業務システム 490530530570310380
生産システム 530520570600520490
PCアプリ 530310460420690650
Javaアプリ 600530500450790590
ゲーム機ソフト 450530400530350530
上の例では対応を決める要因を縦に並べたが、今度は横に並べた。
開発者の能力には当然個人差がある。 全開発者が6つの分野にて仕事をしたので、分野間ではどれとどれが同じ人のデータであるかの対応が取れる。
1人の開発者は男性と女性の両方を経験できないので、男性群と女性群のデータには対応が取れない。
> sample10 <- read.table("anova10.dat", header=TRUE)
> summary(aov(productivity ~ sex * area - Error(developer), data=sample10))

Error: developer
          Df Sum Sq Mean Sq F value  Pr(>F)  
sex        1 4225.0  4225.0  9.6266 0.03613 *
Residuals  4 1755.6   438.9                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 

Error: Within
          Df Sum Sq Mean Sq F value Pr(>F)
area       5  77314   15463  1.2919 0.3066
sex:area   5  51892   10378  0.8671 0.5202
Residuals 20 239378   11969               
> summary(aov(productivity ~ sex * area, data=sample10))
            Df Sum Sq Mean Sq F value Pr(>F)
sex          1   4225    4225  0.4205 0.5228
area         5  77314   15463  1.5390 0.2153
sex:area     5  51892   10378  1.0330 0.4209
Residuals   24 241133   10047               
1つ目のaov()の出力より、このデータでは、性別による影響に有意差が見られるという結果になる。
2つ目のaov()の出力は、2要因とも対応なしとして計算すると、どの要因、交互作用にも有意差が見られないことを示すものである。

二元配置(1要因のみ対応あり)にする時のポイントを整理する。

  • 標本には個体差があることを前提とする
  • 1つの要因については、複数の水準に同じ個体からの標本があり、水準間でデータの対応が取れる
    例えば、同じ被験者から、各水準の条件下でデータが取られている
  • もう1つの要因については、1つの個体からの標本は1つの水準にしかなく、水準間でデータに対応がない
    例えば、水準を決める条件は同時に発生するので、水準毎に異なる被験者を選ぶ
もちろん、対応を決める要因は、個人や個体に限らず、標本に影響があることが既にわかっている条件であれば何でも良い。


二元配置(2要因対応あり)の標本のデータ構造は、1要因のみ対応ありのものよりも単純である。

二元配置(繰り返しあり、2要因対応あり)の例(1)
欠陥数
(steps/人月)
要因1:開発プロセス
WaterfallSpiralIncrementalTDD
要因2:
座席の形態
+ フェーズ
作業空間開発フェーズ
大部屋 仕様作成6673
全体設計4787
個別設計68105
テスト設計3264
実装6588
小部屋 仕様作成2635
全体設計5746
個別設計63104
テスト設計7564
実装12567
自宅 仕様作成5494
全体設計9296
個別設計7997
テスト設計5332
実装491011
要するに3次元なのである。ここでは表を立体的に書けないので、対応を決める「開発フェーズ」については列方向に展開している。
開発フェーズが違うと仕事の性質が全く違うので、フェーズの違いは当然ミスの数に影響する。
どんな開発プロセスにも、これくらいのフェーズは存在するので、各フェーズのデータはあり得る。また、どんな作業空間でも、一通りの開発をすれば全てのフェーズを経るので、各フェーズのデータが得られる。従って、異なる開発プロセスのデータ間でも異なる作業空間のデータ間でも、フェーズの違いによる対応が取れる。
> sample11 <- read.table("anova11.dat", header=TRUE)
> summary(aov(error ~ process * workspace - Error(phase/(process*workspace)), data=sample11))

Error: phase
          Df Sum Sq Mean Sq F value Pr(>F)
Residuals  4 94.733  23.683               

Error: phase:process
          Df Sum Sq Mean Sq F value  Pr(>F)  
process    3 30.850 10.2833  3.8482 0.03852 *
Residuals 12 32.067  2.6722                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 

Error: phase:workspace
          Df  Sum Sq Mean Sq F value Pr(>F)
workspace  2  4.9333  2.4667  0.6251 0.5594
Residuals  8 31.5667  3.9458               

Error: phase:process:workspace
                  Df Sum Sq Mean Sq F value Pr(>F)
process:workspace  6  17.20  2.8667  0.5148 0.7912
Residuals         24 133.63  5.5681               
2要因対応ありのデータ構造は単純だが、その計算は、コンピューター任せでも複雑である。対応を決める要因の違いによる影響が、要因1との交互作用、要因2との交互作用、要因1と要因2と対応要因との交互作用と多岐に渡って分離して計算されるからである。
上記のaov()の構造式のError()の部分は、その構造がわかりやすいように
  • Error(phase + phase/process + phase/workspace)
  • Error(phase + phase:process + phase:workspace)
  • Error(phase + phase:process + phase:workspace + phase:process:workspace)
といった形で書かれることも多いようだが、ここではシンプルで入力ミスも避けられる
  • Error(phase / (process * workspace))
を採用している。
上の計算結果では開発プロセス間に有意差が出たが、同じデータを対応なしとして計算すると、次のようにどこにも有意差が出ない。
> summary(aov(error ~ process * workspace, data=sample11))
                  Df  Sum Sq Mean Sq F value Pr(>F)
process            3  30.850 10.2833  1.6904 0.1816
workspace          2   4.933  2.4667  0.4055 0.6689
 process:workspace  6  17.200  2.8667  0.4712 0.8262
Residuals         48 292.000  6.0833               

もう1つ例を考える。新規に参入した人がそこの設計業務をマスターするのに何ヶ月かかったかというデータが、次のように得られたとする。

二元配置(繰り返しあり、2要因対応あり)の例(2)
定着期間
(ヶ月)
要因1:設計手法
構造化設計データ指向オブジェクト指向コンポーネント指向
要因2:
開発内容
+ 性別
開発内容性別
プラットフォーム 男性 8 9 711
女性 3 6 7 7
ミドルウェア 男性 2 4 3 6
女性 9 5 9 8
フレームワーク 男性10 910 9
女性 910 511
アプリケーション 男性11 9 5 5
女性1210 1 5
男性と女性とでは元々差があるはずだという前提で、対応ありの分散分析を行う。
> sample12 <- read.table("anova12.dat", header=TRUE)
> summary(aov(takes_month ~ design_policy * target - Error(gender/(design_policy*target)), data=sample12))

Error: gender
          Df  Sum Sq Mean Sq F value Pr(>F)
Residuals  1 0.03125 0.03125               

Error: gender:design_policy
              Df  Sum Sq Mean Sq F value Pr(>F)  
design_policy  3 23.3438  7.7813  14.647 0.0269 *
Residuals      3  1.5938  0.5313                 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 

Error: gender:target
          Df Sum Sq Mean Sq F value Pr(>F)
target     3 45.844  15.281  0.8886 0.5375
Residuals  3 51.594  17.198               

Error: gender:design_policy:target
                     Df Sum Sq Mean Sq F value Pr(>F)
design_policy:target  9 95.531 10.6146  2.3142 0.1136
Residuals             9 41.281  4.5868               
> summary(aov(takes_month ~ design_policy, data=sample12))
              Df  Sum Sq Mean Sq F value Pr(>F)
design_policy  3  23.344  7.7812  0.9237 0.4422
Residuals     28 235.875  8.4241               
このデータでは、男女の違いによる対応ありとすると、設計手法の違いによる有意差が見られ、対応なしとすると、設計手法にも開発内容にもその違いによる差が見られないという結果になった。

See more ...

Posted at 23:47 in 数学 | WriteBacks (0)
WriteBacks