密結合と疎結合を分かりやすく解説

密結合と疎結合を分かりやすく解説

密結合とは?

密結合とは、クラスやウィジェット、コンポーネントが互いに強く依存している状態のことです。

密結合なコードの具体例・改善例

以下のコードはUserProfileScreenUserSettingsScreenに直接依存しているため、密結合しています。

UserProfileScreenを別の場所で使おうとすると、他の関連部分も一緒に修正する必要が出てきます。

class UserProfileScreen extends StatelessWidget {
  final User user;
  UserProfileScreen(this.user);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Name: ${user.name}'),
        Text('Email: ${user.email}'),
        ElevatedButton(
          onPressed: () {
            Navigator.of(context).push(MaterialPageRoute(
              builder: (context) => UserSettingsScreen(user),
            ));
          },
          child: Text('Settings'),
        ),
      ],
    );
  }
}

改善例として、以下のコードのように、コールバック関数を渡すことで、画面遷移の処理をUserProfileScreenから分離します。

このようにすると、遷移先や動作を後から変更しやすくなり、再利用性も向上します。

class UserProfileScreen extends StatelessWidget {
  final User user;
  final VoidCallback onPressed;
  final String buttonText;

  UserProfileScreen(this.user, this.onPressed, this buttonText);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Name: ${user.name}'),
        Text('Email: ${user.email}'),
        ElevatedButton(
          onPressed: onPressed,
          child: Text(buttonText),
        ),
      ],
    );
  }
}

密結合なコードのデメリット3つ

密結合なコードのデメリット3つを解説します。

1.変更が難しい

ある部分を変更すると、依存している他の部分も一緒に修正する必要があり、時間と工数がかかります。

2.再利用性が低い

他のプロジェクトや他の部分で再利用したくても、密結合しているため簡単に取り出して使うことができません。

3.テストが難しい

一部の機能だけをテストしたい場合でも、密結合だと他の関連部分も含めてテストしないといけません。

疎結合とは?

疎結合とは、各コンポーネントやクラスができるだけ独立しており、互いに依存関係が少ない状態のことです。

疎結合なコードのメリット3つ

疎結合なコードのメリット3つを解説します。

1.変更や拡張がしやすい

あるコンポーネントを変更しても他への影響が少ないため、保守が容易です。

2.テストが容易

個々の部分を独立してテストできるため、単体テストがしやすく、バグの検出も簡単です。

3.再利用性が高い

独立性が高いため、別の場所や別のプロジェクトでそのまま再利用しやすいです。

すべてを疎結合にすることのデメリット3つ

疎結合は理想的に見えますが、すべてを疎結合にすることで生まれるデメリットもあります。

1.複雑化

すべてを疎結合にしようとすると、コンポーネント同士の連携に工夫が必要で、コード全体が複雑になることがあります。

2.設定の難しさ

各部分が独立しているため、状態管理や依存関係の設定が増え、コードが読みにくくなる可能性があります。

3. パフォーマンスの低下

各部分が独立しすぎると、やり取りが増えてパフォーマンスが低下する場合もあります。

疎結合にしすぎた具体例

以下のように疎結合にしすぎるがゆえに、コールバックやデータの受け渡しが増えすぎてコードが複雑化しています。

また、複数のスクリーンで同じデータを管理するため、更新の際にどこを変更すべきかを把握するのが難しくなります。

今回の例においては、完全な疎結合を避けて、共通のデータをProviderRiverpodなどの状態管理パッケージを使って、一元化することで疎結合を保ちながらもデータの管理が簡単になります。

// ユーザー情報を表示し、設定ボタンが押されたときにコールバックを実行
class UserProfileScreen extends StatelessWidget {
  final User user;
  final VoidCallback onSettingsPressed;

  UserProfileScreen(this.user, this.onSettingsPressed);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Name: ${user.name}'),
        Text('Email: ${user.email}'),
        ElevatedButton(
          onPressed: onSettingsPressed,
          child: Text('Settings'),
        ),
      ],
    );
  }
}

// ユーザー設定を行いますが、UserProfileScreen からデータを引き継ぐ方法が複雑になっています
class UserSettingsScreen extends StatelessWidget {
  final Function(User) onSaveSettings;

  UserSettingsScreen(this.onSaveSettings);

  @override
  Widget build(BuildContext context) {
    // 設定を保存するためのフォームや入力フィールド
    return Column(
      children: [
        TextField(
          decoration: InputDecoration(labelText: 'Username'),
          onSubmitted: (newName) {
            // 保存ボタンでデータをコールバック経由で保存
            onSaveSettings(User(newName, 'newEmail@example.com'));
          },
        ),
        // 他の設定フィールド...
      ],
    );
  }
}

// つなぎ合わせるメインスクリーン
class MainScreen extends StatefulWidget {
  @override
  _MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  User user = User('Alice', 'alice@example.com');

  void _goToSettings() {
    Navigator.of(context).push(MaterialPageRoute(
      builder: (context) => UserSettingsScreen((updatedUser) {
        setState(() {
          user = updatedUser;
        });
      }),
    ));
  }

  @override
  Widget build(BuildContext context) {
    return UserProfileScreen(user, _goToSettings);
  }
}

class User {
  String name;
  String email;

  User(this.name, this.email);
}

まとめ

疎結合を意識することは大切ですが、必要以上に分離しすぎず、適度に密結合も取り入れるバランスが重要です。

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

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

まずは相談する

記事を書いた人

Matsuura

エンジニア

Matsuura

Twitter

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