オブジェクト指向設計の基本をコードの具体例で学ぼう

ギルドワークスがで開催する、チーム開発力強化セミナーのご案内です。

日時:10月7日(火) 16:00−18:00
場所:東京 六本木
参加費:5000円
定員:10名
テーマ:「オブジェクト指向設計の基本を学ぶ」
講師:(私が担当します)

※8月12日に開催したものと同じ内容です

セミナーの内容


良い設計は、ソフトウェアの変更コストを下げます。
コードのちょっとした書き方の違いが、ソフトウェアの変更のやりやすさに大きく影響します。

このセミナーでは、以下のようなコードの書き方の工夫を、具体的に解説します。

・コードを読みやすくする三つの基本テクニック
・if文をわかりやすくする
・数値/文字列/日付の扱い方
・コレクションや配列の扱い方
・区分コードの扱い方

内容、マーチンファウラー「リファクタリング」に出ててくる設計改善のやリ方、その狙いと効果を、サンプルコードを使って具体的に解説します。

サンプルコードはJavaですが、どのプログラミング言語でも通用する内容のはずです。

「オブジェクト指向設計」といっても、難しい用語は使いません。
プログラムのどこにでもでてくるif文やfor文、演算式、区分コードなどを題材に、オブジェクト指向らしくコードを整理する具体的なやり方の説明です。

参考


ギルドワークスブログに書いた、こちらの内容の実演です。

リファクタリングのエッセンス

if文の条件式の書き方あれこれ

設計力の向上

今年は設計力を向上する。
そのために設計の基本をちゃんと勉強しなおそうと思う。

ソフトウェア開発を生業にしている以上、日々なんらかの設計作業はしている。
しかし、経験と勘と度胸で乗り切っているが、一流のプロの設計とは言い難い。それは自分がいちばん分かっている。

ある日突然、スーパーエンジニアに変身できるわけはないが、設計力を向上するためにプロとして最低限の努力は続けたいものだ。

今年の最初の記事なので「決意表明」みたいな力みがあって、読みやすい文章にはならないだろうけど、設計力の向上について、自分のキーワードを書きだしておこうと思う。

心技体


設計力も、心技体だと思う。
心を練り、技を磨き、体を鍛えることがたいせつ。

■体を鍛える


文字通り体力づくりですね。

納期は迫る、バグは消えない、性能は出ない、この期に及んで仕様変更の追い討ち、...

こういう状況で、プロとして設計の質を維持するには、ぜったい体力が必要。
設計とは、頭を使って考えてなんらかの決断をすること。
厳しい状況、いろいろなプレッシャーな中で、へろへろになった状態で、どこまで良い決断ができるかは、体力しだい。それが現実だと思っている。

そういう状況にならないように、プロジェクトのやり方を工夫することはもちろんたいせつ。
まずいプロジェクト運営を体力と根性で乗り切れ、と主張する気はありません。

ただ、良い設計をするためには、健康な体・基礎体力が基本になりますよね、ということ。

自分は、完全に運動不足。
設計力の「体を鍛える」ために今年は意識的に体力づくりをしようと思う。
まずはエスカレータ使わず階段使う、くらいから。

■技を磨く


現場で格闘を繰り返してきているので、経験と勘と度胸は、それなりに身についてきた。
本やネット上で、ソフトウェア設計についての情報にはそれなりに触れていると思う。

でも基本的な設計の考え方が、きちんと身についているか?とあらためて考えると、答えは、はっきりノー。
結果的にソフトウェアは動いてはいるが、それが良い設計である証明にはなっていないことは、自分でもよく分かっている。

もともと「原理原則より実践じゃあ」「設計用語を覚えただけで設計できるなら苦労はしない」「現場を知らずにえらそうなこと言うな」という超現場主義の体質が、ここにきて、やはり限界にきている気がする。

今年は、ちゃんとソフトウェア設計の原則を、基本から勉強しなおそうと思う。

デザインパターンなんかも「知っている」「使っている」というレベルではなく、その背景にある「考え方」を理解し、いつ、どう使うか/使わないかを適切に「判断する力」を身に着ける、という視点で学びなおそうと思う。

自分は、業務アプリケーションの開発が専門で、設計の流儀は、ドメイン駆動設計にこだわっている。だから、ドメイン駆動設計を実践するための基本を学び直そうと思う。

コードを書きながら体で基本を覚えるには、

◎オブジェクト指向エクササイズ 9つのルール
◎リファクタリング by マーチンファウラー
◎実装パターン by ケントベック
◎Effective Java by ジョシュアブロック

を地道に実践してみる。身体で覚える。

設計の考え方は「オブジェクトへ責任の割り当て方」を軸に本の読み直しと、実際の自分たちのコードで検証してみようと思う。

◎オブジェクトデザイン by ワーフスブラック
◎実践UML、特に GRASP by クレーグラーマン
◎アジャイルソフトウェア開発の奥義、特にクラスとパッケージの設計原則

いずれも、今まで、何回か読んだことはあるし、知識として知らないわけではない。
ただ知識や理解が断片的。一番、問題なのは、日々の設計活動と、これらの知識がほとんど関係づいていないこと。

おそらく、自分たちなりに良い設計だと思っている箇所は、これらの書籍で語られている設計原則をうまく実践できているんだと思う。

設計でぎこちなさや違和感を感じている箇所は、設計原則に反していることが具体例になっているんだと思う。

実際のコード(設計内容)と、これらの原則を突き合わせながら、設計の基本の考え方を、勉強しなおしてみようと思う。

■心を練る


設計力とは判断力だと思っている。

論理的に考えたり、いろいろな手法を適用すれば、それなりのレベルの設計はできると思う。
でも、プロとしてもっと上のレベルを目指したい。

「心の思うままに設計すると、それが最高の設計になる」

このレベルに実際に到達できるとは思わないが、そういう境地にあこがれる。

技を練り、体を鍛え、現場での実践を正しく積み重ねていくことで、こういう境地に近づけるんだと思う。

as a whole:全体力


システムの設計は、as a whole つまり全体としての質の問題なんだと思う。
しかもその質とは、一言で言い表せるようなものではないし、数値化して比較できるようなものでもない。

しかもシステムの全体は、常に変化を続けるもの。その中で「いきいきたとした全体」をどうやって生み出していくか?

この考え方は、もちろん「環境設計の手引き パタン・ランゲージ」クリストファーアレグザンダーから影響を受けている。

ソフトウェアシステムの設計について、全体としての質、成長を続ける全体を生むための設計、という視点で、もう一度、この本も読み直してみようと思う。

ソフトウェア設計の技術書、それから現場での実践は、どうしても局所での判断をしがち。また、目先のリリース日が最終ゴールであるかのように行動してしまう。

良い設計は「全体」を、そして全体が「成長を続ける」ことを、もっと意識することが大切なんだと思う。

こういう「全体力」が設計力の重要な要素。
問題は「全体力」をどうやって勉強して身につけるか。

in the context:文脈力


全体力とならんで重要なのが「文脈力」。
ソフトウェアの設計判断は「文脈」(取り巻く環境/関係者の都合や事情/前後関係)に依存する。

書籍で語られる「原理・原則」や「パターン」は、個々の文脈を捨象したり、高度に一般化した結果ともいえる。

「パターン」は「ベストプラクティス」である、という説明もあるが「汎化した」ベストプラクティスという考え方には正直なじめない。

設計でたいせつなのは、現在の特定の文脈においてのベストプラクティスを見つけることなんだと思う。

自分は、特定の文脈の経験、その中で身につけてきた勘と度胸を重視しすぎていることは自覚しているつもり。

だからこそ、一般的な原理とか基本原則を学び直そうとは思う。
でも、設計は、最後は、今の特定の文脈の中でのベストプラクティスを求める、ことだと思う。

問題は、全体力と同じ。
「文脈力」をどうやって勉強し身につけるか。

全体力/文脈力の学び方


難しすぎるテーマではあるが、今年は、いくつかチャレンジしてみたい。

■学習パターン


慶応SFCの井庭先生の研究室のパターンランゲージ研究の成果の一部として

学習パターン

が公開されている。これを題材に技術者仲間で「技術者の学び方」をテーマに勉強会を続けていた。

それがひとつのきっかけになって、DevLove のイベントで、井庭先生に講演いただいたり、対談までさせていただいたいのは、貴重な経験だった。

「全体力」や「文脈力」を磨くには、この学習パターンが参考になると思っている。

たとえば「鳥の眼、虫の眼」「広がりと掘り下げのT字」「隠れた関係性に学ぶ」とかは、そのまま「全体力」や「文脈力」の学び方になっていると思う。

あまり読み込めてないけど、同じく井庭研プロジェクトの、

コラボレーションパターン

も「全体力」「文脈力」の学び方の手がかりになりそう。

あたりまえだけど、井庭先生のパタンランゲージの研究はクリストファーアレグザンダーの業績を深く研究された上での活動。

これらの研究成果を「ソフトウェアの設計力」とくに「全体力」「文脈力」の能力アップの手がかりとして生かしていきたい。

国語力


最近、感じているのは「国語力」。

「設計力」と「国語力」は、基本的に同じものなのかもしれないとさえ思う。

自分自身の「本を読む力」「文章を書く力」は貧弱だと思う。さらにまずいことに、年々、劣化している気がする。

私の文章を書く力は、このブログでご覧の通りです。
もともと「自分のメモ書き」とか居直って書き始めているので、自分で読み返してもひどい文章が多い。

今年は、もうちょっと、ちゃんとした文章を書こうと言いながら、結局、こうやって、思いつくままにメモ書きモードから抜け切れない。

本を読む力は「ななめ読み」と「勝手読み」は得意。
その分、読み落としや、読み違えは日常茶飯事。

これだと思った本は、何度も読み返すほうなので、それなりに理解は進んでいる気もするけど、何度、読み返しても「こんなこと書いてあったんだ」という再発見の繰り返し。

そういう再発見があるのは、読み直す価値のある「良書」だから、と勝手な理屈で、自分のずさんな読み方を正当化してきたのが実態。

こういう読み方・書き方が、自分の設計のスタイルや設計品質に直結しているように思う。

設計力を向上するには、本の読み方、文章の書き方の基本をもう一度勉強しなおすのも、今年のテーマだと思っている。



プログラミングのパラダイムをちょっと考えてみた

私が仕事で使っているプログラミング言語は、最近は Java ばかり 。

SQL は、昔は得意技の一つだった。最近はほとんど書いていない(人まかせ)。

HTML, CSS, JavaScript は、画面まわりで必要なので、いちおう書く程度。
XML は、各種の定義ファイルやデータ交換形式として使うが、サンプルの写経レベルだな。

スクリプト言語だと、groovy を年に数回は使う。

プログラミング言語の遍歴


過去に仕事で使ったことがあるプログラミング言語を並べると、

Z80アセンブラ
C言語
Lisp
Prolog
Visual C++
C#
PL/SQL
...

ベターなアセンブラとして、C言語にのめり込み、C++ のわけのわからなさに挫折し、人工知能の研究プロジェクトで、Lisp と Prolog を使っていろいろ試作して、オラクルに転職してから数年間 SQLと PL/SQL の世界にどっぷりつかり、その後 ベターな C 言語として、Java にはまり込んでいった、というのが私のプログラミング言語の遍歴。

どの言語も、どっぷりつかっている時は、その言語が一番おもしろいと思ってやってきた。

Z80アセンブラはコンピュータを直接いじる感覚で、いまでも一番のお気に入りかもしれない。

C 言語を使っていた時も、メモリを直接操作するイメージで、プログラミングしていた気がする。
関数呼び出しがスタック操作で、malloc と free でヒープ領域を自己責任で管理。構造体は、バイトのアラインメントをいつも意識して書く感じがきらいじゃなかった。

Java をはじめた時も、バイトコードとか JVM の実装とかに異様に興味をもっていた気がする。

メモリ操作感覚とは対極にある言語だけど、もう一つのお気に入りが、Prolog。

・条件分岐やループがない。
・記述の順番は関係ない
・ファクト(事実)のセットと、ルール(規則)のセットだけを記述する。

そうすると、処理系が、事実とルールの中から、論理的な答えを推論してくれる。

Prolog を使った、こういう宣言型の論理プログラミングが、プログラミングのあるべき姿なんだと漠然と思っている。

SQL も事実(データ)と導出ルール(SQL)の宣言のみ、格納や取り出しの順番は関係なし、というところは、Prolog と通じるところがある。(だからSQLも嫌いじゃない)

Erlang は Prolog の影響を受けているらしい。パターンマッチングの感じとか似ているかな?

プログラミングのパラダイムって何?


パラダイムをどう定義したり、どう説明すればいいのかはわからないけど、感覚的には「発想」「常識」「基本の枠組み」あたりをイメージしている。
それも「無意識」に使う、体にしみこんだ「発想」「常識」「枠組み」という感じ。

最近、関数型プログラミング向きの言語が復権(?)してきているっぽい。久しぶりに「プログラミングのパラダイム」という言葉を思い出し、この記事を書き始めてみた。

最近の実用的な言語は「マルチパラダイム」、つまり複数のパラダイムをミックスした言語があたりまえらしい。

プログラミングのパラダイムを原理主義的に分類したり、比較することは意味はないかもしれない。

ただ、複数のパラダイムを経験するのはプログラム能力の向上には、とっても良いことだと思う。
複数のパラダイム、つまり異なる発想や枠組を知ることで、より質の高い設計や実装ができるようになるはず。

特定のパラダイムが絶対的に優れているとか、劣っているとか、そういうことはないんだと思う。

まったくの新しいパラダイムが生まれて、そちらに完全にシフトする(他のパラダイムを駆逐する)ということはプログラミングの世界ではないんじゃないだろうか?

