ホラクラシーと制御の反転(Inversion of Control/IoC)の共通点を考察する

(上のアイキャッチ画像の無断転載・流用を禁止します。)


こんにちは。GMOグローバルサイン・ホールディングスCTO室の神沼(@t_kanuma)です。

本稿は、最近話題の組織構造である”ホラクラシー”とソフトウェアの設計思想の1つである”制御の反転(IoC/Inversion of Control)”の共通点について考察をした記事です。

メインとサブルーチン / ヒエラルキー型組織

制御の反転を説明するために、まず制御の反転ではないプログラムを考えてみます。例としてシンプルなシェルスクリプトのプログラムを考えると、そこにはいわばメイン、中央となる関数が存在すると思います。処理は基本的にその関数の上から下に流れていきます。メイン関数(メインルーチン)が大きくなり見通しが悪くなる場合や再利用する処理があれば、その部分をサブルーチンに切り出すことが考えられます。

ここにヒエラルキー、上下関係がメインルーチンとサブルーチンの間に生まれます。これは多くの企業の組織構造と同じです。メインルーチン(管理職/上司)がサブルーチン(一般職/部下)に処理(仕事)を割り振ります。サブルーチン(部下)はメインルーチン(上司)からの入力パラメータを基に決められた処理を行い結果を返します。

ここでポイントとなるのは、サブルーチン(部下)の処理(仕事)の結果から、その次にやることを”判断”するのはメインルーチン(上司)であるという点です。上司の視点で砕けたイメージをすれば、”部下Aさんにあの仕事をやってもらった結果がこうだったから、部下Bさんにこういう仕事をやってもらおう”ということです。

この構造はタスクに変化がない場合は優れたものだと考えます。逆にいうと変化には弱く、保守性に優れないという弱点を持つと考えます。ポイントは3つです。

  1. メインルーチンの規模が大きくなる。
    そのプログラム全体でやることが多くなれば、中央であるメインルーチンの規模は大きくなり、可読性が下ります。またメインルーチンは判断処理だけでなく細かい整形処理をすることもある、つまりプレイヤーとしても動作することもあります。会社で例えるならば管理職に負荷が集中する、やることが多すぎて疲弊したり1つ1つの精度が落ちてしまうことに繋がります。

  2. ルーチン間が密結合になる。
    サブルーチンは元々メインルーチンに含まれていたとすると、メインルーチンはサブルーチンが抽象的に何をするのか?だけでなく”具体的にどう処理するのか?”までを把握しています。つまり処理の詳細までを把握しているということです。例えばサブルーチンの戻り値のパターンをあらかじめ知っているため、そこに依存したコードにしてしまうなどです。またメインルーチンを介してサブルーチン間にも依存性が発生してしまうこともあります。内部の詳細に依存すると変更の局所性を保てなくなり保守性が悪くなります。あっちを変更するとこっちにも影響が及ぶことになるのです。会社に例えれば、部下Aさんの仕事を調整したらそれが部下Bさんに波及してそれがさらに他の部下に波及するイメージです。

  3. ルーチン間のオーバーヘッド
    リモート通信でない限りプログラムにおいてこれは心配無用な問題ですが、人間間ではそうではありません。細かい情報を報連相する手間、そこで発生する情報の欠落や誤解などは積もり積もって問題になります。

関心を分離する。

では、この変化に弱い課題を解決するにはどうすれば良いでしょうか?”サブルーチン(部下)の処理(仕事)の結果から、その次にやることを判断するのはメインルーチン(上司)であるということがポイント”と前述しました。この次に何をするのか?の”判断”の権限をサブルーチンに委譲します。以下のイメージです。上から下に流れていたものを、左から右にどんどん押し込んでいく形です。

