Red > Green > Refactor > Red

cycle is based on desire

interface を使う理由 Java

interface を使う理由

実例

  • 標準入力した文字列を replace して trim する

interface 利用前

package

src
  |
  +- Main.java
  +- util
     |
     +- MyIO.java

Main.java

package src;
import src.util.MyIO;

public class Main {
  public static void main(String[] args) {
    MyIO io = new MyIO();
    io.IOStream();
  }     
}

MyIO.java

package src.util;
import java.util.*;
import java.io.*;

public class MyIO {
  public void IOStream() {
    try(BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in))) {
      while(true) {
        System.out.println("Input");

        String output = stdin.readLine();
        output = trimer(replacer(output, "a", "ummm"));
        System.out.println("Final output is:[" + output + "]");
      }
    } catch(Exception e) {
      e.printStackTrace();
    }
  }

  private String replacer(String input, String oldStr, String newStr) {
    return input.replaceAll(oldStr, newStr);
  }

  private String trimer(String input) {
    return input.trim();
  }
}

実行例

@ = space. not stdin

Input
@he is a ... who?@
Final output is:[he is ummm ... who?]

上述設計での課題

  • 機能追加への柔軟性がない
    • 機能追加要望が発生した場合
      (replacer, trimer 以外に upper を追加など)
      private メソッドを増やす必要が有る
    • MyIO の replace と trim が別クラスでも使われることになった場合
      → replacer と trimer が public になる
      → MyIO クラスが別クラスと密結合になる
      → replacer と trimer を修正する度に、結合クラスへの影響を調べる必要が有る
      → (゚∀゚)

interface 利用後(モジュールが疎結合)

package

src
  |
  +- Main.java
  +- util
     |
     +- MyIO.java
  +- interfaces
     |
     +- StringConverter.java
     +- Replacer.java
     +- Trimer.java

Main.java

package src;
import java.util.*;
import src.util.MyIO;
import src.interfaces.Replacer;
import src.interfaces.Trimer;

public class Main {
  public static void main(String[] args) {
    MyIO io = new MyIO(Arrays.asList(new Replacer("a", "ah!"),
                                     new Trimer())
    );  
    io.IOStream();
  }
}

MyIO.java

package src.util;
import java.util.*;
import java.io.*;
import src.interfaces.StringConverter;

public class MyIO {
  private final List<StringConverter> converters;
  
  public MyIO(List<StringConverter> converters) {
    this.converters = converters;    
  } 
    
  public void IOStream() {
    try(BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in))) {
      while(true) {
        System.out.println("Input");

        String output = stdin.readLine();
        for(StringConverter convert : converters) {
          output = convert.execConvert(output);
        }
        System.out.println("Final output is:[" + output + "]");
      }
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
}

StringConverter.java

package src.interfaces;

public interface StringConverter {
  String execConvert(String input);
}

Replacer.java

package src.interfaces;

public class Replacer implements StringConverter {
  private final String oldStr;
  private final String newStr;

  public Replacer(String oldStr, String newStr) {
    this.oldStr = oldStr;
    this.newStr = newStr;
  }

  @Override
  public String execConvert(String input) {
    return input.replaceAll(oldStr, newStr);
  }
}

Trimer.java

package src.interfaces;

public class Trimer implements StringConverter { 
  @Override
  public String execConvert(String input) {
    return input.trim();
  }
} 

実行例

@ = space. not stdin

Input
@he is a ... who?@
Final output is:[he is ah! ... who?]

メリット

  • (設計上)機能追加しやすい
    • interface を implements した class を修正した場合の影響範囲を
      これを使う側だけ気にすればよい
    • 「文字を大きくする機能を追加して」と要望がきたら
      src/util/Upper.java(toUpperCase を使う) なり作成し
      Main.java に new Upper() を Arrays.asList に追加すればよい

デメリット

  • 機能ごとにファイルが必要になり、冗長性が増す

参考

gihyo.jp

7章 インターフェース より