ウェブ制作

バックエンド

凝集度を考える

お刺身@プログラマー

お刺身@プログラマー

#Oookey_blog_018

凝集度を考える

こんにちは。
みなさんモジュールは作っていますか?
モジュールを積極的に作成することは素晴らしいことですが、何も考えずにぽんぽん作っていくと読みづらく使いにくいものになりがちです。
その対策としてある一つの考え方、凝集度についてLaravelを元にざっくりと説明しようと思います。

目次

凝集度とは?
凝集度とは?
凝集度の7段階
1.偶発的凝集
2.論理的凝集
3.時間的凝集
4.手続き的凝集
5.通信的凝集
6.逐次的凝集
7.機能的凝集
まとめ

凝集度とは?

凝集度(Cohesion)とは、クラスやメソッドといったモジュールが「どれくらい一つの目的に集中しているか」という設計の良し悪しを表す指標です。

高凝集 : 「一つのことに集中している」
低凝集 : 「いろんなことが混ざっている」

凝集度の7段階

凝集度は「悪い → 良い」順に7段階に分けられます。

レベルLv 名前 状態
1 偶発的凝集 完全に無関係な処理の集まり
2 論理的凝集 条件分岐で別処理が混在
3 時間的凝集 同時タイミングで実行される処理が集結
4 手続き的凝集 手順通りの処理をまとめただけ
5 通信的凝集 同じデータを扱う処理が集結
6 逐次的凝集 機能領域でまとまっている
7 機能的凝集
(理想)
明確な1つの目的だけに集中

1. 偶発的凝集

Copied!

copyBtn
class UtilityController extends Controller
{
    public function handle()
    {
        Log::info('ログ記録');
        Storage::delete('tmp.txt');
        echo date('Y-m-d');
        $user = User::find(1);
    }
}

説明:処理内容がバラバラ。意味のあるまとまりがない。
対処法:役割ごとのクラスやServiceに分けよう。

2. 論理的凝集

Copied!

copyBtn
class NotificationController extends Controller
{
    public function notify(Request $request)
    {
        if ($request->type === 'email') {
            Mail::to($request->email)->send(new NotificationMail());
        } elseif ($request->type === 'slack') {
            // Slack通知処理
        } elseif ($request->type === 'sms') {
            // SMS通知処理
        }
    }
}

説明:同じ「通知」とは言えど、内部でまったく違うことをしている。
対処法:通知の種類ごとにServiceクラスやStrategyパターンで分けよう。

3. 時間的凝集

Copied!

copyBtn
class BootService
{
    public function initialize()
    {
        $this->loadEnv();
        $this->connectDB();
        $this->cacheWarmup();
    }
}

説明:「起動時に実行する」というだけでまとめられている。
対処法:処理内容ごとにクラスを分けて明確にしよう。

4. 手続き的凝集

Copied!

copyBtn
class ReportController extends Controller
{
    public function generate()
    {
        $users = User::all();
        $csv = $this->makeCsv($users);
        Storage::put('report.csv', $csv);
    }
}

説明:処理の流れは見えるが、責務の境界が曖昧。
対処法:CsvExporterなどのユーティリティクラスで責務分離をしよう。

5. 通信的凝集

Copied!

copyBtn
class UserService
{
    public function updateEmail(User $user, $email)
    {
        $user->email = $email;
        $user->save();
    }

    public function deactivate(User $user)
    {
        $user->active = false;
        $user->save();
    }
}

説明:同じデータ(User)に関する操作が一つのクラスに集まっている。
対処法:メソッドが増えすぎたら、機能別に分割を検討しよう。

6. 逐次的凝集

Copied!

copyBtn
class AuthService
{
    public function login($credentials)
    {
        // 認証処理
    }


    public function logout()
    {
        // ログアウト処理
    }


    public function register($data)
    {
        // ユーザー登録処理
    }
}

説明:「認証」という同じ領域に関する処理が集まっている。
対処法:この状態ならまだ健全。複雑になれば分割を検討しよう。

7. 機能的凝集

Copied!

copyBtn
class PasswordResetService
{
    public function reset(User $user, string $newPassword)
    {
        $user->password = Hash::make($newPassword);
        $user->save();

        event(new PasswordReset($user));
    }
}

理想の状態

まとめ

凝集度は、「このクラス(関数)は、何をするべきか?」という設計の本質とも言えるものです。
「ただ動く」から「長く保てる」コードへ。
Laravelを通して、凝集度の高い美しい設計を目指していきましょう!

書籍プリンシプル オブ プログラミング