おじゃまぷよ系エンジニアメモ

アプリエンジニアからサーバーとインフラエンジニアに転身しました

モジュール結合度について復習した

プリンシプルオブプログラミング読んでなんとなく思い立ってモジュール結合度について考え直した
昔、基本情報技術者試験受けたときに勉強したときはただ暗記しただけだったけど改めて結合度について学んでみると「なるほどな…」って思うことが多かったので実際にクラスとか書いてまとめてみる

モジュール結合度とは

モジュール同士の関係の密接さを表す尺度……
と書いてあるがちょっと意味が難しい。クラス同士の影響度と言ったほうがわかりやすい。クラスの処理や値を変更すると他のクラスに影響を及ぼすものが結合度が高いということだろう。 モジュール結合度には以下の6種類があり、レベルが高いほど結合度が低い。

  • レベル6データ結合
  • レベル5 スタンプ結合
  • レベル4 制御結合
  • レベル3 外部結合
  • レベル2 共通結合
  • レベル1 内容結合

レベル6データ結合

スカラー型のデータを引数として渡す結合。

<?php
class ModuleA {
    public function add($a,$b){
        return $a + $b;
    }
}

class ModuleB{
    public function doService(){
        $moduleA= new ModuleA();
        $result = $moduleA->add(1,2); //データ結合
        // doSomething
    }
}

ModuleAもModuleBもお互いに内部の実装に干渉しない(ブラックボックス化されている)ので、安心してプログラムの中を変更できる。
結合度が低いと言える。

ModuleAはModuleBとデータ結合してる状態。

レベル5 スタンプ結合

共通域にないデータ構造を受け渡しするような結合形態。
スカラー型ではなく、オブジェクトを引数として渡すと解釈して良さそう。

<?php
class User{

    private $id;
    private $name;

    public function __construct($id, $name) {
        $this->id = $id;
        $this->name = $name;
    }

    public function getId() {
        return $this->id;
    }

    public function setId($id) {
        $this->id = $id;
    }

    public function getName() {
        return $this->name;
    }

    public function setName($name) {
        $this->name = $name;
    }


}

class ModuleA {
    /**
     * @param $user User
     * @return string
     */
    public function doService($user){
        return " name : ".$user->getName();
    }
}

class ModuleB{
    public function doService(){
        $user = new User(1,"masahide");
        $moduleA = new ModuleA();
        $result = $moduleA->doService($user);
        // doSomething
    }
}

ModuleAもModuleBもお互いに内部の実装に干渉しない。
スタンプ結合の場合受け渡すデータの一部を使用しないことがある。
Userのnameは使うけどidは使わないというような状態。

レベル4 制御結合

呼び出し側のモジュールが、呼び出し先のモジュールの制御を指示するデータをパラメーターとして渡す方式。
よくある引数にflgとかtypeとかstatusを渡して呼び出し先の処理を制御するパターンと解釈してよさそう。

<?php
class ModuleA {
    public function doHoge($type){
        if($type === 1){
            //doSomething
        }elseif ($type === 2){
            //doSomething
        }elseif ($type === 3){
            //doSomething
        }else{
            //doSomething
        }
    }
}

class ModuleB{
    public function doService(){
        $moduleA = new ModuleA();
        $moduleA->doHoge(2);
        // doSomething
    }
}

ModuleBがModuleAを使用するときに、typeを渡すことによって内部の処理を制御してる。
データ結合や、スタンプ結合と違いモジュールの使用側が使用するモジュールの内部の実装を知っている必要があるので結合度が高くなると解釈した。

レベル3 外部結合

外部宣言したデータを共有したモジュール間の結合形式。
書籍や記事によってはグローバル変数のことであったり、publicな変数であったりと少し表現が違っている。
基本情報技術者試験に則って、グローバル変数の値を参照するパターンを外部結合としてみる。

<?php

$GLOBALS['hoge'] = "hoge";

class ModuleA {
    public function doService(){
        echo "moduleA : ".$GLOBALS['hoge'];
    }
}

class ModuleB{
    public function doService(){
        echo "moduleB : ".$GLOBALS['hoge'];
    }
}

こんな感じかな… グローバル変数なのでどこで書き換えれるかわからず、書き換えられるとModuleA,Bともに影響を受けるので結合度は高い。

レベル2 共通結合

これもグローバル変数をモジュール間で共有するパターンだが、外部結合との違いは、データ結合とスタンプ結合の違いと同じように
データ構造を共有して不必要な値までも共有しているということ

<?php

$GLOBALS['user'] = new User(1,"masahide");

class User{
    private $id;
    private $name;

    public function __construct($id, $name) {
        $this->id = $id;
        $this->name = $name;
    }

    public function getId() {
        return $this->id;
    }
    public function setId($id) {
        $this->id = $id;
    }
    public function getName() {
        return $this->name;
    }
    public function setName($name) {
        $this->name = $name;
    }
}

class ModuleA {
    public function doService(){
        echo "moduleA : ".$GLOBALS['user']->getName();
    }
}

class ModuleB{
    public function doService(){
        echo "moduleB : ".$GLOBALS['user']->getName();
    }
}

これもグローバル変数を共有しているのでどこで書き換えれるかわからず、さらに本来不必要なデータまで共有している分外部結合よりさらに高くなる

レベル1 内容結合

モジュール同士の命令の一部を共有したり、外部宣言していないデータを直接参照したりしている状態
カプセル化できてないクラスや、リフレクション使って無理やり命令の一部を使っている状態と言ってよさそう