На практике, разницы быть не должно. Ещё раз, особенно касаемо внутренного проекта: не хватает возможности — добавь точку расширения через конструктор вместо наследования. Различие только в способе расширения.
Наверное я плохо объяснял на пальцах. Попробую на конкретном примере. Имеем достаточно большой фасад, представляющий третью сторону. То что тут надо чего то делить и разбивать не обсуждается ибо в этом и был весь смысл, что бы сократить зависимость от третьей стороны.
Код:
@Override
public void placeOrder(Order order) throws OrderException {
synchronized ( order ) {
OrderStatus status = order.getStatus();
EditableOrder o = (EditableOrder) order;
if ( status == OrderStatus.PENDING ) {
OrderActivator activator = o.getActivator();
if ( activator != null ) {
activator.start(o);
o.setStatus(OrderStatus.CONDITION);
orders.fireEvents(o);
return;
}
} else if ( status == OrderStatus.CONDITION ) {
order.getActivator().stop();
}
orderProcessor.placeOrder(this, order);
}
}
В данном случае orderProcessor это специально выделенный интерфейс как и положено. За этим интерфейсом специфика конкретной части, то бишь работы с заявками. Что бы разорвать циклическую ссылку, обеспечить целостность и согласованность сразу после инстанцирования конкретной реализации фасада и при этом обеспечить возможность реализовать алгоритм любой сложности, метод placeOrder принимает инстанс фасада. Здесь нет смысла мудрить, потому что мы изначально не знаем какая специфика скрывается за конкретными реализациями. Мы не можем выделить никакой интерфейс для передачи в процессор, потому что это уже спецификация протокола, который нам, повторяю - неизвестен.
Если этот базовый фасад объявить final, то этот код уже не будет работать коректно. Ведь если его декорировать, то this это будет инстанс базовой реализации, а в процессоре нужна специфическая реализация. Здесь можно вывернуться, добавив публичный метод типа setTopInstance, который фабрика фасада должна будет вызвать после инстанцирования своего специфического инстанса. Но это сразу архитектурная проблема и не одна. Во первых, это будет публичный метод, так как декорировать можно только публичные методы. И он будет торчать наружу там, где эти кишки абсолютно не нужны, а только ведут к потенциальным проблемам использования. Далее, велик шанс что называется забыть его установить после инстанцирования. В третьих, это нестандартный подход будет постоянно напрягать необходимостью использовать getTopInstance везде внутри самого фасада. Я надеюсь этого достаточно, что бы выкинуть отсюда final.
И в этом случае отлично работает наследование и расширение. Сам фасад не содержит много кода, он содержит много методов-делегатов к подсистемам. И служит отличной абстракцией, которая позволяет реализовывать различные подсистемы за фасадом в виде отдельных несвязанных классов. В случае с файналом пришлось бы связывать эти подсистемы между собой каждый раз новым специфическим способом. А так, мы имеем отличную тестируемую архитектуру. Она уже разделена на подсистемы и тут нечего выделять в новый интерфейс, потому что задача обратная - собрать пакет в единую систему.
И здесь у меня нет ни одной идеи по поводу того, какую точку расширения и собственно расширения чего конкретно я здесь должен добавить, как ты предлагаешь.
вторая — теоретическая, зачем тогда мок, почему не инстанс конкретного класса?
Я не отношусь к тем людям, которые твердо уверены, что из двух существующих: тестирование поведения и тестирование состояния - только один подход является правильным. Очевидно, что для разных классов эти два подхода будут давать различную эффективность. Но ты прав, это отдельная большая тема.