Действующий элемент Java 16 (2-е издание). Используется ли класс Forwarding только для повторного использования?

Я прохожу через Effective Java, Item-16 Favor composition over inheritance. Я посмотрел на пример Forwarding class ниже.

Мне интересно, какой смысл иметь класс ForwardingSet? InstrumentedSet может очень хорошо реализовать Set и иметь частный экземпляр, который вызывает все методы.

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

// Reusable forwarding class 
public class ForwardingSet<E> implements Set<E> {     
  private final Set<E> s;     
  public ForwardingSet(Set<E> s) { this.s = s; }     
  public void clear()               { s.clear();            }    
  public boolean contains(Object o) { return s.contains(o); }
...
}

// Wrapper class - uses composition in place of inheritance   
public class InstrumentedSet<E> extends ForwardingSet<E> {     
      private int addCount = 0;     
      public InstrumentedSet(Set<E> s) { super(s); } 
      @Override public boolean add(E e) {         
          addCount++;
          return super.add(e);
       }
       ...
    }

person linuxNoob    schedule 15.09.2018    source источник
comment
Обратите внимание, что это относится ко второму изданию Effective Java. В 3-м издании номер предмета для композиции благосклонности перед наследованием изменен на 18.   -  person Olivier Grégoire    schedule 15.09.2018


Ответы (2)


Да, ForwardingSet — это фреймворк.

Если вам нужно написать несколько Set, которые работают с другими Set внутри, но предоставляют разные функции поверх «ванильного» Set, вам лучше написать общую часть один раз, а не несколько раз.

Джошуа Блох в Effective Java называет это "композицией", хотя реальная реализация больше похожа на шаблон декоратора.

Фактическая реализация доступна в Guava в виде класса с именем ForwardingSet.

Это должно способствовать повторному использованию и предотвращению избыточности, если в будущем у нас будет больше классов, подобных InstrumentedSet, которым просто нужно что-то делать в дополнение к базовому поведению?

Да.

Это просто перспективный дизайн?

Да.

или есть что-то еще, что мне не хватает?

Нет, вы ничего не упускаете.

person Olivier Grégoire    schedule 15.09.2018
comment
Мой следующий вопрос: делаем ли мы это каждый раз, когда думаем, что может возникнуть необходимость decorate API с поведением, отличным от стандартного? Например, API, которые на первый взгляд не кричат ​​о необходимости повторного использования. Скажем, получение и возврат некоторых элементов из базы данных. - person linuxNoob; 15.09.2018
comment
@linuxNoob Нет. Не каждый раз начинайте думать о рефакторинге в класс Forwarding... только тогда, когда написано несколько оболочек. - person Olivier Grégoire; 15.09.2018
comment
@linuxNoob Если декорируемый объект реализует множество методов, а вы изменяете поведение только некоторых (если есть), то наличие базового класса только для делегата помогает отделить логику делегирования от логики декорирования, т. е. базы класс выполняет только делегирование, поэтому его не нужно тестировать и проверять, а подкласс выполняет только оформление, поэтому его необходимо тестировать и проверять. Логика оформления не утонет в море логики делегирования. См. также: Разделение проблем. - person Andreas; 15.09.2018

Для поощрения повторного использования и предотвращения избыточности? Да.
Просто для будущего проекта? Да.
Есть что-то еще к этому, что мне не хватает? Нет.

«Пересылка» обычно называется «делегирование».
См.: Какова цель образец делегирования?

Некоторые примеры классов Java с реализациями только для делегатов:

  • #P4# <блочная цитата> #P5#
  • #P6# <блочная цитата> #P7#
person Andreas    schedule 15.09.2018