必要に応じて複数のパラダイムをミックスして、仕事できる能力がたいせつなんだと思う。
モダン(?)なプログラミング言語は、マルチパラダイムが当たり前になってきている。だから、複数のパラダイムをベストミックスすることが、重要なプログラミングスキルになってきていると思う。

パラダイムって無意識というか、体に染みついた癖みたいなところがあるので、なかなか、マルチパラダイムを文脈に応じて自在にベストミックスというわけにはいきませんが。

今の自分はマルチパラダイムとベストミックスにどんな立ち位置で、どんな方向に進んでいるのか、ちょっと考えてみた。

動的型付+スクリプト vs. 静的型付+コンパイラ


自分は「静的型付+コンパイラ」にどっぷり。
動的型付+スクリプトで、複数モジュールに分割する規模のプログラムを書いたことがない。

※私は経験がないと言っているだけです。

動的型付+スクリプトは、食わず嫌いかな。
自分がプロとして責任もてるのは、静的型付+コンパイラだけというのが素直な気持ち。
JavaScript はちょっとなあ、と思っているけど、Typescript は面白そうと思う自分がいる。

動的型付+スクリプト言語で、大規模なアプリケーションを開発している現場では、リファクタリング、名前の変更、クラスの抽出、メソッドやフィールドの移動とか、どうやっているだろう。興味はある。

自動テストをたくさん書く?

設計駆動 vs. テスト駆動 ?


プログラム言語のパラダイムからずれるどけど、これもプログラミングパラダイムの一種ですね。

私は、設計駆動派。

単体レベルの自動テストは書かずに済むならそれが一番と個人的には信じている。
自分で仕事していて、自動テストを書かなくても、それほど、バグや変更の副作用に悩んだことがないので、設計スキルを磨けば、単体レベルの自動テストは、むだなんじゃないか、と心のどこかで思っている。

業務系のアプリケーションでは、個々の部品は、それほど複雑なロジックにはならないので。
というか、ロジックが単純明快になるように設計・実装するのが腕の見せ所と思っている。

命令的 vs. オブジェクト指向的 vs. 関数的


「型」という言い方があまり好きではないので、あえて「的」と書いています。
「的」というのは、程度の問題という、意味も込めて。

「わり」と命令的か、「まるで」命令的ではない、という言い方ですね。

手続き型とか構造化プログラミングは「命令的」な世界の一部だと思っています。
(加筆:「ると」さんからのコメントにある通り、この解釈は私の勉強不足かな)

三つのパラダイムは、もちろん特徴(癖?)がある。ひとつのパラダイムが体にしみつくと、他のパラダイムのプログラミングスタイルは、なかなかなじめない、ぴんとこない。

パラダイムの違いを精緻に考えとか、得意じゃないので「ようするにこういうことでしょ」と自分勝手の割り切り解釈ですませている。

私の割り切り解釈のキーワードは「データ」と「ロジック」。

命令的パラダイム

データはデータ、ロジックはロジックと分離して考える。
で、命令型のロジックの基本は「データ」の書き換え。

A = 10;

と書くと、Aの内容(参照先)を、10に上書きしろ、という代入命令になる。

if 文、for 文を駆使(?)して、データを書き換えまくるのが、命令的パラダイム。

関数的パラダイム

こちらも、データとロジックは分離する派。
データを immutable (不変)にするのが大原則であり重要な特徴。
すべてのデータは関数の評価結果として表現できる、という意味でロジック宣言だけのパラダイム、ということができるのかな?

関数的なパラダイムだと、

(A = 10)

は代入ではな「A は 10である」という宣言になり、それを評価すると、真か偽が返ってくる。

Prolog もこっち系。(評価ではなく、パターンマッチングと単一化というのは別の世界?)

関数的なパラダイムだと、関数そのものを返す、関数をパラメータとして渡す、関数の結合や合成ができる。
命令的、オブジェクト指向的なパラダイムがしみ込んでいると、ここが、感覚的にわからないところ。
私も、理屈はある程度わかっているつもりですが、感覚的にはついていけていません。

関数を投げると、言語処理系が文脈ごとに、うまい具合に、うまいタイミングで評価してくれる。
これが、感覚的にどうもわからないというか、なじめないというか...

ループ構造も関数的なパラダイムでは、for 文ではなく、再帰ですよね。

関数的なパラダイムだと、List, Set, Map, Tree などのデータ構造(コレクション)の扱いが、めちゃくちゃエレガントにできる(らしい)。

このコレクションの扱いに向いていることと、immutable 原則とが合わさって、マルチスレッド/マルチコア/マルチサーバーを使った、並行処理とかビックデータ処理とかのプログラミング向きということで、最近は関数的なパラダイムが脚光をあびるているんだと思う。

あと、関数型といわれる言語って記法が記号だらけで予約語も略語が多い。
記述は、冗長になるけどわかりやすさを追求するなら、記号や略語ではなく、日常的な英単語で記述する関数型言語があってもいいんじゃないかなあ。

オブジェクト指向的パラダイム

このパラダイムは、「データ」と「ロジック」は一体化すべし、が基本の発想。

命令的なパラダイムも、関数的なパラダイムも「ロジック」をデータと分離することで、いろいろ面白いこと(?)ができると考える。
データとロジックを分離するから「疎結合」になり、扱いやすくなる、という発想かな?

オブジェクト指向的なパラダイムでは「データ」と「ロジック」を一体にして「凝集度」を高めたオブジェクト(ソフトウェア部品)を用意することで全体のソフトウェアが組み立てやすくなる、という発想。
かつ、オブジェクト内部のデータ(状態)は、外には見せないのが基本の発想。

C言語の構造体宣言とは正反対の発想。
C言語だと、ロジック追うには、まず構造体の宣言を読む。構造体を隠ぺいされたら、わけがわからない。
オブジェクト指向だと、オブジェクト内部のデータ構造は、プログラムを理解するために知るべき情報ではない。

部品(オブジェクト)間の関係は、できるだけ「疎結合」にする。
データやロジックの実装は隠蔽して、オブジェクト同士は、できるだけ単純な「メッセージ」のやりとり方式にすることで、結合度を下げる。

多態(ポリモーフィズム)は、同じメッセージを送っても、その時の文脈に応じて、適切なオブジェクトが適切に反応するという仕組み。(メールの宛先は info で、実際には、その日の受付担当者が処理する、というモデル?)

Java は、オブジェクト指向的な言語という一面は確かにあるけれど、別の一面は、C 言語そのままの命令的なパラダイムの言語でもある。

Java でプログラミングをしていても、

・public 宣言だらけ
・get/set で、隠蔽したはずのデータ(状態)を、生のまま直接操作
・インタフェースや委譲など、間接参照の仕組みを使わない
・実装クラスへの参照が網の目のように広がっている(ドット連結)
・メソッドは、パラメータ渡しと返値というC言語の関数的に設計

こういう書き方だと、オブジェクト指向的なパラダイムが目指す世界とは、だいぶ違う世界。
Java を使って、命令的パラダイムだけでがんばっている書き方。
それも構造化プログラミング以前のグローバル変数、goto文天国の臭いがぷんぷんする書き方。
public な setter/getter って、グローバル変数そのものなんだけどなあ。

自分は Java でプログラムを書くなら、オブジェクト指向のパラダイムを中心で行きたいと思う。
言語仕様として、C言語的な命令的、手続的な書き方ができちゃうだけに、意識してオブジェクト指向的に設計し実装する必要がある。

Java でオブジェクト指向的に書く場合も、関数的なパラダイムから取り入れたい発想は多い。

まず、immutable (不変)。これがほんとうに大切。

Web アプリケーションしかり、マルチスレッドプログラミングしかり。
immutable なオブジェクトを、積極的に活用することで、プログラムの安定感がおどろくほど向上するし、変更の副作用も、激減する。

ドメイン駆動というか、業務のロジックとしても「 immutable 」が正しいことが多い。
ビジネスは、起きたこと、約束したことを「事実」として記録し不変にする(改ざんしない)ことが大原則。

情報の変更が発生した場合は「上書き」ではなく「変更があった」という事実を不変データとして「追加」で記録するのが業務アプリケーションの基本パターンですね。

オブジェクト指向的なパラダイムの「データ」と「ロジック」の凝集は良いことばかりではない。両刃の剣。
設計を間違えると悲惨なことになる。
特に、肥大化したクラスは、データとロジックのごった煮になっちゃって、手がつけられなくなる。

オブジェクト指向でも関数的パラダイムの「関数」を分離して扱いたいシーンはいろいろある。

Java の場合、匿名 inner クラスでメソッドをラッピングしたり、Comparator インタフェースを実装したオブジェクトをコレクションに渡して、処理を委譲する、などの仕組みを使う。
関数的なパラダイムで「関数」そのものを渡せれば、ここらへんはもっとすっきり書けるはず。

そうなれば、Java の中途半端なコレクションフレームワークも、もっとエレガントになるはずなんだけどなあ。
拡張 for 文でがんばるシーンが激減できそう。

関数的なパラダイムの言語は immutable で関数が第一級の構成要素、というのが、言語仕様として枠組になっている。

Java とかだと、immutable や関数渡しのテクニックは、言語仕様としては明示できないので、設計や実装のガイドラインとして、あるいはイデオムや小技として使うことになる。

クラスベース vs. プロトタイプベース


この二つを「オブジェクト指向的なパラダイム」と、ひとくくりにするのは、まずいのかもしれませんね。

クラスベースとプロトタイプベースでは、別の発想のプログラミングが必要だから、別のパラダイムと考えたほうが良い気がする。

JavaScript だと .prototype のうまい使い方が、いろいろな技のネタになっているようですね。

クラスベースでも、Ruby だとクラスもオブジェクトとして扱うという発想のようです。
ここらへんも、こってこっての Java プログラマにはなじみにくいんだよなあ。

プリミティブ vs. コレクション vs. 型のユーザ定義


データをどういう扱い方をするかの発想の違い。

プリミティブ重視

命令パラダイムでは、データは、整数や float など、プリミティブ(固定のバイトサイズのビットパターン)が、根底にある発想。

アセンブラなら、メモリ番地へのラベル、C 言語ならポインタを使って、固定のバイトサイズのメモリをいじりまくる。
(だから、効率的なコードが書ける、という側面もある)

コレクション重視

関数的なパラダイムは、データの扱いかたは、なんといってもコレクション、特に「リスト」ですね。
Lisp に代表されるように、あらゆるものをリストとして数学的なモデルで処理することが、関数的パラダイムの基本思想。

数式を美しいと思うか、わけわからんかと思うか?
美しいいと思う人は、まちがいなく、関数的パラダイムに向いている。
わけわからんと思う人は、オブジェクト指向的パラダイム向き(だと思う)。
オブジェクト指向的なパラダイムでは、日常使っている言葉を、そのままクラス名やメソッド名にすることが、基本の発想なんだと思う。

関数的なパラダイムは数学、オブジェクト指向のパラダイムは文学、と言った人がいるが、そういう感じがする。

命令的パラダイムに向く人?
メモリとCPUの構造が面白いと思う人は命令的パラダイム向きかな。
メモリとCPUはよくわからないが、プログラミングと言えば命令的・手続的パラダイムしか経験がない、という人が多いような気がする。命令的なパラダイムに向いていたわけではなく、最初に命令的なプログラミングを経験してそのまま、というパターンが多いのかもしれない。

Java の入門書なんて、これでもかというくらい命令的パラダイムに振り切った説明やサンプルばかりだからなあ。

型のユーザ定義

(クラスベースの)オブジェクト指向的なパラダイムでは、いろいろな型を必要に応じて宣言して、それを部品としてプログラムを組み立てていくスタイル。

ドメインモデル中心でソフトウェアを設計開発する、ドメイン駆動設計やるなら現時点ではオブジェクト指向的なパラダイムの一択だと思ってやっています。
(Prolog の論理パラダイムを遠くに見ながら)

業務の言葉を使ってロジックを組み立てるには、やはりオブジェクト指向のパラダイムがやりやすいと思う。

宣言的 vs. 手続き的


最後に「ロジック」記述のパラダイムについて。

ロジックを説明するときに、

・手続き
・処理
・計算

とか、命令的パラダイムの説明が多いように思う。

これは、いろいろなプログラミングパラダイムを考えるときは、よろしくない。

ロジック(論理)の記述方法は、命令的な手続の記述とは限らない。

ビジネスのルール・約束事を、宣言的ルールとして書くことも「ロジック」の記述。

論理(ロジック)を「手続き的」に書くか「宣言的」に書くかは、プログラミングのパラダイムの大きな分かれ目の一つ。

Prolog のように、言語仕様として宣言的にしか書きようがない言語だと、話は簡単ですね。
SQL, HTML, CSS, XML もみんな宣言的に書くための言語。

問題は、手続き的にも宣言的に書けてしまう言語。

Java は、母体が C 言語だから、命令的パラダイムで手続き的に書く方が一般的かもしれない。
Java の初心者向けの本やサンプルが、命令的パラダイムの説明だけだし。

Java は広く使われるようになったけど、オブジェクト指向的なパラダイムでのプログラミングは、それほど普及していない。

Java で書けば、自然にオブジェクト指向のプログラムになるわけではない。

コード量からいったら、命令的パラダイムで書かれた Java プログラムが圧倒的に多いんだろうなあ。
Java 言語の入門書や解説書、サンプルコード、Java 自体のソースコード、公開されたさまざまなオープンソースコード...

どれを見ても、命令的なパラダイムの発想や書き方が多い。
それらを参考に学習すれば、そりゃ命令的なパラダイムが体にしみついちゃいますよねえ。

