Java Interface-Implementation Pair [closed]

15
задан Carl Manaster 12 June 2015 в 00:47
поделиться

9 ответов

Я бы выбрал следующий подход:

public interface MyInterface {

      MyInterface DEFAULT = new MyDefaultImplementation();

      public static class MyDefaultImplemenation implements MyInterface {
      }
 }

Конечно, MyDefaultImplementation может быть частным или собственным классом верхнего уровня, в зависимости от того, что имеет смысл.

Тогда вы можете иметь в своих реализациях следующее:

 public class MyClass implements MyInterface {
        @Override
        public int someInterfaceMethod(String param) {
             return DEFAULT.someInterfaceMethod(param);
        }
  }

Он немного более самодокументированный и, в конечном счете, более гибкий, чем класс реализации по умолчанию, который существует где-то еще, но не упоминается в интерфейсе. С его помощью вы можете делать такие вещи, как просто передавать реализацию по умолчанию в качестве параметра метода, когда это необходимо (что вы не можете сделать со статическими методами).

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

25
ответ дан 1 December 2019 в 00:13
поделиться

Вы можете превратить интерфейс в абстрактный класс и предоставить реализацию по умолчанию для методов по мере необходимости.

Обновление: Я вижу, множественное наследование закрывает преобразование интерфейса в абстрактный класс ... в этом случае я бы сделал то же самое, что и вы. Если реализация метода (ов) по умолчанию не зависит от состояния, лучшее место для них действительно находится в статическом служебном классе. Однако, если задействовано состояние, я бы рассмотрел композицию объектов, которая может даже закончиться чем-то вроде декоратора .

16
ответ дан 1 December 2019 в 00:13
поделиться

Статические реализации имеют две проблемы:

  • вы не можете найти их с помощью какого-либо статического анализа кода, например, перечисления всех разработчиков, потому что статический метод не реализует интерфейс, поэтому вы вынуждены упомянуть значение по умолчанию реализации в javadoc

  • иногда необходимо иметь какое-то состояние в разработчике, что на самом деле не может быть выполнено в рамках статического метода

. Поэтому я предпочитаю использовать конкретный класс, который допускает наследование (если вы не ограничен единичным наследованием) и композицией, и вызовет результирующий класс * Peer, поскольку он обычно будет использоваться вместе с основным классом, реализующим интерфейс. Одноранговый узел будет реализовывать интерфейс, а также может иметь ссылку на основной объект в случае, если ему нужно запускать события от имени основного класса.

1
ответ дан 1 December 2019 в 00:13
поделиться

Я бы также использовал статические методы для объявления функциональности по умолчанию для функций без сохранения состояния.

Для функциональности с отслеживанием состояния я бы предпочел композицию вместо наследования. С помощью композиции и использования делегатов / адаптеров вы можете комбинировать функциональность по умолчанию из многих источников.

например.

public interface StuffDoer{
    void doStuff();
    void doOtherStuff();
}

public class MyStuffDoer implements StuffDoer{
    private final StuffDoer mixin;
    public  MyStuffDoer(StuffDoer mixin){
        this.mixin = mixin;
    }
    public void doStuff(){
        mixin.doStuff();
    }
    public void doOtherStuff(){
        mixin.doOtherStuff();
    }

}

public class MyStuffDoer2 implements StuffDoer{
    private final StuffDoer mixin1, mixin2;
    public  MyStuffDoer(StuffDoer mixin1, StuffDoer mixin2){
        this.mixin1 = mixin1;
        this.mixin2 = mixin2;
    }
    public void doStuff(){
        mixin1.doStuff();
    }
    public void doOtherStuff(){
        mixin2.doOtherStuff();
    }

}

Для простых случаев наследование тоже нормально, но на самом деле оно не очень гибкое.

Реализация нескольких интерфейсов также является тем случаем, когда этот подход лучше масштабируется.

public interface A{
    void doStuff();
}

public interface B{
    void doOtherStuff();
}

public class MyStuffDoer implements A, B{
    private final A mixin1;
    private final B mixin2;
    public  MyStuffDoer(A mixin1, B mixin2){
        this.mixin1 = mixin1;
        this.mixin2 = mixin2;
    }
    public void doStuff(){
        mixin1.doStuff();
    }
    public void doOtherStuff(){
        mixin2.doOtherStuff();
    }
}

Вы не можете сделать это с абстрактными классами. Я использовал этот подход к композиции в нескольких проектах, и он отлично сработал.

1
ответ дан 1 December 2019 в 00:13
поделиться

Я более или менее следую шаблону, который изначально усвоил у Swing. У меня есть интерфейс, и я создаю класс «Базовый» или «Адаптер». Для меня адаптер часто имеет бездействующие реализации для всех методов интерфейса, чтобы позволить разработчику писать только те методы, которые им нужны, игнорируя другие. Базовым будет абстрактный класс, который предоставляет удобные реализации для некоторых методов интерфейса - будем надеяться, что это «наиболее вероятная» реализация.

