Большая часть шаблонов проектирования, с технической точки зрения, сводится к полиморфизму подтипов. Это значит что можно подменить реализацию некоторой функциональности так, что клиентский код этого не заметит, потому что способ вызова останется тем же. Этот подход одинаково работает и для обычных функций и для классов и других конструкций существующих в языках и позволяющих делать вызовы функций в зависимости от типа данных, но без условных конструкций.
const logger = new Logger();
// logger.info(message)
runApp(logger);В примере выше создается экземпляр типа Logger и передается в приложение. При необходимости можно заменить Logger на другой тип логгера (другую библиотеку) так что не придется изменять содержимое приложения (то что вызывается в runApp). Единственное условие для этого, совпадение по интерфейсу старого и нового логгеров.
const logger = new AnotherLogger();
// logger.info(message)
runApp(logger);Плюсы полиморфизма раскрываются именно тогда, когда приложение или его часть конфигурируется нужным поведением. Например через передачу объекта (или функции) как параметра функции использующей объект. Такой способ конфигурирования называют принцип инверсии зависимостей (Dependency inversion principle или DIP). То есть вызывающий код не сам обращается к полиморфной сущности, а эту сущность передают ему снаружи. Этот принцип часто называют принципом голливуда: "не звоните нам, мы сами перезвоним". А способы передачи зависимостей называют понятием внедрение зависимостей (dependency injection). Внедрять зависимости можно как параметр обычной функции, через вызов сеттера или через конструктор.
import Logger from 'logger';
export default runApp = () => {
const logger = new Logger();
logger.info('start application')
}В данном коде зависимость используется напрямую. Если понадобится его заменить, то придется пройти по всем местам в которых он встречается и произвести ручную замену. Другими словами без инверсии зависимостей полиморфизма нет. Клиентский код придется переписывать.
С другой стороны это совершенно не значит что абсолютно весь код нужно инъектировать через параметры функций, а зависимости определять только в одном месте сразу для всего приложения. Полиморфизм нужен не так часто как об этом трубят, да и инвертировать зависимость, как правило можно, и на более поздних этапах разработки. К тому же, в современных фреймворках (на которых базируется веб-разработка) включены все необходимые механизмы для инъекции зависимостей. В таких языках как PHP или Java ничего самостоятельно придумывать не придется.
В паттернах, которые описаны здесь, подразумевается что вы знакомы с понятием DIP и поэтому этот аспект опускается, иначе пришлось бы значительно усложнять все примеры. Важно всегда помнить, что любой паттерн сводящийся к полиморфизму подтипов, подразумевает наличие инверсии зависимостей. То есть клиентский код в тех ситуациях работает с элементами не через прямые импорты и вызовы, а посредством инъекции.