JavaScript だと、命令的にも、関数的にも、オブジェクト指向的にも、どうにでも書けちゃう。
Scala とかもそうかな。 それがいいことなのかどうか...

What vs. How


宣言的に書くなら、What に注目すべし。
What に名前をつけて、それを、パッケージ名、クラス名、メソッド名、変数名に積極的に使う。

What は、結果として「何が欲しいか?」とか、関心の焦点は「何か?」という問いかけから見つけていく。

手続き的に書くなら、How に注目して、それに名前をつける。

業務アプリケーションの場合、業務手順は文字通り「手続き」的に書くのが正しい。
ただしは、それは業務の概念での話。

業務アプリケーションを、コンピュータ上の一連のデータ操作の命令、手続きとして書くのが広く普及しているアプリケーションプログラミングのスタイルのようです。

コンピュータのデータ操作の手続き( How ) を隠蔽して、表に見せる顔は「何が欲しいか?」、「関心の焦点は何か?」という発想で、What 重視でクラスやメソッドを組み立てるのが、良いオブジェクト指向プログラミングなんだと思う。

What 指向の場合は、基本的に記述の順番は関係ない。

論理プログラミング的な発想だと、

A=B;

は、代入ではなく、AはBである、という宣言になる。

こういう宣言的な書き方は、データ(状態)が immutable であれば、どういうタイミングで、どういう順番で評価しても、同じ結果になります。

What 指向というのは、そういう意味で関数的パラダイムの発想ですね。

結論(?)


Java でオブジェクト指向的に書くことを大切にしたい、と言いながら、

・immutable で
・What 重視で
・宣言的に

となると、それって関数的なパラダイムでは、という突っ込みがありそうですね。

私もよくわかっていないんですよ。

Java 言語を使いながら、オブジェクト指向的パラダイムの進化形を目指している?
関数的パラダイムに入り込みながら、実装の道具としては Java に執着してミスマッチを起こしている?

私の今の感覚では、オブジェクト指向的なパラダイムの発展過程なんですけどねえ。

どうなんでしょう?

設計の基本パターン:Whole-Part(全体-部分)

良いソフトウェアの設計は、小さくて、気の利いた「部品」を、うまく「組み合わせる」こと。

役割が単純で明確なオブジェクト(部品)が、集まって、協力して、なにか人の役に立つことをしてくれる。
そういうソフトウェアを設計するためのテクニック、基本パターンの一つが、 Whole-Part(全体-部分)パターン。

Part = 役割が単純で明確なオブジェクト
Whole = Part を集約して、何か役にたつことをする

Part には、どんな役割を持たせ、Whole には、どんな役割を持たせるのが良いかをパターン化しものが、 Whole-Part パターン。

前から知ってたパターンだが、今回、新卒採用の求人票のモデリングをやるために、参考書を読み返しながら、このパターンの考え方をもう一度、整理してみる。

「求人票」が Whole (全体)。

Part ( 部分の方は) ,

企業概要、募集職種、基本給や諸手当、応募方法、選考方法、提出書類、....

ざっと見渡しても、データ項目数は、100以上はある。
全体-部分パターンではなく、一枚岩クラスで作ってしまえば、設計は不要になるけど、完全なアンチパターンですね。

22大学の求人票(紙ベース)を予備調査をして、だいたいの項目や、おおよその構造は、つかめてきた。
本格的にモデリング・設計する前に、Whole-Part(全体-部分)パターンをおさらいをしておく。本をひっくり返しながら、メモ書き。

主たる参考情報は、

POSA:Pattern-Oriented Software Architecture
(邦訳:ソフトウェアアーキテクチャ―ソフトウェア開発のためのパターン体系)

「ストリームラインオブジェクトモデリング」と「ビジネスパターンによるモデル駆動設計」の関連個所を参照している。

Eric Evans の DDD:Domain-Driven Design 「ドメイン駆動設計」のネタも使う。
ケント・ベックの「実装パターン」も取り上げる。

まずは、基本的なところから。

Whole-Part(全体-部分)パターン


Whole は、集約(aggregate) のルートになるオブジェクト。
Part は、集約される側。

Whole-Part パターンの狙いの第一は、 Whole が、便利で単純なサービスを提供すること。

使う側(クライアント)のオブジェクトが、parts オブジェクトを、自分自身で組み合わせながら、使う面倒くささを解消する。

あるいは、ある巨大なオブジェクト(なんでもクラス)の、さまざまなAPIの動作を、使う側が研究して、適切なAPIメソッドを、都度、選んで使うしんどさを減らすこと。

Whole クラスの設計目標は、「面倒くさがり屋のクライアント」オブジェクトに、ワンストップで、単純で、わかりやすいサービスを提供すること。

Whole の内部は、役割が単純な、小さな Part に分割しておく。
こうすることで、変更が必要になった時、変更すべき箇所を特定しやすくなり、変更の副作用が局所化される。つまり、簡単に、安心して変更できるようになる。

クライアントオブジェクトに使いやすいサービスを提供するのも、変更を容易で安全にするのも、ようするに、コードを書いている自分たちの仕事を楽にするということ。

Whole-Part パターン適用の機会


全体-部分で設計に適用する機会は、開発初期よりも、ある程度、開発が進んでからのほうが多いと思う。

a. 大きくなったクラスを、小さなクラスに分割する ( Part クラスの導入:トップダウン方式 )
b. 複数のクラスを組み合わせた仕事を、第3のクラスでラッピングしてやる( Whole クラスの導入: ボトムアップ方式)

リファクタリングのパターンでいえば、 a. は「クラスの抽出(149)」、b. は「委譲の隠蔽(157)」の実践かな。

関心の分離


Whole-Part パターンは、設計の基本原則「関心の分離」の良い実践例。
Whole の設計は、クライアントオブジェクトが持つ関心事だけに集中する。
Part の設計は、その Part だけの狭く、単純な問題の解決だけに集中する。

カプセル化と情報隠蔽


他の設計の基本原則「カプセル化と情報隠蔽」も、もちろん、Whole-Part パターンと密接に関係する。

Whole は、複数の Part を一塊にまとめ、かつ、外部からは、個々の Part の存在や振舞を隠す役割。

Whole は、個々の Part の単純な setter/getter を持つべきではない。
Whole のメソッドは、複数の Part を組み合わせて、はじめて実現できるサービスを提供することが基本。

setter/getter 以外に、どんな役割(メソッド)を持つべきか?

これが、Whole-Part パターン設計のキモというわけだ。

Whole が、どんな価値・役割を持つと便利になるかは、Whole と Part の関係をパターン分けして、考えると具体的でわかりやすくなる。

POSA では、Whole と Part の関係を三つに分類している。

・assembly-parts (組立品-部品)関係
・container-contents (入れ物-内容物)関係
・collection-members (集合-要素)関係

「ストリームラインオブジェクトライン」も基本は同じく、三つに分けている。
「ビジネスパターンによるモデル駆動設計」では、collection-members 関係をさらに、「グルーピング」と「タイプ」という2つのパターンい分けて考えている。

assembly-parts 組立品-部品 の関係


これはわかりやすいと思う。

例えば、iPhone という組立品は、その部品のCPU,メモリ,バッテリ,液晶パネル, .. とは、別の次元の機能を提供している。
利用者は、全体(Whole = assembly)として意識するだけで、部品を単体で意識することはない。

これが、assembly-parts 関係の設計の基本。

また、 assembly には必須の部品が、完全に揃っていることが必要。
動的に追加したり、取り外すのは、 assembly-parts 関係の関心事ではない。

こういう性質を持った Whole クラスの設計は、次のようになる。

assembly の生成


assembly のオブジェクトを生成するとき、必ず、完全な形で生成する。
new() して、set,set,set, .. という生成はダメ。

基本は、固定数のパラメータを持ったコンストラクタだけを宣言する。

new IPhone( CPU, メモリ, バッテリ, ... )

ケントベック「実装パターン」の「完全なコンストラクタ(110)」ですね。

もっとも、部品の数が多くなってくると、これはいかにも不細工。扱いにくい。

こういう場合、検討したいのが、DDD の Factory パターン。
Whole(assembly) を組み立てる方法は、全部、Factory オブジェクトに委譲して、そこに隠ぺいする。

iPhone ユーザにとっては、iPhone の部品構成や組み立てのプロセスは、関心事ではない。だから、組立に関する知識や、IPhone クラスではなく、別の Factory クラスに分離してしまう、という考え方。

assembly は不変(immutable)にする


いちど生成した assembly は、部品の変更などをしてはいけない。
だから、 setter メソッドは assembly に持たせてはいけない。

Whole-Part を、assembly-parts 関係で設計・実装するには、この完全コンストラクタ+setterの除去が定石になる。

役に立つメソッドの特徴


assembly-parts 関係の設計をする時、assembly 側のメソッドの原則は、

・複数の parts を利用する。
・もっとも良いメソッドは、assembly が持つすべての parts を利用するメソッド。

簡単な例:
PersonName クラスが、 LastName クラスと FamilyName クラスを部品として持つ場合。

・良いメソッド fullName()
・悪いメソッド getFamilyName(), getLastName()

悪いメソッドを用意して、利用する側で、

fullName = getFamilyName() + ' ' + getLastName()

なんて、やるのが典型的なアンチパターン。

Whole オブジェクトに、getter で部品を要求して、その部品を Whole オブジェクトの外側で組み立てるのはダメだよ、ということ。

また「姓」というPart を変更するための、 void PersonName#setFamilyName() メソッドは、良くない。

変更するなら、PersonName PersonName#changeFamilyName( 新しい姓 ) メソッドを用意して、別の PersonNameオブジェクトを生成する。

こういうことを意識して、assembly-parts 関係の、assembly オブジェクトを設計するのが、Whole-Part パターン。

PersonName クラスの例のように、「使う側の役に立つ便利メソッド fullName()」を assembly に持たせることが、重要。

単なる部品の提供者ではなく、部品を束ねて、もっと価値のあることをやるから、assembly として存在意義がある。

Part クラスのスコープ


assembly-parts 関係の Whole-Part パターンでは、クライアントから見た時は、Part クラスは不可視にする。
ひとつの実装方法としては、Java であれば、

・Whole クラスと Part クラスを一つのパッケージにまとめる
・Whole クラスは public にする
・Part クラスは、 デフォルト、つまり、パッケージ内にスコープを限定する。

こうすることで、 Whole クラスは、自由に Part クラスにアクセスできるが、外部のクラス(クライアント)は、Whole クラスしかアクセスできない。

こういう可視性で、うまく Whole-Part を設計・実装できるのが、良い設計になっているということ。

container-contents (入れ物-内容物)関係


この関係は文字通り、コンテナと、その中身ですね。
例えば、トラックと、そこに積み込む、荷物、という関係。

オブジェクトの構造は、assembly-parts 関係と同じかもしれない。

container-contents 関係の特徴は、Part を動的に add したり、remove することが前提。
完全コンストラクタで、setter をなくすという、assembly-parts 関係とは、正反対。

ただし、add/remove と、set/get をいっしょに考えてはいけない。
この違いを意識することが、この関係で Whole-Part パターンで設計するキモ。

container オブジェクトの生成


コンテナは、最初は、空っぽ(荷物がない状態)でも良い。
ただし、なんらかの「ポリシー」を持って、生成する。

積んで良いものの判断
最大積載量
積載時の配置
...

などを判断するための知識を持つのが、この関係の基本。

単純な例では、クラスに固定的に宣言しても良いと思う。
ニーズによっては、動的にポリシーを変えたいかもしれない。
この動的な変更や汎用化のテーマは、とても面白いけど、話が複雑になるので今回は、パス。
(目先の課題の 求人票オブジェクトには、必要なさそう)

ハイライトは add メソッド


containerのキモは、なんといっても、add() メソッド。

積み込みに関する知識と「現在の状態」を元に、

・積んで良いかの判断をする。
・OKなら、積み込む。
・積んではいけない時には、別の振舞を用意する。

assembly-parts 関係では、Whole は(変更可能な)状態を持たない。

container-contents 関係では、逆に「状態」が変わるので、いつも、その「状態」を意識することが重要になる。

特にある状態の時に「積み込みがNG」の場合の設計が重要。

例外のスローは、たぶん、良くない設計。
アプリケーションレベルで、積み込み可否をチェックして、コンテナを使う側のクラスで、それなりの対応をするのが基本だと思う。

canAdd() で問い合わせて、OK なら add() する、という単純な約束事(プロトコル)で使う。
この場合は、add() は、例外を返す設計でよい。
状態を確認せずに、add() を使うのは約束違反だから、こういう場合は、ランタイム例外 IllegalStateException をスローする設計が自然。

クライアントが、積み込めない理由を、より詳しく知る仕組みが必要かもしれない。

add() メソッドが、その return 値で、OK/NGの通知,NGの場合はその理由を扱う設計はさける。
そのメソッドの役割が複雑になって、潜在的なバグの原因、あるいは、変更がややこしくなる可能性が十分。

最初は、最大個数だけのポリシーでよいけど、だんだん、重量とか、冷蔵可否とか、ポリシーが増えてくると、add() だけで、すべてもまかなう設計は破たんするのが目に見えている。

「問合せと更新の分離」も大切な設計原則。

状態の管理


Whole つまり、container クラスは、「状態」を管理して、適切に扱つことが重要な役割になる。

単純な例では、 isEmpty() とか、isFull() とかがメソッドの候補になる。

もうちょっと複雑な例では、Facebook や LinkedIn のプロファイル情報の登録と管理がある。

