まくまくいろいろノート
アプリ内の名前空間(Java のパッケージ階層)に迷った時のヒント
2015-12-15

あいまいなパッケージ分割

みなさんは、アプリケーション内の名前空間をどういったポリシーで分けていますか? (ここでは Java プログラムのパッケージ分割について考えることにしましょう) パッケージの分割方法、命名方法は、ベテランのプログラマでも悩むところです。

例えば、MyApp (com.example.myapp) というアプリケーションのルートパッケージが、下記のように分割されていたとします。

com.example.myapp
  + optionmenu
  + view
  + ...

このパッケージ分割方法には少々問題があるということにすぐに気付いた方は、普段から保守性を意識したコーディングを行えている人だと思います。 上記のパッケージ名からは、次のようなことを想像できます。

  • オプションメニューという機能を構成するクラスは optionmenu パッケージに格納すればよさそうだ。
  • UI 系のコンポーネントは view パッケージに格納するのだろう。でも、オプションメニューを構成する UI コンポーネントは optionmenu に入れるの?それとも view に入れるの?

ようするに、パッケージ分割のポリシーがあいまいだということです。 optionmenu というパッケージは、「アプリ内の部分的な機能」という側面 (aspect) に注目して分割しており、一方で、view というパッケージは、「クラスが提供する機能の種類」という側面で分割してしまっています。

そもそも、物事は複数の側面(アスペクト)から捉えられるのに、名前空間は階層構造という概念でしか分割できないというところに制約があります。 ブログなどのシステムでは、タグという機能によって、記事に対して横断的なラベルを付けらるのですが、プログラミングの名前空間の世界には、このような機能は今のところありません。 どのような側面によりパッケージ分割するかのポリシーを決めておかないと、先の例のようなあいまいなパッケージ分割が行われることになってしまいます。 重要なのは、新しいクラスを作成するときに、どのパッケージに格納すべきかが直感的に分かるようなパッケージ分割のポリシーを決めることです。

パッケージ分割のヒント

ここでオススメするのは、「アプリ内の部分的な機能」を基準にパッケージ分割していくという方法です。 例えば、アプリケーションのルートパッケージを下記のように分割します。

com.example.myapp
  + feature(あるいは function など)
  + common
  + main(あるいは app など)
  + ...
  • feature パッケージには、アプリケーションを構成する部分的な機能(フィーチャ)を提供するためのパッケージ、クラスを格納する(例: feature/opetionmenu)。
  • common パッケージには、上記の複数のフィーチャから共通で使用するユーティリティクラスなどを格納する。
  • main パッケージには、アプリケーションを動作させるのに必須なクラスを格納する(起動シーケンスに関わる部分など)。モジュール化を意識するのであれば、main パッケージはできるだけ小さく維持すること。

このように分割しておけば、例えば、オプションメニュー機能の UI を構成するクラスは feature/optionmenu パッケージ以下に格納すればいいんだな、とか、一方で共通で使用する UI モジュールは common/view パッケージ以下に格納すればいいんだな、と一発で分かるようになります。 この分割方法は、複数メンバから構成されるチームで 1 つのアプリケーションを作っていくときに威力を発揮します。 ある機能を担当するメンバは、feature 以下の機能用パッケージでの修正に集中し、必要に応じて common パッケージにある共通クラスを利用する、というように作業範囲を明確に絞り込めるようになります。

1 つのシステムは、複数のアプリケーションから構成されています。 これは、1 つのシステムがアプリケーションという「機能」単位に分割されていることにほかなりません。 この考え方を、アプリケーションの中の構造にも適用するということです。

例外は、ライブラリです。ライブラリ内のパッケージ構成は、「クラスの提供する機能の種類」で分割した方が利用者にとってわかりやすい構成となるでしょう。例えば、UI コンポーネントであれば、uiview というパッケージにまとめて入れる、という具合です。

2015-12-15