こうすると何が起きるでしょうか?まずメイン、サブというヒエラルキー、上下関係が解消されます。また個々のプログラムはマイクロなデータのやりとりではなく、粗いデータをやりとりする形になります。これにより、各プログラムは単一の責務を持つロールに集中する形に分割されます。関心を分離して物事をシンプルにするわけです。これにより変化に素早く対応できるわけです。(つまり、単一責務の原則、KISSの原則、関心の分離、分割して統治するを満たす方向に進むわけです。)

ここで以下の伝統的なWebアプリの3層アーキテクチャを例に出します。

プレゼンテーション層 <-> ビジネスロジック層 <-> データアクセス層

プレゼンテーション層のプログラムで全てのことを行うのは論理的には可能です。しかしこれまで述べてきた通り現実的ではありません。3層アーキテクチャではプレゼンテーション層からビジネスに関する部分をビジネスロジック層に押し出します。そしてビジネスロジック層からデータアクセス(DBやAPI)に関する部分をデータアクセス層に押し出します。こうすることで、プレゼンテーション層はクライアントからのHTTPリクエストの内容に応じてどのビジネスロジック層プログラムを呼び出すか、その応答に応じてどの画面に遷移させるか、またはHTTPセッション周りの処理を行うなど自身のロールに集中することができるのです。

ここまででメイン/サブルーチンの構造と比較してプログラム間の結合度を下げることができましたが、まだ十分ではありません。呼び出す側は呼び出される側の実装を見て呼び出すことを選択していることに変わりはなく、その詳細に依存することはまだまだ可能です。

これはオブジェクト指向プログラミングの真髄であるインターフェイスの包含(Composition)と委譲(Delegation)解決することができます。

インターフェイスの包含と委譲

オブジェクト指向プログラミングには、親クラスの継承とインタフェースの包含/委譲という、クラス(プログラム単位となる概念)のモジュール度を上げる、別の言い方をすればクラス間の結合度を下げて部品として再利用可能にする、アプリの拡張性・保守性を上げるための大きな武器が2つあります。(一般的に継承よりも委譲のほうが結合度を下げることができ優れています。)またクラス設計のベストプラクティスをパターン化した有名なGoFのデザインパターン23種も、この2つの武器を駆使したものになっています。

インターフェイスはいわばプログラムのガワだけに相当する物で中身の実装を持ちません。事前条件と事後条件、抽象的な振る舞い、何をやるのかが定義されており、具体的にどうやるのか?はそれを実装するクラスに委ねられます。そのポリモフィズムを使ったプログラムの再利用について、以下に具体的に例示します。

クラスA1 -> クラスB -> インターフェイスC

クラスA1はクラスBを呼び出します。またクラスBはインターフェイスCを包含(保持)して処理を委譲しています。インターフェイスCにはそれが外部に示す条件を満たす実装クラスが10個あるとします。例えば、ある実装クラスはDBにアクセスしたり、あるものは外部APIを呼んだりと、その詳細は異なるわけです。

クラスA1はクラスBを生成する役割を持つわけで、いわばBの創造主です。A1はBを実体化する際にCの実装クラスを選択してBに与えます。Bとしては、インターフェイスCを満たす実装クラスの実体を受け取るという事実しかわからず、具体的にどの実装を受け取ったかはわかりません。このように外側から実装を注入されると、その主体は詳細に依存することがなくなります。そして、このことがCの実装クラスを交換可能にします。例えばA1がCの他の実装クラスを使いたい場合は、A1のコードだけ修正すればよく、Bを修正する必要はありません。またBの新しい創造主A2、A3・・・を想定すると、Bを修正することなく、各々がCの実装クラスを選択できるわけです。

制御の反転 / 依存性の注入(Dependency Injection)

”外側から実装を注入されると、その主体は詳細に依存することがなくなります。”と前述しました。呼び出し元(主体)が依存性を決める、つまり制御をするのではなく、その制御を主体の認知しない外側に移してしまう。これこそが制御の反転という設計思想の1つの具現化です。そしてこれを依存性の注入といいます。