転職サイトは、基本プロファイル、連絡先、直近の職務経歴などは、必須制約が多い。
assembly-parts 関係の Whole-Part パターン。最初に、完全な登録を強制する。
登録の順番は、ウィザード形式というか、一定の順番で登録してユーザインタフェースが多い。

Facebook や LinkedIn は、プロファイル、連絡先、職務経歴は、好きな時に、好きなところだけ、登録するスタイル。
そして「情報不足だからもっと登録したほうが良い」とかリコメンデーションメッセージがでたりする。

これが、container-contents 関係で、container クラスが持つべき役割になる。

つまり、「本来、こうあったほうが良い」という知識と、「今はこういう状態」という二つの知識を使って、リコメンドという情報をだす。「不完全」であることと「変更」を前提にしたスタイル。

Facebook や LinkedIn のプロファイル登録のリコメンドを注意深く見ると、プロファイル情報(contents) の管理役の container クラスは、「現在の状態の判断知識」「状態に応じたメッセージを内容やタイミングの出し分け」などが、よく考えられて実装されていると感じる。

そして、そういう「知識」や「判断ロジック」は、おそらく柔軟に変更が可能な設計・実装になっているんだと思う。

container-contents 関係で、 Whole-Part パターンを実践する場合の、良い研究ネタだと思っている。

「不完全」で「変更」が全体の情報のかたまりを、シンプルにわかりやすく扱う設計には、container-contents 関係の Whole-Part の設計を実践の中で、いろいろチャレンジしたい。

add/remove という名前


set/get よりは、ましだけど、container のメソッドの名前としては、もっと、意味を持たせた名前にしておきたい。

荷台の積み下ろしなら、load/unload だろうし、予定表だったら makeAppointment/cancel とか。

ここらへんは、DDD の考え方です。また、「実装パターン」でも「意図を示す名前(97)」として、メソッド名について工夫する価値を力説している。

set/get の名前だけでがんばっても、ソフトウエアは動きます。
名前、考えるのは、面倒くさいし、大変なので、なんでもかんでも、set/get だけで書けば楽です。

でも、それは、アンチパターンですよ、というのが DDD の発想であり、ケントベックの考えなんですよね。
意味のある名前、業務的にぴったりくる名前にこだわるのが、良いことだと。

content (内容物)の設計


container-contents (入れ物-内容物)関係では、「内容物」は、いろいろなタイプのオブジェクトを格納できる。
でも、内容物のさまざまな特徴を、container がすべて知る必要はない。

container には、本でも、ワインでも、格納するかもしれない。本やワインには、いろいろな属性が考えられるけど、、container は、大きさと重量だけを知りたいのかもしれない。「isワレモノ」は、必要かな?

container-contents 関係で、contents 側は、container の関心事だけを抽出した狭いインタフェースを用意して、それぞれの荷物に固有の特性・メソッドは隠蔽するのが基本だと思う。
contanable (格納可能)インタフェースとかね。

contents 側は、このインタフェースさえ実装していれば、後は、どんな特性を持っていても良い。

こういうちょっとした工夫で、関心事の分離とか、情報隠蔽を地道に設計・実装していくことの積み重ねが、ソフトウェアが、わかりやすく、あつかいやすくコツなんだと思う。

ひとつのひとつの作業では、何も変化は起きないかもしれないけど、こういう工夫・努力の積み重ねが、ある日、大きなブレークスルーを引きおこすことはまちがいない。( By Eric Evans in DDD )

collection-members (集合-要素)関係


三つ目の関係は、集合-要素の関係。
プログラミング言語だと、配列、リスト、Map、Set などですね。
もちろん、これも、典型的な Whole-Part パターン。

言語備え付けの List, Map, Set と、Whole-Part パターンの collection-members 関係の違いは、何か?

単純にいえば、言語備え付けのクラスの Wrapper クラスを作って、

・言語備え付けの List/Map/Set を隠蔽し、保護する
・使いやすい、目的にぴったりのお役立ちメソッドだけを提供する

こうすることで、わかりやすく、扱いやすい、専用目的のコレクションクラスがいろいろ揃ってくる。
私たちのチームでは、メンバーに、「(言語備え付けの)コレクションクラスは必ずラッピングすること」という原則でやっている。

ケントベック「実装パターン」の「コレクション用アクセッサメソッド(113)」の実践を大切にしている。

タイプ集合か寄せ集めか?


collection-member 関係では、member を、同じタイプ(同じクラス)に限定するか、いろいろなタイプ(異なるクラス)の混在を許すか、というのが設計ポイントの一つ。

List<Object>

List<IPhone>


ということです。

可能であれば、1タイプ限定の集合にしたほうが良い。
その方が扱いやすいに決まっている。

いろいろなタイプを混在する場合も、可能な限り、共通のインタフェースを宣言して、1タイプの集合にするのが、collection-menbers 関係の設計の基本。

List<Object> にしてしまうのはアンチパターンでしょうね。

collection クラスの設計


Java の List インタフェースを例に、collection という Whole の設計を考えてみる。

List インタフェースは大別すると

・個々の要素に関心のあるメソッド get,add, indexOf など
・集合全体に関心があるメソッド size, contains, sublist など

に分かれる。

Whole-Part パターンの設計は、collection のメソッド(役割)は、「集合全体」に関心のあるメソッドだけにする。

もちろん、collection 内部では、個々の member へのアクセスは必要になる。
でも、外部に公開するメソッドは、集合全体に関心のあるメソッドだけにする。

これが、Whole-Part パターンの設計の基本精神。(個々の部品への関心は除去する)

個別にメンバを取り出すにしても、first(),last() など、意味のあるメソッドにする。
indexOf(0), indexOf(size-1) などはダメ。

これも、DDD 的には、あたりまえの発想なんだけど。
(どっちでもいっしょ、という人が多い?)

あるいは、subList( from, to ) も、目的によって、topThree() とかのメソッドを用意する。
subList( 0, 2 ) でも実現できるけど、topThree() という意図を持った名前、かつ、パラメータが無いシンプルな呼び出し、というのが、大切なポイント。

どちらで書いても、動作は同じ。だったら、topThree() をわざわざ書くのは、無駄、というのもひとつの考え方ではある。 DDD の Eric Evans や、ケントベックとの価値観とはだいぶ違うけどね。

SQL からのヒント


collection が持つべき有用なメソッドのヒントは、SQL の集約関数と集合演算にもある。

集約関数:count, sum, max, min , avg, rollup, ...

さらに、group by を使ったり、 WHERE句で、対象を絞りこんだり。
order by とかも。

どういう集約をすると便利か考えて、それを、collection 役の Wholeクラスのメソッドとして用意する。

collection が、getList() を提供するだけで、使う側で for 文で処理をする、というパターンはダメ。

集合演算 UNION, UNION ALL, MINUS, INTERSECT の発想も役に立つ。

collection 同士の基本演算。

集合(SET) を扱うのが基本の SQL の世界(パラダイム)が、collection-contents 関係の Whole-Part パターンの設計の具体例ということですね。

もちろん、 UNION ALL とか MINUS という名前を生で使うよりも、もっと、意味のある名前を、いつも模索したい。

候補者全員から最終選考に残った候補者だけのリストを生成したいなら、shortList() とかね。 longList は全員で、最終候補者は、shortList という言い方を、実際に選考業務のドメインでは使うことがある。

まとめ


Whole のメソッドは、Part を集約(Aggregate)して、はじめて実現できる振舞に徹すべし。

個々の Part への関心事は、Whole の外からは遮断する。(内部に閉じ込めて、隠す)

Whole 役のクラスが、 get/set/indexOf など、Part へのアクセサだけなら、完全なアンチパターン。

Whole を使う側から見て、役に立つ、気の利いたメソッドを発見すること。
そして、そのお役立ちメソッドを中心に、Wholeクラスの公開メソッドを設計する。

Whole-Part の設計が、ぱっと見えちゃうことは、実は少ないと思う。

・クラスが肥大化してきた
・クラス間の関係が、「横恋慕」「メッセージの連鎖」「不適切な関係」になってきた

こういう「いや臭い」をかぎつけて、「クラスの抽出(149)」「フィールドの移動(146)」「メソッドの移動(142)」「委譲の隠蔽(157)」のリファクタリングで、設計を地道に改善していく。

「実装パターン」として、フィールドの名前は「役割を示す名前(74)」に、メソッドは「意図を示す名前(97)」に改良していく。

そうやって、シンプルで、読みやすいコードへの改善作業を、日々続けていくことがたいせつなんだと思う。

Whole-Part パターンも、最初からパターンの適用を考えるというよりは、リファクタリングして、設計、実装を地道に改善しつづけると、結果として、Whole-Part パターンになるはずなんだ。

設計の原則:モジュール化

ソフトウェア設計には、パターン理解が大切。
でも、その前に、設計の基本原則(設計の基本テクニック)を、押さえておきたい。

いろいろな設計パターンに共通する、その根っこにある設計原則を会得すれば、パターンの理解や実践が、もっと楽に楽しくなる。

設計原則の会得は、ソフトウェア設計の免許皆伝、達人への道であり、ここぞという時の、銀の弾丸になる。

モジュール化

たぶん、これが、いちばん基本の設計原則。

デザインパターンも、アナリシスパターンも、アーキテクチャパターンも、モジュール化原則の適用例になっていると思う。

モジュール化の原則違反の典型例は、一枚岩(モノシリック)で巨大なソフトウェア。
別名、密結合、スパゲティコード。

こういうソフトウェアは、理解しがたく、変更が恐ろしく難しい。
バグがあちこちに隠れていて、副作用が怖く、とてもじゃないが、コードを変更する勇気(度胸?)はない。
「一枚岩」は、頑強なイメージだけど、ソフトウェアとしては、けして良い特性ではない。

ソフトウェア設計でもっとも基本的で、大切な原則は、全体を分割して「モジュール化」すること。
システム全体は、いろいろなモジュールを組み合わせて実現する。
単純に分割するだけでなく、「組み合わせやすい」設計が、「モジュール化」原則のツボになる。

「モジュール化」の概念は、1970年前後に、構造化プログラミングとか、議論されていたころに、ほぼ確立された設計原則。

開発言語、OS、設計・開発手法が、どんなに変遷しても、この原則は変わりようがない。
クラウド化が進もうが、スマートフォンが普及しようが、「モジュール化」は、ソフトウェアの基本設計原則。

というか、ソフトウェアに限らず、建物、船や自動車、電子機器など、モノづくりすべてに共通する設計の基本原則が「モジュール化」。

ソフトウェアもある程度の規模になれば、当然、モジュール分割して、それを組み合わせて全体システムを実現する。

ハードだと、モジュールは、目に見えて、手で触って、確認できる実体。
ハードの設計技術者は、成長の過程で、ある意味、自然に良い設計、扱いやすいもモジュール化を体得していく。

ソフトウェアだと、こういう体感ができない、あるいは理解に個人差が大きい。
だから、モジュール化の会得は難しいかもしれないけど、やはり設計の原則の第一は、「モジュール化」。

モジュール化の価値

機能的にはまったく同じ、ユーザインタフェースも同じで、きちんと動作しているソフトウェアが二つある。

A:モノシリックなソフトウェアシステム(別名:密結合スパゲッティコード)
B:うまくモジュール化されたソフトウェア (別名:高凝集部品の疎結合)

現在、ちゃんと動いている点だけで評価すれば、両方とも、価値は同じ。

でも、変更や拡張の必要がでてきた時に、AとBの価値は、まったく別になる。
Aのソフトウェアは、変更ができないのだから、「変更・拡張」の点で、価値が激減する。

エンタープライズアプリケーションでは「変更容易性」は、ソフトウエア価値の重要ポイント。

ビジネスは生き物だし、時間とともに、ビジネス環境も変化していく。
ビジネスの道具である、エンタープライズアプリケーションは、それに合わせて、容易に変更・発展できることに価値がある。

同じ開発コストをかけたソフトウェアシステムなら、もちろん、モジュール化され、「変更容易性」「発展性」に優れたソフトウェアの方が価値が高いにきまっている。

現在の SI ビジネス(受託開発)の現状は、この「変更容易性」「発展性」が価値として取引されることはないとは思う。
「発展性」なんて、評価できないし、値付けも難しい。

変更が、どだい無理なソフトウェアを押しつけられた保守担当者が、呪いの言葉を吐きながら、格闘している姿で、ソフトウェアの価値の低さが明らかになる。
でも、開発費用は払ってしまっているのだから、今更、その費用の回収は無理。

ビジネス的な動機(対価の裏付け)がなければ、良いモジュール化の設計原則を実践したり、そのためのスキルアップを目指す動機も希薄になるのも当然かもしれない。

でも、ソフトウェア設計でよい仕事をする人は、別に、金銭動機でモジュール化原則を実践しているわけじゃないように思う。

◎設計原則に忠実であったほうが、自分や仲間の仕事が楽になることを経験上知っている。
◎良い設計をした方が、楽しい。仕事にやる気がでる。

両方とも、無理やりそうしているわけではなく、仕事の基本スタイルが設計原則に忠実になっている。

設計原則に忠実で、良い仕事をする技術者をもっと評価できる制度や風土があれば、ソフトウエア技術者の育成には有効だとは思う。

大きな SI 会社や、企業の情報システム部門のしかるべき立場の人には、考えてほしいところではある。

モジュール化はしたけれど

ある程度の規模のソフトウェアになれば、当然、なんらかのモジュール化は行う。
プログラムファイルは一つではないだろうし、パッケージ構成とか、レイヤ化とかは(たぶん)常識。

でも、モジュール化しているはずなのに、変更は難しいし、発展性ゼロというシステムが多いのが現状かもしれない。

別名:泥団子システム

