Паттерны нужны только там, где они уместны. У каждого паттерна есть собственная цель и область применения. Если нет точной уверенности что использование паттерна решит проблему масштабирования приложения, то и использование его не рекомендуется.
Так же есть один из самых распространенных паттернов так же один из самых полезных - это фабричный метод. Его самый важный плюс в том, что его цель отделить конкретику от общего процесса. При этом достигается инкапсуляция. Так же соблюдается инверсия зависимостей. И реализация не уходит в излишнюю абстракцию, что сохраняет читабельность, и не делает из кода лазанью.
Типовая задача звучит примерно всегда одинаково: У нас есть разные сущности, которые должны иметь одинаковое поведение.
Возьмем к примеру склад на который товар может прийти различными способами и будет различие в оприходовании, но выгружен на одном и том же складе. Допустим нам его может привезти грузовик, или он может прийти к нам пароходом.
interface IArrival
{
public function getProducts(): array;
}
class TruckArrival implements IArrival
{
public function getProducts(): array
{
...
}
}
class ShipArrival implements IArrival
{
public function getProducts(): array
{
...
}
}
class StockControl
{
const TRUCK_ARRIVAL = 1;
const SHIP_ARRIVAL = 2;
private function getArrival(int $arrivalType): IArrival
{
if ($arrivalType === self::TRUCK_ARRIVAL) {
return new TruckArrival();
}
if ($arrivalType === self::SHIP_ARRIVAL) {
return new ShipArrival();
}
}
public function control()
{
...
$arrival = $this->getArrival($arrivalType);
$products = $arrival->getProducts();
...
}
}
Таким образом происходит деление задачи на две подзадачи. Первое определить сам товар и оприходовать его. Вторая разместить на складе.
Так же есть разные вариации, трактования и подтипы паттернов, что привело к нарушению принципа не плодить сущности сверх необходимого.