依存性の注入とは、制御の反転を実現する1つのデザインパターンで、インターフェイスを通した制御の反転を実現するアプリにて採用されます。具体的にはアプリの構成設定ファイルに実装の選択、つまりどのプログラム呼び出しルート上に存在する、どのインターフェイスに、どの実装を使うかを予め定義しておき、アプリ起動時などのタイミングでそのファイルを読み込むことで実装の実体を注入する形をとります。端的に言えば、アプリ起動時にオブジェクトグラフを作りあげるということです。その注入をする主体はIoCコンテナと呼ばれフレームワークが担うのが一般的です。実装の切り替えに際して開発者は設定ファイルを変更するだけでコードの変更は必要ありません。いわばその系(システム)の外側にいる創造主が、内側に直接介入することなく、その系を作り替えるわけです。

ホラクラシー

ここまでの中央が制御するプログラム構造から制御の反転への転換の話は、まさにヒエラルキー型組織からホラクラシー型組織への転換にマッピングできると考えます。ホラクラシーは上下関係、ヒエラルキーがない組織と言われます。個人的にホラクラシーの本質はロール中心主義にあると考えます。

ホラクラシーにおける組織の構成単位はサークルと言われます。各サークルにはPDAという概念が紐づきます。PDAとはPurpose(目的)、Domain(領域)、Accountability(責任)の頭文字を取った用語です。また各サークルには、サークルリードという文字通りそのサークルに紐づくPDAを果たすべくリードをとる人物が1人います。

あるサークルが誕生して、そこに属する人物がサークルリード1人であった場合を想定します。サークルリードはそのサークルのPDAを果たすために、そのままでは自身一人で担わなければならないと思う役割群をロールという概念に切り出すことができます。そしてロールごとにPDAを定義して、他者にロールアサインの要請を出すことができます。

このPDAが前述したインターフェイスに該当すると考えます。PDAは抽象的であり、具体的にそれを果たすべき道のり、つまりどう実装するかまでは書かれていません。実装の権限はそのロールにアサインされた人物に委譲されています。サークルリードは実装の詳細に関与せず、インターフェイスを設計することが重要な仕事になるわけです。そしてアサインされる側は、そのインターフェイスの契約条件に合意すると、つまりアサインを承諾するとロールリードとなり、(ここが最も重要なポイントですが)PDAを果たすべく状況に応じて柔軟に実装を変更することが可能になるわけです。

またロールリードは、例えば自身の役割が複合的な役割から構成されると考えた場合、自身がサークルリードとなりさらに細かいロールを定義する形で、そのロールをサークルへと変化させることができます。

このようにインターフェイス(ロールのPDA)を通した制御の反転を推し進めることにより、変化に強い自律型の組織ができて上がっていくものだと考えます。

後書き

物理学にはエントロピー増大の法則というものがあります。これは端的に言えば”物事は放っておくと、それ以上どうしようもなくなるまで乱雑、無秩序になっていく傾向にある、乱雑さ(エントロピー)は増大する傾向にある。”というものです。身近で例えるなら、家を放っておけばどんどん汚くなるそれです。

ここでのポイントは、生命体がエントロピーを抑制できる、つまり物事を制御し形作れる、言い換えれば物事をデザインできる存在である点です。そして生命体の中でも人間は最もそれを得意とすると言えます。この事からエントロピーを抑制する、つまり物事をデザインするというのは人間の本能であり、全ての人間はデザイナーであると言えると思います。

どうエントロピーを抑制するか、つまりデザインの方法というのは千差万別、十人十色、無限大にとり得るものだと思います。こと仕事において、これまでのヒエラルキー組織では上司がその方法を決めていました、言い換えれば仕事をデザインしていました。ホラクラシーでは(Domainの範囲の中でPurposeを達成してAccountabilityを果たすように)ロールにアサインされた個々がそれを行います。集団の中で自己のオリジナリティを押し出し、活き活きと働くことができるホラクラシーはとても人間的であると考えます。