Экстремальное программирование. Разработка через тестирование | страница 111




SelectionTool

Figure selected;

public void mouseDown() {

selected = findFigure();

if (selected!= null)

select(selected);

}

public void mouseMove() {

if (selected!= null)

move(selected);

else

moveSelectionRectangle();

}

public void mouseUp() {

if (selected == null)

selectAll();

}


В глаза бросаются три похожих условных оператора (я же говорил, что они плодятся, как мухи). Что делать, чтобы избавиться от них? Создаем встраиваемый объект, SelectionMode, обладающий двумя реализациями: SingleSelection и MultipleSelection.


SelectionTool

SelectionMode mode;

public void mouseDown() {

selected = findFigure();

if (selected!= null)

mode = SingleSelection(selected);

else

mode = MultipleSelection();

}

public void mouseMove() {

mode.mouseMove();

}

public void mouseUp() {

mode.mouseUp();

}


В языках с явными интерфейсами вы обязаны реализовать интерфейс с двумя (или больше) встраиваемыми объектами.

Встраиваемый переключатель (Pluggable Selector)[26]

Как обеспечить различающееся поведение разных экземпляров одного и того же класса? Сохраните имя метода в переменной и динамически обращайтесь к этому методу.

Что делать, если у вас есть десять подклассов одного базового класса и в каждом из них реализован только один метод? Может оказаться, что создание подклассов – это слишком тяжеловесный механизм для реализации столь небольших различий в поведении объектов.


abstract class Report {

abstract void print();

}

class HTMLReport extends Report {

void print() {…

}

}

class XMLReport extends Report {

void print() {…

}

}


Альтернативное решение: создать единственный класс с оператором switch. В зависимости от значения поля происходит обращение к разным методам. Однако в этом случае имя метода упоминается в трех местах:

• при создании экземпляра;

• в операторе switch;

• в самом методе.


abstract class Report {

String printMessage;


Report(String printMessage) {

this.printMessage = printMessage;

}

void print() {

switch (printMessage) {

case "printHTML":

printHTML();

break;

case "printXML":

printXML():

break;

}

};


void printHTML() {

}


void printXML() {

}

}


Каждый раз, когда вы добавляете новую разновидность печати, вы должны позаботиться о добавлении нового метода печати и редактировании оператора switch.

Шаблон «Встраиваемый переключатель» (Pluggable Selector) предлагает динамически обращаться к методу с использованием механизма рефлексии:


void print() {

Method runMethod = getClass(). getMethod(printMessage, null);

runMethod.invoke(this, new Class[0]);

}


По-прежнему существует весьма неприятная зависимость между создателями отчетов и именами методов печати, однако, по крайней мере, мы избавились от оператора switch.