Внедрение шаблона адаптера класса в 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, для достижения одного и того же результата.