DRY原則と密結合について初心者向けに解説

DRY原則と密結合について初心者向けに解説

はじめに

今回の記事を読むにあたり、以下の記事が前提となってきますので、まだ読まれていない方はこちらお読みください。

「DRY原則」と「密結合」は、どちらもコードの品質に大きく関係する概念ですが、誤って適用するとかえってコードが複雑になり、メンテナンスが難しくなることがあります。

この記事では、以下のポイントについて、Flutterの具体例を交えて解説していきます。

  1. 誤ったDRYが密結合に繋がる理由
  2. DRYだが密結合なコードの具体例
  3. DRYだが密結合・低凝集なコードを疎結合・高凝集なコードにリファクタする方法

それではさっそく見ていきましょう。

誤ったDRYが密結合に繋がる理由

一見正しいDRYの適用でも、無理にまとめすぎると以下のような問題が発生します。

1.コードの密結合

まとめたコードに複数の箇所が強く依存してしまい、一部を変更するだけで他の部分に影響が及んでしまう。

2.低凝集

1つのクラスやモジュールが複数の責任を持ち、特定の目的に集中していない状態。

本来異なる目的の処理が一箇所に詰め込まれることで、コードが読みにくくなり、再利用もしにくくなる。

DRYだが密結合なコードの具体例

以下のコードは、Flutterでボタンを作成する際にDRYを意識したコードの例です。

import 'package:flutter/material.dart';

class SharedButton extends StatelessWidget {
  final String buttonText;
  final Function onPressed;
  final bool isPrimary; // ボタンの種類を切り替えるフラグ

  SharedButton({
    required this.buttonText,
    required this.onPressed,
    this.isPrimary = true,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => onPressed(),
      style: ElevatedButton.styleFrom(
        backgroundColor: isPrimary ? Colors.blue : Colors.grey,
        padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
      ),
      child: Text(buttonText),
    );
  }
}

一見、何の問題もないように見えますが、上記は密結合・低凝集になってしまっています。

密結合

このボタンは、スタイルや機能をまとめていますが、isPrimaryフラグの切り替えに全て依存しており、他の画面や用途に合わせたカスタマイズが難しいです。

例えば、特定の画面でボタンに別の色を追加したい場合などに柔軟に対応できません。

低凝集

ボタンのスタイルとロジックが1つのクラスに詰め込まれており、中途半端になってしまっています。

DRYだが密結合なコードを疎結合・高凝集にリファクタする

密結合なコードをリファクタリングするには、単一責任の原則(Single Responsibility Principle)を意識して、それぞれの要素が1つの目的に集中するように設計することが望ましいです。

リファクタリング後のコード例

import 'package:flutter/material.dart';

// ボタンのスタイルを担当
class ButtonStyles {
  static final ButtonStyle primary = ElevatedButton.styleFrom(
    backgroundColor: Colors.blue,
    padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
  );

  static final ButtonStyle secondary = ElevatedButton.styleFrom(
    backgroundColor: Colors.grey,
    padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
  );
}

class CustomButton extends StatelessWidget {
  final String text;
  final VoidCallback onPressed;
  final ButtonStyle style;

  CustomButton({
    required this.text,
    required this.onPressed,
    required this.style,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onPressed,
      style: style,
      child: Text(text),
    );
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('リファクタ後')),
      body: Column(
        children: [
          CustomButton(
            text: 'Primary Button',
            onPressed: () {
              print('Primary Button Clicked');
            },
            style: ButtonStyles.primary,
          ),
          CustomButton(
            text: 'Secondary Button',
            onPressed: () {
              print('Secondary Button Clicked');
            },
            style: ButtonStyles.secondary,
          ),
        ],
      ),
    );
  }
}

上記のように改善することで、疎結合・高凝集にリファクタすることができました。

疎結合

スタイル(ButtonStyles)とウィジェット(CustomButton)が分離されており、それぞれ独立して変更可能になった。

高凝集

プログラムの1つのクラスやモジュールが、単一の責任や目的に集中している状態で、読みやすく再利用しやすい設計になっている。

まとめ

DRY原則を守るときも、「疎結合・高凝集」を意識して設計することが重要だとわかりました。

リファクタリングのポイントとして、責務を分離し、各部分が独立して動作するよう設計することで、柔軟性と保守性を高められます。

こうした設計の工夫がコードの質を大きく向上させるので、ぜひ実務でも活用していきたいです。

Anycloudではプロダクト開発の支援を行っています

プロダクト開発をお考えの方はぜひAnycloudにご相談ください。

まずは相談する

記事を書いた人

Matsuura

エンジニア

Matsuura

Twitter

Anycloudでエンジニアしてます!主にFlutterやWebフロントをやっていて、最近はバックエンドやインフラに挑戦・苦戦中。