次に、LibrarySystemTestの名前をAddPatronCommandTestに変更し、AddPatronCommandを直接呼び出すように書き換えます。変更後のテストをリスト4に示します。変更箇所は太字で示しています。
public class ''AddPatronCommandTest'' { @Test public void ''execute''() { final List<Patron> added = new ArrayList<Patron>(); LibrarySystem system = new LibrarySystem() { @Override public void add(Patron patron) { added.add(patron); } }; Request request = new Request(); request.setParameter("command", "addPatron"); request.setParameter("name", "patronName"); request.setParameter("id", "patronId"); ''AddPatronCommand command = new AddPatronCommand(request, system);'' ''command.execute();'' assertEquals(1, added.size()); assertEquals("patronName", added.get(0).getName()); assertEquals("patronId", added.get(0).getId()); } }
LibrarySystemTestクラスの名前を変更したので、LibrarySystemをテストすることができなくなりました。この問題はすぐ後で再度取り上げます。先に、LibrarySystemとAddPatronCommandの密結合を取り除くことにします。これにはメソッドとフィールドを正しく移動させる必要があります。add(Patron)メソッドを移動しなければならないことは間違いありません。add(Patron)メソッドを移動した後に、コンパイラを使用して他にどのメソッドやフィールドを移動させる必要があるかを調べたところ、add(Patron)に関してはデータアクセスフィールドもコピーする必要があるとわかりました。
変更後のテストとAddPatronCommandクラスは下記のとおりですが、内容はあまり変わっていません(リスト5と6を参照)。
public class AddPatronCommandTest { @Test public void execute() { Request request = new Request(); request.setParameter("command", "addPatron"); request.setParameter("name", "patronName"); request.setParameter("id", "patronId"); final List<Patron> added = new ArrayList<Patron>(); AddPatronCommand command = new AddPatronCommand(request) { @Override public void add(Patron patron) { added.add(patron); } }; command.execute(); assertEquals(1, added.size()); assertEquals("patronName", added.get(0).getName()); assertEquals("patronId", added.get(0).getId()); } }
public class AddPatronCommand implements Command { private String name; private String id; ... public AddPatronCommand(Request request) { name = request.getParameter("name"); id = request.getParameter("id"); } public void execute() { Patron patron = new Patron(name, id); add(patron); } public void add(Patron patron) { ... } }
このリファクタリングパターンを使用して、このようなコマンドクラスを段階的に作成していくことができます。2つ目のコマンドクラスに取り組んでいるときに、両方のコマンドが同じメソッド(execute)をサポートしていることに気が付いたので、Commandインターフェイスを作成し(リスト7を参照)、各コマンドクラスがこのインターフェイスを実装するように変更しました。
public interface Command { void execute(); }
