Внедрение шаблона адаптера класса в Java
При чтении на шаблоне адаптера класса в Head First Design Patterns я натолкнулся на это предложение:
адаптер класса... потому что для его реализации требуется множественное наследование, что невозможно в Java
Чтобы экспериментировать, я попробовал следующее:
interface MyNeededInterface{
public void operationOne(MyNeededInterface other);
public MyNeededInterface operationTwo();
}
public class ThirdPartyLibraryClass{
public void thirdPartyOp();
}
Предположим, что создаю:
class ThirdPartyWrapper extends ThirdPartyLibraryClass implements MyNeededInterface{
@Override
public void operationOne(ThirdPartyWrapper other){
this.thirdPartyOp();
dosomeExtra();
}
@Override
public ThirdPartyWrapper operationTwo(){
int somevalue = doSomeThingElse();
return new ThirdPartyWrapper(somevalue);
}
}
В моем коде я могу использовать:
MyNeededInterface myclass = createThirdPartyWrapper();
myclass.operationOne(someobj);
...
Это не шаблон адаптера класса?
Ответы
Ответ 1
Шаблон адаптера класса невозможен в Java, потому что вы не можете расширять несколько классов. Поэтому вам придется перейти с шаблоном адаптера, который использует состав, а не наследование.
Пример шаблона адаптера через композицию можно найти ниже:
interface Duck
{
public void quack();
}
class BlackDuck implements Duck
{
public void quack() { }
}
class Turkey
{
public void gobble() { }
}
class TurkeyAdapter implements Duck
{
private Turkey t;
public TurkeyAdapter(Turkey t)
{
this.t = t;
}
public void quack()
{
// A turkey is not a duck but, act like one
t.gobble();
}
}
Теперь вы можете передать Turkey
методу, который ожидает Duck
через TurkeyAdapter
.
class DuckCatcher
{
public void catch(Duck duck) { }
}
Используя шаблон адаптера, DuckCatcher
теперь также может ловить Turkey(Adapter)
и Duck
s.
Ответ 2
Полная история в хэдз-ап: класс шаблон адаптера невозможен в Java только потому, что Java не предоставляет множественное наследование.
В своей диаграмме они показывают, что класс Adapter
подклассы как Target
, так и Adaptee
. Ваш пример (близкий к) шаблон адаптера объекта. Разница в том, что вы реализуете Target в своем классе адаптера, а не просто подклассифицируете цель (MyNeededInterface
в вашем примере)
Ответ 3
Да, вы можете создать адаптор класса с интерфейсом, если вы только обертываете одного адаптируемого. С множественным наследованием вы можете взять две или более адаптированных и объединить их в один интерфейс.
Ответ 4
GoF (Gang of Four) рассказывает нам о двух основных видах адаптеров:
А. Класс адаптеров. Обычно они используют множественное наследование для адаптации одного интерфейса к другому. (Но мы должны помнить, что в Java множественное наследование через классы не поддерживается (по уважительной причине :)). Нам нужны интерфейсы для реализации концепции множественного наследования.)
Б. Адаптеры объектов. Они зависят от составов объектов.
Чтобы проиллюстрировать концепции, я приведу простой пример:
(источник: книга Java Design Patterns)
interface IIntegerValue
{
public int getInteger();
}
class IntegerValue implements IIntegerValue
{
@Override
public int getInteger()
{
return 5;
}
}
// Adapter using interface
class ClassAdapter extends IntegerValue
{
//Incrementing by 2
public int getInteger()
{
return 2 + super.getInteger();
}
}
// Adapter using composition
class ObjectAdapter implements IIntegerValue
{
private IIntegerValue myInt;
public ObjectAdapter(IIntegerValue myInt)
{
this.myInt=myInt;
}
//Incrementing by 2
public int getInteger()
{
return 2+this.myInt.getInteger();
}
}
class ClassAndObjectAdapter
{
public static void main(String args[])
{
System.out.println("Class and Object Adapter Demo");
ClassAdapter ca1=new ClassAdapter();
System.out.println("Class Adapter is returning :"+ca1.getInteger());
ClassAdapter ca2=new ClassAdapter();
ObjectAdapter oa=new ObjectAdapter(new IntegerValue());
System.out.println("Object Adapter is returning :"+oa.getInteger());
}
}
Вывод на консоль:
Демонстрация адаптера класса и объекта
Адаптер класса возвращается: 7
Адаптер объекта возвращается: 7
Ответ 5
Адаптеры классов возможны в Java с использованием одного наследования.
В качестве примера из шаблона проектирования для макетов, предположим, что мы должны адаптировать флажки AWT для использования вместе с флажками Swing, для этого мы можем написать адаптер класса.
Код пользовательского интерфейса в Swing, чтобы определить, установлен ли флажок, выполняется методом isSelected. Но флажки AWT не поддерживают isSelected(), вместо этого они используют getState().
Таким образом, мы можем написать адаптер для переноса флажка SWT и адаптировать getState() к isSelected()
public class CheckboxAdapter extends Checkbox
{
public CheckboxAdapter(String n)
{
super(n);
}
public boolean isSelected()
{
return getState();
}
}
Теперь мы можем обрабатывать флажки, адаптированные к AWT, так же, как и стандартные флажки Swing, когда речь идет о методе isSelected.
public void itemStateChanged(ItemEvent e)
{
String outString = new String("Selected: ");
for(int loopIndex = 0; loopIndex
<= checks.length - 1; loopIndex++){
if(checks[loopIndex].isSelected()) {
outString += " checkbox " + loopIndex;
}
}
text.setText(outString);
}
ОБНОВЛЕНИЕ: Истинный адаптер класса невозможен в Java, если бы мы могли наследовать его от нескольких классов, которые мы хотим имитировать в классе адаптера.
Также см. http://www.journaldev.com/1487/adapter-design-pattern-in-java-example-tutorial для двух примеров в Java, использующих Class Adapter и Object adapter, для достижения одного и того же результата.