なんか、モジュールっぽいもの(泥団子)をいくつか作って、それを集めて全体を組み立てていく。
泥団子なんで、組み立てていくときに、都合が悪い場所があれば、ちぎったり、くっつけたりが簡単。
力づくで押し付ければ、ぐちゃっとなって、何とか、そこに収まってくれる。

自分たちがすべてのソースコードを変更可能な場合、泥団子方式が、普通の作り方になる。
( Hey ! それが、ソフトウェア開発のいいところだろ?)

泥団子になっちゃうのは、モジュール化や、それに関連する設計原則に違反しまっくているから。
簡単にちぎったり、くっつけたり、力づくで押し込むことができるのは、けして、良いことではないのです。

カプセル化

モジュール化に近い設計原則にカプセル化がある。

要素を集めて、がっちりとした容器に収納すること。

タイムカプセルをイメージしてもらうのが良い。
いろいろな情報をあつめて、堅牢な容器に納める感じ。

●メソッドは、ステートメントの格納容器。
●フィールドとメソッドを集めて、クラスに格納。
●クラスを集めて、パッケージ化。
●パッケージを集めて配置単位( jar )
●配置単位を集めてアプリケーション単位( war )
...

という感じで、ソフトウェアは、カプセル化の原則を実践するための仕組みがたくさん用意されている。

問題は、この仕組みを使っても、良いカプセル化は保障されないこと。

カプセルは、がっちりした容器で、収納したら、カプセルの中身には、外からおいそれと手を出せないのがカプセル化原則の意味。

ところが、メソッド、クラス、パッケージなんて、しょっちゅう変更の対象になる。
変更しないまでも、容器を開いて、中身をいろいろ調べるのが、当たり前。

設計の初期、試行錯誤のフェーズであればともかく、コードをごりごり書くようになり、モジュール間の結合が必要なったフェーズで、カプセルの中身を見たり、いじったりするのが当たり前だと、あっという間に、泥団子システムになってしまう。
たしかに、設計を改善するより、力づくで、ちぎったり、くっつけたり、ぎゅっと押し込んで形を整えたほうが手っ取り早い。

絶対変更するな、容器は開くな、とはいわないけれど、開発が進むにつれて、

●容器の中身を空けない(調べない)
●収容した内容を、おいそれとは、あっちこっちに移動しない

というのがカプセル化の原則ということは意識すべき。

がっちりした容器に格納し、安定するのが、理想形。そこになんとか、近づけようというのが、「カプセル化」原則の意図。

「変更禁止」というアンチパターン

カプセル化原則の誤った適用例が、「コードの変更禁止」。
あるいは、「一度書いたら、もう編集しない」、改善放棄アンチパターン。

いちど、モジュール分割し、クラスやメソッドを書いて、動作確認できたら、原則変更禁止、というスタイル。

これを実践すれば、表面的には、「カプセル化」を実現できる。
中身を開いちゃいけない、いじっちゃいけないのがカプセル化だから。

でも、これ、本末転倒。

カプセル化は、コード変更禁止令で実現するものではなく、その必要性をなくす設計をすべき、という原則。

コードの中身をみたり、クラス内やパッケージ内への追加や変更が頻繁に起きるのは、そこに、設計改善の余地が大きいということ。

クラスやパッケージの役割を単純に、明確にした設計をすれば、そのクラスやパッケージは、安定し、自然に、「カプセル化」された状態に進化していく。

「カプセル化された状態」に近づけようね、というのが「カプセル化」原則を意図なんだ。

もちろん、良い「モジュール」は、かならず、カプセル化されている。

ろくでもない設計を、変更禁止でカプセル化らしく見せようとすると、変更禁止を回避しつつ、変更を実現するためのコードが、不適切な場所に植え付けられ、じわじわと増殖していく。
そうなったら、もう、そのソフトウェアは、変更容易性も発展性も急激に失っていく。
価値の劣化。

情報隠蔽

「カプセル化」原則ととても近い原則がこれ。

「中身を見せない/見ない」というのが、この原則。

これは、関連個所のソースコードを確認することが日常茶飯事の技術者にとって、もっとも会得しにくい設計原則かもしれない。
オープンソースの良いところは、ソースコードを、自分で確認できることにあるのも事実。

ソースが読めない/公開されていない部品は使いにくい、使いたくないという技術者も多いと思う。

でも、中身を安易に見に行くのは、「情報隠蔽」の設計原則違反、ということは意識すべき。

よくできた部品(モジュール)であれば、中身を見る必要はないはず。

中身を見る必要がないように、パッケージ名、クラス名、メソッド名をうまく設計しようね、というのが「情報隠蔽」という設計原則。

モジュールの中身を見ることは勉強になるけど、開発しているときに、使うクラスの中身をしょっちゅう覗きにいくようであれば、「情報隠蔽」がうまくいっていない。
つまり、設計に改善の余地が大きいということ。

公開メソッドの多いクラスとか、public なクラスが多いパッケージは、「情報隠蔽」がうまくいっていない。

クラスの中身やパッケージの中身が透けて見えてくるくらい、公開情報( public 宣言 ) が多い設計は、密結合なコードが増殖する。

ちょっとした変更があちこちに波及するようになる。
それが怖くて、既存のメソッドとか変更できない。

結果として、似たような、ちょっとだけ名前と振舞が違うメソッドをちょっと書いてお茶を濁す。
そのメソッドが、呼び出す側に追加されるのか、呼び出される側に追加されるのか。

どちらにしても、扱いにくい、変更容易性・発展性に欠ける、ソフトウェアの道をまっしぐら。

あと、フィールドをそのまま set/get するメソッドを公開したら、それは、「情報隠蔽」原則に違反している。
それは、フィールドそのものを、public にしているのと同じ。
つまり、悪しき、グローバル変数を作っているということ。
setter/getter に変形しただけで、実体は、グローバル変数。

set/get でフィールドを生で扱うだけのクラスは、役立たずというか、存在意義が疑わしい。

クラスは、本来は、フィールドを完全に隠蔽して、何かフィールドに対して、加工したり、判断した結果を返すメソッドを持つべき。

フィールドの setter は、不変原則(immutable ) 違反でもある。
コンストラクタで、インスタンス生成時に、すべてのフィールドをセットするの原則。

フレームワークとかの都合で、デフォルトのコンストラクタや、フィールドを生で操作する setter/getter が必要になるかもしれない。
でも、それは、本来のクラス設計の原則からは外れている。
設計の腕を磨くには、フィールドが外から見えてしまう設計を、邪悪と感じることがまず第一歩。

HashMap とか、TreeMap とか、内部のデータ構造を公開しているコレクションクラスなんて、設計としては最低だということ。

モジュール化、カプセル化、情報隠蔽

たぶん、どの原則に忠実であっても、同じところにたどり着くはず。
(正解は、ひとつではないかもしれないけど)

それぞれの設計原則は、強調するポイントや、アンチパターンの現れ方が変わるかもしれないけど、「良い設計」としてイメージしている「名前の無い質」は、同じ。

私が「モジュール化」を、もっとも基本的な設計原則と考えるのは、「部品」を組み立てて「全体」を作る、というイメージがもっとも明確だから。

データ、操作、そしてオブジェクト指向

モジュール化は、オブジェクト指向的には、関連する「データ」と「操作」をひとつにまとめて(「カプセル化」して)、かつ、中身を隠蔽することを意味する。
正確には、これは、オブジェクト指向というより、「抽象データ型」の考え方。

「抽象データ型」は、コレクションフレームワークが良い例ですね。

List , Set, Map という情報の表現と操作を、クラスやパッケージにカプセル化している。
もちろん、内部の処理は、可能な限り隠蔽されている。

※ Java では、これは嘘です。
クラス名、コンストラクタ、メソッドに、内部のデータ構造やアルゴリズムのヒントがいろいろ漏れ出している。
性能に関する意思決定をプログラマにまかせているので、そうしているのだろうけど、「抽象データ型」としては、質が低いし、「情報隠蔽」の原則からはずれている。

「抽象」というのは、もともとは、「バイト」や「アドレス」というメモリ構造という物理実体と、if 文と goto 文連発のアルゴリズムの塊を、カプセル化したうえで、可能な限り、情報隠蔽し、「情報の集合」を抽象的に、つまり単純に扱えるようにしたもの。

メモリ上には、データと命令がある。(データと命令しかない)

良く使うデータの塊のパターン、命令の塊のパターンをカプセル化して、情報隠蔽を繰り返してきたのが、言語(とライブラリ、フレームワーク)の発展の歴史ともいえる。

データはデータでモジュール化し、操作は操作でモジュール化する、というアプローチもある。

抽象データ型とか、オブジェクト指向は、そうではなくて、「データと操作」を一つのカプセルに入れる、という発想。

単純なデータを扱うなら、操作だけをモジュール化したほうが再利用はやりやすい。
データの構造に重要な意味がある場合には、データと(その構造に強く関係した)操作を一体にする発想の方が、モジュールとして、利用価値が高くなる。

モジュール化という設計原則を実践するには、

◎強く関連するデータを、クラスやパッケージに集める。
◎そのデータに固有の操作を、そのクラスやパッケージに集めてくる。
◎クラスやパッケージは、可能な限り、公開インタフェースを少なくする。

●パッケージ内の public クラスは一つであるべき。
●その公開クラスの public メソッドも、できるだけ少なくする。

そうやって作ったパッケージが、わかりやすく、使いやすくなっていれば、うまく「モジュール化」できたということ。
そのパッケージがわかりにく、使いにくい、公開クラスやメソッドを増やしたい、というときは、設計改善の余地が大きい。

そのパッケージに集めようとしたデータやメソッドがおそらく、不適切な集め方をしているということ。

パターンとモジュール化

最初に書いたように、どのパターンも「モジュール化」の設計原則の忠実な実践結果。
モジュール化原則違反のパターンは、たぶん、存在しない。
(何をモジュール単位にするか、という考え方の違いはあるだろうけど)

アーキテクチャスタイル、分析パターン、設計パターン、実装パターンを問わず、どれも設計原則の適用結果。
パターンを理解するためには、まず、「モジュール化」という設計原則をおさらいしてみるのが早道。
あるいは、パターンとその適用に格闘しながら、「モジュール化」という設計原則を意識してみる、というのも良いかもしれない。

いずれにしても、単純な設計原則、特に「モジュール化」を理解し、習得し、設計の基本スタイルにすることで、設計の腕は各段に向上するはず。

アーキテクトを考える

Bruce A. Tate 著、まつもとゆきひろさん監訳、田和勝さん訳の

「7つの言語 7つの世界」

を楽しみながら読んでいる。

最初のページの「読者の声」の、

「複数のパラダイムを理解すると、設計能力が大幅に強化される」by Dr. Venkat Subramaniam

に、はっとさせられた。

読み進めるうちに、この言葉の意味が、実感できてきた。
「異なるパラダイムを、実感してみる」と「設計の発想やセンスが変化する」手ごたえがあった。

9章「まとめ」に、プログラミングパラダイムを4つに分類した要約がある。

●オブジェクト指向 --> Ruby, Scala
●プロトタイプ型 --> Io
●制約論理型 --> Prolog
●関数型 --> Scala, Erlang, Clojure, Haskell

※「手続き型」がないのは、この本では C, FORTRAN, COBOL, BASIC, PHP とか、手続き系の言語を取り上げていないから。

すべてを言語やそのパラダイムを高度に習得する機会はないだろうし、またその必要もないと思う。
でも、Suramaniam さんが書いているように、設計能力のスキルアップには、複数のパラダイムを経験し、理解することが、すごく役に立つはず。

この本の筆者自身、本を書くために未経験の言語に触れたことで、専門の Ruby の設計スタイルが変化し、設計能力がアップした、と書いている。

本を読みながら、インストールにはまったり、自分勝手なコードを書き始めて、脇道に入り込んだりで、なかなか、先に進まない。

いまのところ、Ruby , Io, Prolog までチャレンジしてみた。
ここまでだけでも、自分の専門の Java に対する別の見方、設計のヒントが手に入った気がする。

Io のシンプルなオブジェクト指向は、ちょっと感動もの。多重継承を実際にできる言語は初めてだったので、とても面白かった。

Prolog の、「事実とルールを宣言すると、問い合わせに回答してくれる」というパラダイムは、データベースとSQL、RESTful スタイルの リソース指向アーキテクチャ(ROA)と、共通性が高い。if や for が存在しない宣言型の言語パラダイムですね。

複数のパラダイム

パラダイムとは、基本的な考え方や発想という意味かな?

パラダイムは難しい言葉。
「視点」とか「観点」と置き換えて、考えてみたい。

異なるパラダイムというのは、異なる視点や観点を持っているということ。
あるいは、重きを置く、視点・観点が異なる、ということなんだと思う。

プログラミング言語でも、純粋に一つのパラダイムに振り切った言語は無い。
実用的な言語は、手続き型、オブジェクト指向、関数型などのパラダイムが融合(ごった煮?)しているのが実際のところですね。

ただし、その中でも、重視しているポイント、こだわっているポイントに、その言語のパラダイム、あるいは、思想が感じられる。

もっとも、言語のパラダイム(思想)と、それを使う技術者のパラダイム(発想)が、同じとは限らない。

Java なんかは、C言語の土台の上に作られたので、いまでも手続き型で、Java プログラムを設計・コーディングする人の方が多い印象。
言語のパラダイム(思想)と、その技術者の実装パラダイムのインピーダンス・ミスマッチですね。
まあ、オブジェクト指向でほんとうにやりたいなら、そもそも、Java じゃ無理、という意見もあるでしょうが。

いずれにしても、複数のパラダイム(複数の視点、観点)を経験し、理解するほど、設計能力がアップするのはまちがいない。