Например, у меня есть интерфейс SearchFilter, который, помимо прочего, имеет метод apply (Collection ) . Этот метод почти всегда будет перебирать коллекцию и вызывать метод интерфейса boolean include (T item) , чтобы решить, сохранить или отфильтровать элемент. My SearchFilterBase предоставляет это в качестве реализации для apply (), а разработчик должен только написать свою логику include ().

Разработчики, конечно, могут просто реализовать весь интерфейс сами, а не унаследовать его от Base, что является преимуществом по сравнению с изменением интерфейса на абстрактный класс, что вынуждает их использовать свое единственное наследование (это проблема с java.util.Observable)


В ответ на комментарий N8g - Вы можете создать подкласс базы или адаптера, но не требуемый подкласс - вы можете реализовать интерфейс самостоятельно с нуля.База или адаптер предоставляется для удобства, реализуя безоперационные методы, поэтому вам не нужно это делать, или реализуя удобные общие функции в абстрактном базовом классе (например, мой класс SearchFilterBase ). Преимущество этого по сравнению с превращением интерфейса в абстрактный класс состоит в том, что вы не вызываете наследование от абстрактного класса.

1
ответ дан 1 December 2019 в 00:13
поделиться

В большинстве случаев я использую что-то вроде этого:

public interface Something {
  public void doSomething();

  public static class Static {
    public static void doSomething(Something self) {
      // default implementation of doSomething()
      // the name of the method is often the same
      // the arguments might differ, so state can be passed
    }
  }

  public static abstract class Abstract implements Something {
    // the default abstract implementation
  }

  public static class Default extends Abstract {
    // the default implementation
    public void doSomething() {
      Static.doSomething(this);
    }
  }

  public static interface Builder extends Provider<Something> {
    // initializes the object
  }
}

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

Кроме того, вы можете поместить служебный класс ( Static ) в отдельный файл, если методы касаются не только интерфейса.


Кстати, проблема с декораторами в том, что они скрывают другие реализованные интерфейсы. Другими словами, если у вас есть декоратор something, следующее значение false new SomethingDecorator (new ArrayList ()) instanceof List if SomethingDecorator не реализует интерфейс списки. Вы можете работать вокруг, используя API отражения и, в частности, используя прокси .

Другой хороший подход - адаптеры, как указывает PersicsB.

1
ответ дан 1 December 2019 в 00:13
поделиться

Композиция превосходит множественное наследование с точки зрения гибкости. Как и в случае с шаблоном адаптера (см. Книгу GOF), он явно позволяет нам адаптировать (преобразовывать) классы к разному поведению в зависимости от запроса вызывающего абонента. См. Интерфейс IAdaptable в Eclipse RCP. В основном использование интерфейсов аналогично использованию шаблона адаптера, встроенного непосредственно в язык Java. Но использование адаптируемых классов лучше, чем реализация нескольких интерфейсов. Адаптируемые классы более гибкие, но у них есть некоторая нагрузка на ЦП: оператор instanceof потребляет меньше ресурсов ЦП, чем вызов canAdapt (Class clazz). Но в современных JVM это может быть неверно, поскольку вычисление instanceof с интерфейсами сложнее, чем вычисление instanceof с наследованием.

0
ответ дан 1 December 2019 в 00:13
поделиться

Dependency injection может предоставить делегат и указать реализацию по умолчанию - которая может быть переопределена в модульных тестах.

Например, фреймворк Guice поддерживает аннотацию интерфейса для его реализации по умолчанию, которая может быть переопределена явным связыванием в модульных тестах.

@ImplementedBy( ConcreteStuffDoer.class )
public interface StuffDoer{
    void doStuff();
}

public class ConcreteStuffDoer implements StuffDoer {
    public void  doStuff(){ ... }
}

public class MyClass implements StuffDoer{
  @Inject StuffDoer m_delegate;
  public void  doStuff(){ m_delegate.doStuff(); }
}

Это вводит требование, что для создания экземпляра MyClass требуется метод Guice, а не просто new.

1
ответ дан 1 December 2019 в 00:13
поделиться

Если эта ваша какая угодно функция :-) работает как статическая, то вам не нужно делегировать ей все, поскольку это означает, что ей не нужно "this" - это утилитарная функция.

Вы можете пересмотреть, действительно ли у вас есть веские причины не объединить все, что вы считаете реализацией по умолчанию, в первый абстрактный класс, реализующий этот интерфейс - знаете, адаптация к реальности :-)

Также вы можете создать вторичные, защищенные виртуальные методы в абстрактном классе, которые будут вызывать ваши производные от интерфейса методы, что дает вам больше контроля, т.е. производному классу не нужно заменять методы и копировать и вставлять 90% своего кода - он может просто переопределить метод, который вызывается из основных методов. Это дает вам определенную степень поведенческого наследования.

0
ответ дан 1 December 2019 в 00:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: