EeBlog(テクニカルブログ)

第20回 クラスの依存関係

担当が代わりましたJavaワンポイントですが、今後も続きます。

今回のテーマは「クラスの依存関係」です。

まず「クラスの依存関係とは何ぞや」というところから始めましょう。
Javaプログラミングにおいて、クラスは欠かすことのできない要素です。
通常の場合、クラスとクラスを組み合わせてプログラミングします。
そのクラス間の結び付きのことをクラスの依存関係と言うのです。

では、簡単なプログラムを使って説明していきます。

public class Player {
     public void play() {
         System.out.println("ウォームアップします。");

         Badminton badminton = new Badminton();
         badminton.play();

         System.out.println("クールダウンします。");
     }
 }
public class Badminton {
     public void play(){
         System.out.println("バドミントンして遊びます。");
     }
 }

上記のPlayerクラスでは、Badmintonクラスをnew演算子によって直接生成しています。
クラス名がハードコードされているため、クラスの依存関係が非常に強いと言えます。
クラスの依存関係が強い場合、両方のクラスの実装を並行して行うことが難しくなります。
上記の場合だと、Badmintonクラスの実装が終わらないと、Playerクラスはコンパイルができません。
また、例えばVolleyballクラスを作って、Badmintonクラスと入れ替えたいという場合に、Playerクラスを書き換える必要があります。
という具合に、このようなクラスの依存関係が強い状態はあまり好ましいとは言えません。
では、どうすれば依存関係を弱められるのでしょうか。

今回はインターフェースとファクトリを使用して依存関係を弱める手法を紹介します。
まずSportsというインターフェースを用意します。

public interface Sports {
     void play();
 }

次にSportsインターフェースの具象クラスを作成します。
モックオブジェクトを最初に作成しておくと、他クラスからSportsインターフェースの呼び出しが可能になります。

public class MockSports implements Sports {
     @Override
     public void play() {
         System.out.println("開発途中です。");
     }
 }

Badmintonクラスに加えて、Volleyballクラスも作成します。

public class Badminton implements Sports {
     @Override
     public void play() {
         System.out.println("バドミントンして遊びます。");
     }
 }
public class Volleyball implements Sports {
     @Override
     public void play() {
         System.out.println("バレーボールして遊びます。");
     }
 }

そして、Sportsインターフェースの具象クラスを生成するSportsFactoryクラスを作成します。
条件分岐に列挙型を使用することで、異常な値が引数に入れられることを防止しています。

public class SportsFactory {
     public static Sports createSports(DayOfWeek dayOfWeek) {
         if (DayOfWeek.TUESDAY.equals(dayOfWeek)) {
             return new Volleyball();
         } else if (DayOfWeek.WEDNESDAY.equals(dayOfWeek)) {
             return new Badminton();
         } else {
             return new MockSports();
         }
     }
 }
public enum DayOfWeek {
     SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
 }

Badmintonクラスと強く結びついていたPlayerクラスは、ファクトリを使った形になります。
Sportsインターフェースの具象クラスのクラス名が一つも無いことに注目してください。

public class Player {
     public void play(DayOfWeek dayOfWeek) {
         System.out.println("ウォームアップします。");

         Sports sports = SportsFactory.createSports(dayOfWeek);
         sports.play();

         System.out.println("クールダウンします。");
     }
 }

最後にPlayerクラスを実行するクラスを作成します。

public class Main {
     public static void main(String[] args) {
         Player player = new Player();

         player.play(DayOfWeek.TUESDAY);
         System.out.println();

         player.play(DayOfWeek.WEDNESDAY);
         System.out.println();

         player.play(DayOfWeek.SATURDAY);
     }
 }

以上のような構成をとることにより、PlayerクラスとSportsインターフェースの具象クラスの依存関係は弱まりました。
例えばSoccerクラスを追加するといった場合に、Playerクラスを書き換えることなく、PlayerクラスからSoccerクラスが使用できるようになります。

最初のPlayerクラスとBadmintonクラスだけのシンプルな構成から、随分複雑になってしまいましたが、今後の拡張を考慮するのであれば、依存性の弱い構成のほうが良いのです。

次週はこの考え方を更に深めたDI(Dependency Injection)を学びます。