特定のパラダイムだけ、特定の視点・観点(つまり思い込み)だけでは、設計の発想やアイデアは広がらない。
設計(技術的な意思決定)の判断基準が、どうしても偏ってくる。

いろいろな技術者と仕事をしてみると、設計能力の高さを特に感じる人がいる。

●世の中には複数のパラダイムがあることを知っている
●どのパラダイムにも、良い点もあれば、限界やクセがあることを素直に受け入れる
●目的を達成するために、適切なパラダイムを選ぶ、組み合わせる

という感覚を持って仕事をしている人。

「異なる分野や視点の情報を集め、実践してみて、ものごとを観察し、理解する」のが楽しいと思う、知的好奇心が旺盛な人。

こういう人との仕事はほんとうに楽しい。自分の知らなかった視点・観点に生で、わかりやすく触れることができる。

こういう人は、目先では役に立たない情報まで集めている人が多い。
仕事ができる人なんで、余計な(?)情報収集やりながら、目先の納期にもちゃんと間に合わせくるところがすごい。

「別の世界に触れること」が楽しいし、また、その価値を実感してきているから、それが、仕事の基本スタイルになっているんでしょうね。

プログラミング言語の複数のパラダイムを経験・理解するには、「7つの言語 7つの世界」は、ほんとうに良い本。

筆者が宣言しているように、インストールやAPIについての説明を割愛して、「その言語に特徴的な考え方・こだわりポイント」を、簡明に記述している。もちろん、その言語のパラダイムを、うまく実感できるサンプルコードも豊富。

いわゆる「言語入門」本ではなく、「言語のパラダイムに実際に触れてみる」ことに、徹していることがすごいし、この本の価値だと思う。

で、この本を読みながら、プログラム設計能力だけでなく、アーキテクトとして設計能力をアップするためにも「異なるパラダイム」に触れてみることが、いちばんなんだろうな、と思った。

異なるパラダイム、つまり、異なる視点や観点を、複数経験して比較してみることが、アーキテクトの設計能力アップに有効なんだろうなと。

で、いくつか頭に浮かんだアーキテクトとして腕を磨くための「異なる世界の経験の仕方」を書きとめてみた。

アーキテクチャスタイルの4つの世界

まずは、4つのアーキテクチャスタイルは、ぜひ経験しておきたいところ。

ネタ元は、Hohpe/Woolf の Enterprise Integration Patterns (EIP) の2章の四つのスタイル。
(この章を書いているのは、PoEAA のマーチン・ファウラー)

●ファイル転送(データファイルのバッチ式の処理)
●データベース中心 (データベース読み書きのアプリケーション)
●リモート手続き呼び出し (同期式の手続き呼び出し)
●メッセージング (非同期、疎結合のアプリケーション連携)

特に、この本の主眼である、「メッセージング」スタイルを経験し、理解すると、アーキテクトとして別の次元にステップアップできる気がする。

自分は、ソフトウェア開発の仕事をして長いので、最初のころは、データファイルのバッチ処理式が基本方式だった。

その後、データベース中心(DOA)に目覚める(?)。
このスタイルでの設計・開発が一番長い。自分の基本パラダイム、もっとも重視する体に染みついた発想は、データベース中心。

リモート手続き呼び出しは、RESTful スタイルのリソース指向アーキテクチャ(ROA)として気に入っている。
(まあ、ROA も DOAの仲間ですから)

RPC , CORBA , WS-* など、重量級のリモート手続き呼び出しスタイルは、だいきらい。
(開発効率、実行性能や、運用時の障害などでなんどか痛い目にあって、このパラダイムにはかなり否定的)
というか、手続き指向のパラダイムそのものがしっくりこない。
PL/SQL (Procedual Language ) で、いろいろ処理手続きを書くより、テーブル設計をがんばって、SQL 一発で答えがでてスタイルが良いと思っている。

メッセージングは、最近、取り組み始めたスタイル。
これが、非常に面白い世界。

「7つの言語 7つの世界」で感じたたような、異なるパラダイムを知る、という意味で、メッセージングは、良いアーキテクトになるための必須科目だと思う。

ネットワーク上でのイベント駆動アーキテクチャ(EDA)、非同期メッセージングの並行処理は、目先で使わなくても、ぜひ経験しておきたい世界。
おそらく、クラウドコンピューティングの基本パラダイムなんだと思う。

自分は、 ActiveMQ と、 Mule ESB で、いろいろ、非同期メッセージングスタイルにチャレンジしている。
EIP 本にあるように、性能、信頼性、整合性維持、エラー処理など、課題は山ほどある。

同期が絶対条件のメソッド呼び出しで、データベースを操作するパラダイムの世界とは、まったくことなる発想や課題がそこにある。

そういう異なる発想に触れることで、バッチ処理や、データベース中心のアーキテクチャの設計にも、いろいろ、別の発想が浮かんでくるようになる。

データベースの5つの世界

データベースも、リレーショナルデータベース以外のパラダイムの技術が、いろいろ実用的になってきた感じ。

KVS, カラム指向、ドキュメント指向、グラフDB。

自分は、リレーショナル以外のパラダイムは、ほとんど知らない。

グラフデータベースには、ぜひチャレンジしてみたい。
幸い、Spring Data Graph for Neo4J なんてフレームワークもでてきたみたいなので、近いうちにぜひ。
グラフ構造(ネットワーク構造)は、業務アプリの分野でも、応用範囲が広いと思っている。
いままで無理やり、リレーショナルにマッピングしてきた情報構造をもっと自然に扱えるはず。

グラフのパラダイムに触れることで、リレーショナルの設計にも、別の発想がでてくるはず。

KVS 、カラム指向、ドキュメント指向も、それぞれアイデア(パラダイム)としては面白そう。

リレーショナルは、「行」指向のパラダイム。

複数のデータを、行にまとめることが、基本の発想。

カラム指向は、たぶん、これを90度転換した発想。同じカラムを一塊にすることを重視。
特定のカラムのデータ集計なんかは、こちらのパラダイムの方が合理的。

KVS や、ドキュメント指向も、それぞれ、ある視点を重視し、ある視点は軽視したり無視することで、成立している世界のはず。

概念だけではなく、それぞれの世界で、いろいろ実践して、感触を得ておきたいところ。

こういう異なるパラダイムのデータベース技術を、実際に使うかどうかといえば、たぶん、自分の仕事は、リレーショナルデータベース中心のまま。
技術者キャリアは、リレーショナルデータベースまでで終えるかもしれない。

でも、他のデータベースの異なるパラダイムを知ることで、きっと、リレーショナルデータベースの設計・実装のスキルをアップできるはず。

ソフトウェアシステムアーキテクチャ(SSA)の6つの視点

アーキテクトたるもの、ソフトウェアシステム全体を、見て、構想する能力が必要。

全体を見ることが簡単にできれば、だれでもアーキテクトなわけだが、なかなか、そうもいかない。

自分が、まがりなりにも、アーキテクト的な仕事をしてこれたのは、「複数の視点」を持つこと、「視点の数を増やすこと」を意識してきたからだと思っている。

優秀だが、アーキテクトになりきれない技術者もたくさん見てきた。
彼らと私に違いがあるとすれば「視点」の数、あるいは、視点へのこだわり度の配分だと思う。

例えば、次の6つの視点の関心事に、それぞれ、ある程度の知識と経験があること。
そのうえで、それぞれの視点に、バランスのとれた目配りをし、技術的な検討と意思決定を行う。

ネタ元は、ロザンスキの「システムアーキテクチャ構築の原理」

●機能
●情報
●並行性

●開発

●配置
●運用

一般的なソフトウェア技術者だと、「機能」と「開発」は、それなりに経験する機会は多い。

「並行性」「配置」「運用」は、開発チームのメンバーの経験だけでは、知らない世界になりがち。

小さなシステムだと、6つの視点(分野)をすべて経験できるが、小さい規模だと問題にならない視点も多い。

「並行性」「配置」「運用」なんて、小規模で利用者が一人、二人なんてシステムとか、データ量やトランザクション量がスカスカの開発環境だったら、たいした問題には、ならない。

アーキテクトあるいは、ソフトウェア設計者として、腕を磨くには、ある程度の規模で初めて具体化する、「並行性」「配置」「運用」の関心事や、その解決方法の知識を持ち、経験を積むことが必須。

「7つの言語 7つの世界」の教訓は、「実際の開発では使わない」でも「とりあえず試して体感しておく」レベルで異なる世界に触れるだけでも、設計能力に大きな変化をもたらす、ということ。

Io 言語を使って仕事をすることは、まずないだろうけど、Io の簡明なオブジェクト指向や、簡明さへのこだわりは、多重継承が簡単にできてしまう世界は、新鮮だったし、ほんとうに勉強になった。 自分の Java の設計に明らかに影響があった。

アーキテクトとして腕を磨くには、自分が直接担当していなくても、配置や運用の課題に取り組んで、機能視点や開発視点以外の世界に触れることが、重要だと思う。

経験・知識が、開発に偏っている技術者は、継続的配置(テスト、ビルド、配置の自動化)とか、(開発環境の)運用監視の自動化という視点で知識と経験の幅を広げるのがいいかもしれない。

ソフトウェアシステムアーキテクチャ(SSA)の4つの観点

「視点」は、ある分野に集中して、他の分野を見ないことで、問題の明確化や解決策の立案を行う手法。

「機能」を考えているときに、情報、並行性、配置や運用まで考えはじめると、わけがわからなくなる。
純粋に機能のことだけ考え、機能分割しながら、機能体系を整理していく。

「情報」を考えるときは、情報の構造の分析・設計に専念する。

このような「視点」オリエンテッド、「分割統治」と「特定関心事に集中」というスタイルは、重要だし、開発者にとっては、なじみがあるやり方。

だけど、システムのアーキテクチャを考えるには、決定的な問題がある。
部分的には合理的かもしれないが、全体として、どうか、ということ。

6つの視点ごとに、ばらばらに、関心事を整理し、解決案を考え実践しても、良いシステムにはならない。

全体を見通すことも、並行して行う必要がある。

これが「観点」。

「視点」は狭いところに絞り込んでいく手法。
「観点」は、離れたところから、俯瞰する手法。

もちろん、観点も複数持つことが大切。

4つの観点。
ネタ元は、これも、ロザンスキの「システムアーキテクチャ構築の原理」。

●性能
●信頼性
●セキュリティ
●発展性

大量の情報を、さくさくさばけて、安定して稼働しつづけ、情報漏えいや不正使用を防ぎ、機能の追加や変更に柔軟に対応できること。
それが良いシステムだし、それを実現できるアーキテクチャが大切。

「性能」という品質を達成するには、上記の6つの視点の関心事を、横断的に検討し、意思決定する必要がある。

アーキテクトとして腕を磨きたかったら、(実践ですべてを経験できなくても)、「性能」「信頼性」「セキュリティ」「発展性」のそれぞれの観点の知識の仕込みと簡単な実験と観察、体験を続けること。

とっかかりは、ロザンスキの本が良い。
それぞれの「観点」ごとの関心事とその対応案が書かれている。知識の元ネタとして、とっても便利。

実際に、体感してみるには、アプリケーションサーバーや、データベースサーバーなど、ミドルウェアの管理者マニュアルを読む。
ここには、性能、信頼性、セキュリティ、発展性の課題に対応した、さざまな機能と、その設定方法が説明されている。

それらの機能は、そのミドルウェアを利用するときに、課題になる、性能観点、信頼性観点、セキュリティ観点、発展性観点のさまざまな現場の課題への回答例なわけだ。

そして、できれば、設定を変えてみて、性能テスト(負荷テスト)、信頼性テスト(耐障害テストや復旧テスト)、セキュリティチェックテストを、自動テストツールを使って、実験してみる。

発展性は、実感してみるのは、ちょっと難しい観点かな?
機能の追加、別プラットフォームへの移植、他のシステムとの連携機能の追加などを、仮想テーマにして、現在のアーキテクチャだと、どんな限界・制限があるか、洗い出してみる、などの思考トレーニングは有効だと思う。

統合する

視点が6つ、観点が4つもあれば、結局ばらばらになりかねない。
異なる世界をばらばらに経験しただけでは、良いアーキテクトにはなれない。

全体を統合する能力を身に着けることが必須。

それが、観点を束ねるためのメタ観点ともいうべき「合目的」という観点。

個々の課題には、やまほどの設計の選択肢がでてくる。
そして、その組み合わせは無限。

その時に大切なことが、「そもそもの目的に合致しているんだっけ?」という問いかけ。

その機能は、面白いかもしれないけど、本来の目的に必要なの?
この情報とその情報は、本来の目的から考えたら、重要度がだいぶ違うのでは?
そこまで、信頼性が必要なんだっけ?
その程度の性能で、目的が達成できるの?
....

すべての技術的な検討、意思決定は、そのシステムの「本来の目的」に合致しなきゃいけない。

この「本来の目的に合致」という考え方で、自分に影響が大きかったのは、 Evans のドメイン駆動設計( Domain-Driven Design : DDD )。
ソフトウェアの本来の目的は仕事に役に立つ道具。
ドメイン駆動設計とは、「本来の目的」を、丁寧に、謙虚に、深く、理解した設計を心がける、というだと思う。

アーキテクチャも同じ。というか、アーキテクチャこそ、ドメインの概念で駆動していくべき。

アーキテクトにとって、一番の重要な能力は、この「全体としての目的合致」という発想であり、判断力だと思う。
ビジネスアプリケーションであれば、そのビジネスの目的や方向性を理解することが、アーキテクチャ設計の前提だし、基本なんだ。

