В объектно-ориентированных языках есть два основных метода образовывать композиции из классов (увязывать их вместе) - наследование и делегирование.
Наследование
Наследование позволяет достигнуть двух целей:
- получение интерфейса (от класса/интерфейса)
- получение реализации (от класса/(с Java 8 - интерфейса))
public class ArrayList implements List extends AbstractList
Хотим "быть листом"
List<String> words = new ArrayList<>(16);
И при этом реализацию метода size() получить от AbstractList.
class Oval implements Figure {...}
Хотим иметь List<Figure> с Oval, Rect, ...
Хотим иметь методы работающие с Figure и коллекциями фигур List<Figure>, хотим насладится полиморфизмом на основе наследования
public class BufferedInputStream extends InputStream
Хотим заменять InputStream
Правильно наследуются в следующих случаях:
- реализация размазана по иерархии - AbstractCollection, AbstractList, ...
- реализация не доделана и ждет нас - TemplateMethod (InputStream)
- реализация доделана, но открыта к расширению - LinkedHashMap
- Hibernate наследует bean
- Spring влезает к нам под интерфейс для tx, security, logging
- маркер интерфейс - RandomAccess, Cloneable, Serializable
Неправильно наследуются в следующих случаях:
- ситуативно (extends ArrayList для перекрытия add, но забыли про addAll)
- бытовая интуиция (Square ex Rect, Rect ex Square) + (Rect - Parallelogram)
- глупое обобщение - Idible {long getId()} - все объекты в системе с Id
Делегирование
Делегирование решает проблемы:
- большое количество комбинаций качеств - комбинация Декораторов
- динамическое изменение поведения - State / Strategy
Существуют и другие способы образовывать композиции в ООП:
- ??? слаботипизированная композиция по аннотациям (JUnit, Servlet API 3, ?)
- ??? Mockito - на основе правил
Существуют и другие способы образовывать композиции в не-ООП:
- функциональные ???
Наследование
Наследование позволяет достигнуть двух целей:
- получение интерфейса (от класса/интерфейса)
- получение реализации (от класса/(с Java 8 - интерфейса))
public class ArrayList implements List extends AbstractList
Хотим "быть листом"
List<String> words = new ArrayList<>(16);
И при этом реализацию метода size() получить от AbstractList.
class Oval implements Figure {...}
Хотим иметь List<Figure> с Oval, Rect, ...
Хотим иметь методы работающие с Figure и коллекциями фигур List<Figure>, хотим насладится полиморфизмом на основе наследования
public class BufferedInputStream extends InputStream
Хотим заменять InputStream
Правильно наследуются в следующих случаях:
- реализация размазана по иерархии - AbstractCollection, AbstractList, ...
- реализация не доделана и ждет нас - TemplateMethod (InputStream)
- реализация доделана, но открыта к расширению - LinkedHashMap
- Hibernate наследует bean
- Spring влезает к нам под интерфейс для tx, security, logging
- маркер интерфейс - RandomAccess, Cloneable, Serializable
Неправильно наследуются в следующих случаях:
- ситуативно (extends ArrayList для перекрытия add, но забыли про addAll)
- бытовая интуиция (Square ex Rect, Rect ex Square) + (Rect - Parallelogram)
- глупое обобщение - Idible {long getId()} - все объекты в системе с Id
Делегирование
Делегирование решает проблемы:
- большое количество комбинаций качеств - комбинация Декораторов
- динамическое изменение поведения - State / Strategy
Существуют и другие способы образовывать композиции в ООП:
- ??? слаботипизированная композиция по аннотациям (JUnit, Servlet API 3, ?)
- ??? Mockito - на основе правил
Существуют и другие способы образовывать композиции в не-ООП:
- функциональные ???