読者です 読者をやめる 読者になる 読者になる

「UMLモデリングの本質」を読んだ

書評

UMLモデリングの本質 第2版

UMLモデリングの本質 第2版

UMLモデリングの本質」という本を読んだ。最近、ソフトウェアの設計について興味があって、いろいろ調べてみたところ各所でこの本がオススメされていたので手にとってみた。以前のエントリー(「達人に学ぶDB設計徹底指南書」を読んだ - naoty.to_s)でデータベースの設計について理解できたんだけど、結局のところ、そのシステムが扱う業務内容をいかにして実装可能なモデルに落とし込むか(=モデリング)が重要になってくると思う。この本で理解したかったのはそこだった。タイトルからUMLの書き方についての本のようにも思えるが、そうではなく、むしろまったくUMLの書き方は書いてない。UMLを使って、複雑な業務用件をいかにモデリングするかを説いている。本書は300ページに満たないものの、密度が非常にあり2, 3週間でゆっくり読んでも第3章までしか読めていない。それでも、十分に学びがあったので忘れてしまう前にエントリーとして残しておきたいと思う。

モデリングとは

モデリングとは「仕組みや概念を理解するために概念的な要素とそれらの関係を記述すること」とあった。これは普段の業務で多かれ少なかれ必ず行っていると思う。ただ、それをモデリングという工程として認識してはいなかった。そして、モデリングの手法についても特に考えることはなかった。いつも漫然とノートに四角形と線を書いて整理してた(以下、イメージ)。

f:id:naoty_k:20150228013509j:plain:h400

こういう自己流ではなくて、標準的な概念の表記法がある。その1つがUMLだ。UMLを習得することで、他人とのコミュニケーションの手段として使えるようになるし、UMLを通して標準的なモデリングの手法も学べるようになる。

本書によると、モデリングは大まかに以下の順番に行っていくようだ。

  1. 業務フロー図を書いて業務フローを整理する。
  2. 業務フローからユースケース図を書いてユースケースを洗い出す。
  3. ユースケースから概念となる名詞を抜き出し、初期の型図を作成する。型図というのは実際はクラス図のことで、クラスと言っちゃうとクラスとして実装されることを含意してしまうが、実際にはクラスとは限らないため型図という言い方をしている。
  4. ユースケースごとにシーケンス図を書く。これによって各アクターの責務が明確になり、初期の型図を機能的側面から修正することができる。

UMLはけっこうな種類があるような気がするけど、とりあえず上に出てきた図だけ覚えておけばよさそうだ。各ステップの具体的な方法については本書を参照してほしい。読んだだけではあまり意味がないと思うので、近いうちに簡単な例で実践してみたいと思っている。

分類の実装

モデリングの手法の次はより実装に近い話が続く。その中でも分類の実装の話がよかった。例えば、ユーザーに「有料会員」と「無料会員」という分類がある場合に、それをフラグとして実装するのか「Stateクラス」として実装するのかという議論がある。

# フラグで実装

class User < ActiveRecord::Base
  enum plan: %i(free premium)
end
# Stateクラスで実装

class User < ActiveRecord::Base
  has_one :plan
end

class Plan < ActiveRecord::Base
end

class Free < Plan
end

class Premium < Plan
end

個人的にはStateクラスという手法を知らなかったので、いつも前者のフラグとして実装していた。この実装の問題点は、状態が増えた場合にコードを修正する必要がある点と、if文の分岐を多用することになりコードが複雑になってしまう点がある。特に後者は、状態フラグの種類(例えば、「公開アカウント」or「非公開アカウント」)が増えたときに組み合わせが倍になり指数関数的に複雑になってしまうため、重大な問題点だと思う。

Stateクラスであれば、サブクラスを追加するだけでよく既存のコードを修正する必要がない。if文による分岐もダックタイピングによって解決する。状態フラグが複数になった場合は、直交する状態をサブクラスとして定義する(PublicFree, PrivatePremiumなど)のがよいと本書では書かれていた。状態によって振る舞いが異なる場合や状態が増える可能性がある場合は、フラグではなくStateクラスで実装する方がいいのかもしれない。

ファサード

モデリングによって整理された概念をクラスとして実装する際、層別化アーキテクチャを使うのがいいという話が出てくる。よく知られた4層モデルの話で、システムを「ユーザーインターフェイス層」「アプリケーション層」「ドメイン層」「永続層」に分離し、層間の参照を一方向にすることで結合度を抑えるというアプローチだ。このとき、モデリングによって整理された概念はそのままドメイン層に配置することになる。そして、アプリケーション層には業務フローで定義された手続きや処理の手順を定義していくことになる。

アプリケーション層を実装していくなかで、ドメイン層に定義された概念が高度に抽象化されていて扱いづらいときがある。とは言え、これまでのモデリングで練り上げてきた概念をアプリケーション層の都合でねじまげるわけにはいかないだろう。そこで登場するのがファサードというオブジェクトだ。ファサードはアプリケーション層からは冗長に見えるドメインを1つにまとめて扱いやすくする。さらに、複数ドメインにまたがった排他制御も扱う。

ファサードという概念はなんとなく耳にしたことがあったが、モデリング→実装という流れの文脈で捉えるとその必要性を実感することができた。普段RailsMVCだけを書いていると、こうした視点が持てずにドメインをねじ曲げてしまうことがある。なので、ファサードの実装方法について調べて実践していきたいと思った。