「全体としての目的合致」のトレーニングは、ビジネスで、そのシステムに責任を持たなければいけない人との対話が一番。その人に理解ができる言葉で説明でき、その人の話す言葉の意味・意図がちゃんと聞き取れるようになることが大事。

6つの視点、4つの観点に、技術者としての知識と経験を持って、そのうえで、ドメインを理解して「全体としての目的合致」を目指し、ビジネスの責任者ときちんと話をする、というのは、とんでもなく高いハードルだと思う。
実際、こんなことが、現場で、完璧にできちゃう技術者なんているわけがない。(見たことがない)

ただ、そういう方向に努力はしたいし、その方向で良い成果を出せるように、腕を磨き続けたい。

そのためには、自分が今まで経験していない「別の世界」をお試しでよいから、触れてみること。
異なる視点、ものの見方、価値判断の基準の違いを、体感すること。

それが、アーキテクトの腕の磨き方なんだと思う。

目先の仕事に直結しない技術を、お試しでチャレンジするのは、現実的にはいろいろ問題があるかもしれない。
遊んでいる?

でも、発展する組織ほど「目先の利益に直結しない情報も抱え込んでおける」風土の組織、という研究があるらしい。

個人の技術的な興味というだけではなく、組織やチームのためにも、目先で直接使わない技術的な知識や経験の仕込みを地道に続けるのが、技術者のあるべき姿だと思う。

そうやって、異なる世界の視点・観点に触れて、幅を広げることが、技術者個人にとっても、その組織やチームにとっても、パワーアップになっていくんだと思う。

Object Oriented を考える オブジェクト指向→目的志向

ソフトウェアの設計・開発で、OO:Object Oriented は、根本的な考え方の変化(パラダイムシフト)だと思う。

ソフトウェア開発の世界では、"Object Oriented" は、誰もが聞いたことがある言葉だと思うが、意味や使い方が、人によって、状況によって、ばらばらな、かなり不可思議な言葉でもある。

OOP:オブジェクト指向プログラミング


オブジェクト指向言語を使ったプログラミングの総称?

もし、Java , C#, PHP5 などをオブジェクト指向言語と呼べるなら、世の中には、オブジェクト指向プログラマがたくさんいるわけだ。

実感とはだいぶ違う。

分析や設計の考え方が オブジェクト指向じゃないのに、言語だけ、オブジェクト指向(風?)でもそれは、OOPとは言えないですよね。

OOAD:オブジェクト指向分析設計


UML でクラス図とか描くこと?

オブジェクト指向言語の話といっしょで、ツールや手段が、 OOAD 風でも、実際の分析や設計が、オブジェクト指向とは、とても呼べないものが多いのが現状。
(というか、そもそも、分析・設計をやっていない?)

オブジェクト・モデリング


オブジェクト指向の「分析」は、オブジェクトのモデリング(模型づくり)をすること。
オブジェクト指向の「設計」は、オブジェクトのモデル(模型)を、実装にマッピングするための、いろいろな意思決定、決め事をすること。

こんな感じだと思っている。

OO は、「オブジェクトのモデリング」が起点であり、また、オブジェクトモデルによって、設計・開発を駆動していくやり方。

実世界を抽象化した「模型」?


オブジェクトモデリングの説明で、「実世界を抽象化して、実世界の模型を作ること」というのをどっかで見た。(あちこちで、似たようなことを読んだ記憶がある)

この説明がまちがっているとは思わないが、「抽象化」も、人や文脈によって、意味や使い方がばらばらな、不可思議な言葉なんで、この説明で、多くの人が、同じ理解をするとは思えない。

「実世界」もそうとう怪しい言葉。物理的に形があるもの、という印象が強すぎる気がする。

すくなくとも、エンタープライズアプリケーション( 事業用の業務システム)の世界では、伝票とか契約書という、物理的な実体ではなく、「申込」とか「契約」とか、そもそも、概念的な情報の扱いが中心課題。

「オブジェクト」を、実体のある「モノ(thing)」という感覚だと、業務アプリ開発で、オブジェクト指向的に取り組むのは、最初から無理なんだと思う。

「目的」指向


私は、学生時代の専門は、英米文学。その影響だと思うけど、コンピュータ用語の「もともとの意味」「ITの世界以外での使い方」に、とても、興味がある。

興味があるというより、コンピュータ用語も、言葉のもともとの意味、IT世界以外での使われ方で、理解するのが、本筋だと思っている。

Object (オブジェクト) は、もともとの意味は、「標的」とか「関心の対象」。

Subject(主体)が、特に関心を持っている対象が、Object(客体)になる。

関心事でないものは、世の中に存在していても、それは、Object とは呼ばない。
関心事であれば、実体がなく、頭の中だけに存在している概念でも、それは、Object である。

つまり、オブジェクトのモデリングとは、「関心事」のモデリングだということ。

業務アプリのオブジェクトモデリングとは、業務の関心事体系の模型を作ること。

業務の関心事が整理されている模型なら、表現形式がクラス図であっても、業務の専門家には、わかりやすいはず。

線の記法など細かいことは別にして、

パッケージ名
パッケージ内のクラス名
全体の並び

が、業務の専門家が自分の担当業務の主たる関心事に見えるようになれば、それが、良いオブジェクトモデルだということ。
(よくできた業務マニュアルの目次のイメージ)

当たり前だけど、「業務の関心事体系」であるオブジェクトモデルには、ソフトウェア開発には必要になる技術的な概念(用語)は、まったく含まれない。

中間的な関心事、最終的な関心事


業務の関心事といっても、中間的な関心事と、最終的な関心事がある。

ユースケースで考えてみると、

◎最終的な関心事:ユースケースの結果、手に入る情報とか、状態。
●中間的な関心事:ユースケースの途中で、作業に必要な参照データなど。

に分けることができる。

オブジェクト指向は、「目的」指向だから、「最終的な結果」が、もっとも重要な関心事になる。

中間の関心事も、業務アプリには、必要だけど、最終的な目的(オブジェクト)に比べれば添え物。

ユースケースに登場するすべての関心事をオブジェクトとしてフラットに列挙するのは、アンチパターン。失敗作ですね。

オブジェクト指向は、関心事、それも最終の結果、つまり目的を中心に物事を把握・整理する手法であり考え方なんだと思います。

ドメインオブジェクトのモデリングでは、いつも、「最終目的であるドメインオブジェクト」を中心に、ものごとを、理解し、整理することが大切。

業務の目的(重要な関心事)は、基本的には、

その関心事を記録する
その関心事を通知する


の二つです。

記録や通知の詳細な属性のモデリングの前に、記録したいことは何か?、通知したいことは何か?、その一番の関心事に、名前を付けること、それが、業務ドメインのオブジェクトモデリングの基本中の基本。

これは、ジャングルや暗闇の中で、迷走する作業ではありません。

ユースケースの名前に、最初からその答えがあるんだから。

注文を受け付ける
発注する
予約する
キャンセルする

「注文」「発注」「予約」「キャンセル」

これが、ドメインの主たる関心事。だから、これがそのままドメインオブジェクトになる。
後は、この主たる関心事オブジェクトが、どんな情報を持っているべきか、そこにどんな業務ルール/業務知識があるか、に注目しながら、モデル(模型)を、育てていけばよい。

簡単でしょう?

ポイントは「主たる関心事」、業務の「目的」オブジェクトを、まず、発見し、関係者で共有すること。

ここができてしまえば、業務ドメインのオブジェクトモデリングは、8割がた、成功したも同然です。

初期の段階で、業務の主たる関心事をとらえそこね、登場する言葉が、なんでもかんでも、オブジェクトに見えてきてしまったら、完全に失敗パターン。

業務の主たる関心事は、単純だし、明確なものです。

多くの技術者が、それをとらえそこねているのが、業務アプリ開発の実情かもしれません。
そんな難しいことでは、ないんだけどなあ。

「指向」から「志向」へ


私は、Oriented ( 指向 )という言葉は好きではない。
これは、無理やりにある方向に、整列させる、という、強制的な印象が強い言葉。

あるいは、教会の祭壇は「東向」が決まり、など、思考停止している印象がある言葉。

なぜそっちに向かっているのか、向かうべきか、直観的に伝わりにくい。

目的を指向すべきだ、というのは正しい。
でも、それは、言われたから、そっちを向いているだけ、という感じがする。
力づくで、そっち向きにさせられている。

私は、「指向」より「志向」のほうが良いと思っている。

「志向」とは、人間が、ある思いをもって、そちらに向かっている、という主体的、自発的なエネルギーを感じさせることばです。

目的「指向」は、営業目標のノルマを強要されているサラリーマンのイメージ。
目的「志向」は、自らの向かう道を見定めて、力強く歩いている、自律した起業家やプロフェッショナルのイメージ。

ソフトウェア開発でも、ドメインオブジェクト(業務の関心事)を「指向」しなさい、では、良いソフトウェアは生み出せないと思っている。

業務の目的に、自分も主体的に参加して、その目的の達成のために、みんなと協力しようという、「目的志向」の個人が集まること。

チームの各メンバーが、チームの「目的(最終結果)」を、共有して、自ら、そこに向かって力強く歩くこと。

それが、Object Oriented 「目的志向」なソフトウェア開発なんだと思う。

続・システムのデッサン

昨日の記事の続き。

デッサンの仲間(?)に、スケッチ、クロッキー、エスキスなどの技法(?)があるらしい。

ネットで拾い読みしたネタで、無理やり漢字をあてると

クロッキー 速描
スケッチ 粗描
デッサン 素描
エスキス 下絵

という感じらしい。

絵画の分野だと、こういう用語や技法、学習法、評価法などが、しっかりしているんだろうなあ。

ソフトウェアシステムの設計は、こういう基本の技法を語る言葉が、貧弱というか、ほとんどないなあ。

なげいていてもしょうがないので、絵画の分野から、借り物でもう少し、考えてみる。

クロッキー(速描)


数分で、さっと描いた全体像。
イメージ図。
出発点?

スケッチ(粗描)


ある程度、対象を観察したり、考えながら、要点を書きとめたモデル

デッサン(素描)


技法は素朴(モノトーンで、道具や木炭とか鉛筆)だが、
視点をきちんと定め、遠近法や、光源と陰影など、幾何学的(?)な論理性を大切にする描き方。
論理モデルですね。

エスキス(下絵)


最終システムの構図(構造、配置)や彩色などをおおまかに把握するために、描く。
そのまま発展させていくプロトタイプ(原型)とか、スケルトン(骨格)やモックアップ(実物大模型)に近いかなあ。

まずは、第一印象をクロッキー(速描)して、いろいろ観察して、スケッチ(粗描)して、論理的にデッサン(素描)して、最終システムに向けて、エスキス(下絵)を描いてみる、という感じかなあ。

基本は、デッサン力


4つの中では、「デッサン(素描)」が基礎でしょうね、きっと。

視点、遠近法、陰影法などをしっかり描く力がないと、クロッキー、スケッチ、エスキス、どれも、良い仕事ができない。
「デッサンがくるっている」のは、だめでしょう、やっぱり。

要点を強調したり、あえてデフォルメする、というようなことも、基礎のデッサン(素描)力がしっかりしていないと、うまくできないはず。

システムのデッサン力を勉強するには、昨日もかいたけど「システムアーキテクチャ構築の原理」が、役に立ちそう。

デッサン(素描)の視点は、ひとつではなく、6つ。

■機能
■情報
■並行性
■開発
■配置
■運用

まず、それぞれの視点で、きちんとデッサン(素描)できる力をつける。
最低でも、6枚のデッサン(素描)を描くことになる。

もちろん、6つの視点には、関係性が強いものがあるので、その関係性は、きちんと押さえて検証する。

遠近法にあたる部分は、観点。パースペクティブですね。(透視図法)

システムアーキテクチャ構築の原理では、主要な観点として、

◎セキュリティ
◎性能
◎可用性
◎発展性

をあげている。

4点消失法だな。

6つの視点のデッサン(素描)ごとに、この4つの観点を、消失点(集約点)として、各部分の遠近を表現する。

6つの視点×4つの観点のマトリクスを作って、24の升目に濃淡をつけて検証するのも、全体の構図をはっきりさせる手段になる。

ふむ。 システム設計の表現技法や、設計の巧拙の評価手段が、なんとなく見えてきたきがする。

しばらくは、この線をおいかけてみよう。

たいせつなことが忘れていた。

「ドメイン駆動」が、おそらく、光源と陰影法にあたる技法だと思う。

光源をどこにするかで、同じシステムでも、ずいぶんと見え方や印象がかわるはず。

ドメインからの光をあてながら、システムに陰影をつけていくのが、よい設計スタイル。

ドメインを光源にしたとき、くっきり浮き上がる部分、影になる部分、その濃淡やコントラストを、関係者で共有することが、価値のあるシステムづくりには、とっても役に立つはず。

システムのデッサン(素描)力

とりあえず描いてみる


年末に、ちょっとした設計を済ませたかったが、いろいろ立て込んで、どうにも時間が足りなくなった。

しょうがないので、「一枚5分くらいでいいから、画面のラフスケッチ、ユースケースモデル、概念モデルの三つを、ざっと描いてみて」と頼んだら、要点がはっきりとわかる、かなり良いモデルがでてきた。
(さすがに、1モデル5分ではなく、10分とか20分はかけたようだけど)

描いた本人は、粗が目立って、あまり、納得していない様子。

私からみると、彼の設計意図、こだわりポイント、切り捨てたポイントが明確で、とてもよい設計ドキュメントになっていると思った。

最近は、モデリングツール( Enterprise Architect ) を使ったモデリングが多かったけど、こういう手書きのラフスケッチの良さを、再発見。

この再発見をブログで書こうと思って、ネットで関連ワードを漁っていたら、見つけたのが「デッサン (素描)」という言葉。

ソフトウェア設計でも、「デッサン(素描)」は、基本スキルだし、とても、役に立つ技法。もっと、デッサンをやるべきだし、デッサン力を身につけることが大切。

油絵で、絵の具を使うのが、コーディングだとしたら、その前に、木炭とか、やわらかめの鉛筆で、デッサン(素描)したり、下絵を描くのが、基本。(だと思う。絵の専門家じゃないので確かじゃないけど)

デッサン(素描)で視覚的な特徴をつかむ


デッサン(素描)は、モノトーンで、輪郭線の強弱や、濃淡による陰影で、表現する技法。

細部の精密表現には向かないけど、全体の構図とか、視覚的な特徴とかを、表現するには、きわめて有効な技法というわけだ。

ソフトェアで、細部を精密に表現するには、コードを書くのが一番。APIの使い方などは、サンプルコードが、もっとも、わかりやすい表現手段だと思う。

ただ、ソフトウェアの全体の構造とか、特徴とかを、関係者で、共有するには、コードは適切な手段ではない。

1枚の視覚的な表現、つまり、デッサン(素描)が、もっとも役に立つ。

デッサン(素描)は、試作方法


ルネサンス時代には、絵画や彫刻、建築の試作方法として大いに用いられるようになったのだそうだ。( from wikipedia )

ソフトウェア開発、システム構築は、「建築」との類似性が高いと思う。

デッサン(素描)が便利な試作方法である、という点も、まさにその通り。

描かないのが、腕の見せ所


デッサン(素描)のツボは、細部を「描きこまない」ことでしょう。

ごちゃごちゃ描きこみすぎると、全体が真っ黒になって、線の強弱や、濃淡による陰影の表現が、わかりにくくなってくる。

強弱や陰影をはっきりさせるには、何も描いてない部分とのコントラスト(対比)がポイントになるはず。

ソフトウェアシステムのデッサン(素描)の勘所も、まさにそこにある。

いろいろ描きこみすぎると、全体の輪郭や、特徴が、どんどん、あいまいになってくる。

全体の輪郭と、システムの特徴を、うまく表現するには、いかに、詳細を描かないか、ということが重要になる。

A4一枚(たぶん裏紙)に、輪郭と特徴を、描きだすデッサン力が、ソフトウェアの設計の、必須の基礎スキルだと思う。

デッサン(素描)のアンチパターン


どういうデッサン(素描)が、よい設計かを、書くのは難しい。
こういう場合は、「アンチパターン」(ありがちなまずいパターン)を挙げることで、よいデッサンを表現してみる。

全体が真っ黒


描き込みすぎ。
強調すべき線や陰影が、全体の中に埋没しちゃって、全体の輪郭や特徴がわからなくなっちゃている。
設計ドキュメントで、よくみかけるアンチパターン。詳細な記述が役に立つシーンもあるが、全体の輪郭や特徴(要点)をつかむには、描き込みすぎては、ダメ。

一般化


誤った抽象化。(単純化)
たとえば、○を三つほど描いて、線でつないで、おしまいにするパターン。
MVC の図とかですね。
「デザインパターン」とか「アナリシスパターン」とかもそうですね。

論理的に正しいし、細部を切りすて、全体の構図、特徴をみごとに表現している。

ただし、こういうのは、一般モデルとして、意味があっても、個別のシステムの設計には、そのままでは、役に立たない。

開発すべきソフトウェアは、ある文脈の中で、特定の関係者にとって、(特殊な)価値があること。
そういう価値のあるソフトウェアの全体の輪郭はなんで、どこが要点かを表現するには、一般モデルではなく、もっと特殊なモデルを描かなくちゃいけない。

デザインパターンは、一般モデルとして、興味深いし、設計の参考になることは、まちがいない。

ただし、一般モデルは、個々のシステム固有の特徴を、捨象することで、成り立っている。

個々のソフトウェア設計で重要なのは、そのシステム固有の特徴であり、固有のこだわりポイントこそが大切。

似顔絵でも同じですね。

ある特定の人物の、特徴を描く出そうとする似顔絵と、一般的な「人の顔」を表現した絵とは、目的がまったく違う。

ソフトウェア開発で大切なのは、そのシステム固有の特徴(要点)を、生き生きと描き出すための、デッサン力なんです。

もちろん、一般モデルの勉強はとても大切。
一般モデルを参考にしながら、自分たちは、特殊モデルを創る、というのが、設計なんだと思う。

幼児の絵


稚拙。
コードしか書いたことがない技術者に、設計してもらうと、多いのが、このアンチパターン。

遠近感や大小の対比、位置関係が、かなり崩れている。

幼児の絵は、実は、ほんとうにそう見えているらしい。見たままを素直に描いているらしい。
子供たちには、絵は自由にのびのびと描かせるべきで、遠近感や大小関係を大人の目線・価値観で、いろいろ言うべきではない。

ただし、仕事としてソフトウェア開発をやるには、そうはいかない。
システムの設計(デッサン)が、幼児の絵では、困るわけです。

きちんとした遠近感、大小表現、位置表現ができる基礎の技量があって、はじめて、特徴を強調することができるようになる。

デッサンの基本


絵画の分野であれば、デッサンの基本とか、練習方法とかが、ある程度の確立しているのかもしれない。

ソフトウェアシステムの設計で、私がデッサン(素描)を学ぶのに、おすすすめしたいものを、ざっとあげてみる。

【道具】モデリングツール
フリーハンドでよいデッサンができるようになるには、ある程度の技量が必要。

入門編としては、モデリングツールを使って、UML 風のダイアグラムを描くのが良いと思う。

おすすめ(私たちが使って、よいと思っているもの)は、Enterprise Architect(EA)

ツールの機能もそうだけど、ヘルプとか、入門編のマニュアルとかに、UML の基礎知識を勉強するのに、よい情報がたくさん提供されている。

関係の表現には、アソシエーション(関連)、継承、依存、実現など、いろいろな種類がある。それらを意味や使い分け、そのサンプルなど、EAのヘルプやドキュメントは、情報の宝庫です。

【勉強のネタ】
システムのデッサン(素描)にも、いろいろな流派があると思いますが、私たちが、愛用しているものを紹介しておきます。

リレーションシップ駆動要件分析(RDRA)

RDRA は、要件定義という、システム全体がまだはっきりしない段階で、どうやって、システム全体像を関係者で創っていくかという、実践的な手法です。

ソフトウェアシステム設計のデッサン(素描)の技法として、一押しです。

ICONIX(ユースケース駆動開発実践ガイド)

ICONIX は、RDRA で定義した要件を、ユースケースモデルやクラスモデルから初めて、実際のコードにするまでの、アジャイルで、実践的な手法です。

デッサン力という観点でいうと、「描き込みすぎない」ことを学ぶには、とても参考になる考え方です。

予備設計の段階で描くべきこと、そうでないこと。
詳細設計の段階で描くべきこと、そうでないこと。

こういうメリハリのつけ方のヒントが満載の開発手法です。

システムアーキテクチャ構築の原理

「システムアーキテクチャ構築の原理」は、システム全体の構図を狂わせないために、システム全体像の描き方のポイントを教えてくれます。

6つのビューポイント(視点)と、4つの主要なパースペクティブ(観点)を使って、システムの全体の構造を、体系的かつ要点を押さえた描き方を勉強するには、最適な書籍だと思います。

----
というわけで(?)、今年は、システムのデッサン力をアップするぞ!

オブジェクト指向とは、モノ指向であり、目的指向である

今、採用支援システムと、国際物流システムという、異なったドメインの初期のモデリングを、掛け持ちでやっている。

採用支援システムのほうは、どちらかというと、「人」と「人」との連係をうまく支援するためのシステム。
「仕事」がそのつなぎ役のオブジェクト。

国際物流システムのほうは、さまざまな外部システムとの「システム間連係」が、重要なテーマ。
「貨物」という実体を、情報化して、関連システム間で、効果的に情報を交換するためのシステム。

2つの案件は、ドメインも、システムの性格もかなり異なるので、頭の切り替えがたいへんだけど、やってみると、モデリングや設計の考え方や基本テクニックを、整理する、良いチャンスだと感じるようになってきた。

個々の部分最適のテクニックではなく、より、一般的に使える考え方や、テクニックを習得するよいチャンス。
自分のモデリングスキルを、向上できる機会に恵まれた感じ。

オブジェクト指向


あらためて、オブジェクト指向的な発想を重視すべきだと、思うようになった。

それも、「そもそも」的な意味でのオブジェクト指向。

オブジェクトは、「的」というのが本来の意味だと思っている。(なにかを投げつける、その対象物)
実体のある「対象物」という意味を重視すると、「モノ」。
実体がなくても、目指す対象、という意味を膨らませると、(概念的な)「目的」。

システムのモデリングや設計という文脈にあてはめると、 how より what、 手段より目的、を重視することが、オブジェクト指向ということ。

ソフトウエア開発は、全体でみれば、「どうやって実現するか」という、how 指向であり、手段指向の世界だと思う。
本質的には、オブジェクト指向ではない。

だからこそ、how より what、 手段より目的を、重視する「オブジェクト指向」に価値がでてくる。

ITは、how と 手段の集合体。それを活用するためには、what や、目的から出発するのが良い結果を産む。

IT に限らず、how より what、手段より目的を重視せよ、というのは、ビジネススキルという一般的な意味でも、とっても大切なはず。

ビジネス一般でも、how 中心、手段中心ということが、多いのが実情。IT の世界では、それが、さらに強くて、弊害が、はっきりあらわれている。 できあがってみたら、目的とはかけ離れたシステム、事業に価値をもたらさないシステムが、なんと多いことか。

事業にとって価値のあるシステム開発するなら、what や 目的を「指向」する、というより、what や 目的で、how/手段を「駆動」する、という、もっと積極的な考え方がよさそう。

オブジェクト指向を進めて、オブジェクト駆動

「モノ」を定義する。
「目的」を考える。

これを駆動力にして、いろろな how / 手段 を引きだしから、引っ張り出してきて、組み立てていくのが、ソフトウェア開発のやり方として、成功パターンになりそう。

まずは宣言


how より、 what ということは、コードのレベルで考えれば、XML, SQL , アノテーションなど、「宣言」型の記述方法が中心になる。

UML などの「図」法も、宣言型に近い。 シーケンス図とか振る舞いモデルの図も、振る舞いを「宣言」する感じ。

文章や、プログラミング言語は、手続き型。

手続きスタイルの、ソフトウェア開発がなくなるわけではないけど、大きな方向としては、「手続き」型より、「宣言」型で、考えたり、記述することが増えてきている。

what 駆動、目的駆動という視点からは、 what や 目的を「宣言」できれば、開発完了、になるのが理想。

少なくとも、まずは、 what / 目的 の記述に注力する。
そこがある程度、固まってきてから、はじめて、 how / 手段 の検討に入る、ということ。

逆にいえば、 how や 手段 が不明だったり、できそうかどうか、とかは無視して、まずは、 what / 目的を明らかにしてみるべし、ということ。

what 駆動、目的駆動でやるには、 how や 手段の知識は、ノイズだったり、余計なバイアスだったり、発想の足かせになる。

技術者として、 how や手段の知識は、財産。その財産から、いったん離れて、 what 駆動、目的駆動に注力するのが、良いモデリング・設計の基本だし、また、それが奥義なんだと思う。

オートマトン


「オブジェクト」は、本来は、「自律的」という意味は、なかった。(今でも、一般的な言葉としては、「自律」という意味はないはず)。

ソフトウェアの技術体系の基本モデルのひとつに「オートマトン」がある。
プログラミングとか、情報システムの根底には、この「オートマトン」モデルがある。

オートマトンは「自律的に動く」ことが、その基本の発想(定義?)。

オブジェクト指向を「プログラミング」という文脈でとらえた時、あたりまえのように「オートマトン」「自律的に反応する」という発想になる。

オブジェクト指向プログラミング(OOP) は、自律的に反応する「what」に注目する、という「オブジェクト指向」+「オートマトン」という合成物。

「オブジェクト指向」そのものには、「自律的に動く」という意味合いは、ない。

オブジェクト指向で、分析・設計するということは、いったんは、「オートマトン」、つまりプログラミングのモデルから離れて、「モノ」の定義、「目的」の定義を、重視するのが良いモデリング・設計のコツなんだろうと、思い始めている。

オブジェクト指向の分析の結果を、現在のオブジェクト指向系といわれる言語で実装するのが良いかどうかは別の問題。

私の感覚では、 XML は、宣言型で、 what 指向なので、オブジェクト指向風の言語。
Java は、アセンブラ、Cという由緒正しい(?)、マシン操作の手続きを記述するための言語。

Java のように、汎用的な言語の場合には、書き方によって、手続き重視でもかけるし、宣言重視でもかける。

ドメインモデルを、Java で実装する場合、Bean Validation のアノテーションなどを使いながら、かなり、宣言的に書けるようにはなってきている。

アグリゲートのルート、というような構造の宣言も、 XML マッピング用のアノテーションを導入すると、記述できる。

自然な書き方かどうかは、かなり疑問だけど、使えるものを使う、という発想で、開発言語として、Java を使いつつ、ドメインモデルを宣言的( what 指向 ) に実装しようと、いろいろやってみている。

calendar
     12
3456789
10111213141516
17181920212223
24252627282930
31      
<< March 2024 >>
システム設計日記を検索
プロフィール
リンク
システム開発日記(実装編)
有限会社 システム設計
twitter @masuda220
selected entries
recent comment
recent trackback
categories
archives
others
mobile
qrcode
powered
無料ブログ作成サービス JUGEM