PHP - продукт фабрики имеет специальный метод

Я стараюсь использовать фабричный узор для создания изделий. Я создаю интерфейс для классов продуктов, чтобы реализовать и спроектировать фабрику, которая будет генерировать продукт в соответствии с переданными ему параметрами.

Интерфейс

interface Product
{
  public function getName();
}

Продукты

class ProductA implements Product{
  public function getName()
  {
    return 'A'; 
  }
}

class ProductB implements Product{
  public function getName()
  {
    return 'B'; 
  }
  public function SpecialMethod()
  {
    return 'only B';
  }
}

Фабрика

class Factory(){
  public function getProduct($type)
  {
    switch ($type) {
      case 'A':
        return new ProductA();
        break;

      case 'B':
        return new ProductA();
        break;

      default:
        # code...
        break;
    }
  }
}

Применение

function someBussinessLogic($type)
{

  // ...ignore...

  $factory = new Factory;
  $product = $factory->getProduct($type);

  if ($type == 'B') {
    $product->SpecialMethod();
  }

  // ...ignore...
}

При использовании я должен проверить, является ли $type «B». Я считаю такое поведение странным. Мне просто сделать это или поставить SpecialMethod на interface Product? Или есть способ лучше?


[редактировать]

Да, я беспокоюсь о том, что в будущем появятся продукты C, D, E ... и у каждого из них есть свой особый метод. Если я просто помещу все специальные методы в интерфейс, интерфейс станет очень огромным, и все остальные продукты должны будут реализовать бессмысленные методы.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
0
58
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я бы сказал, это зависит от обстоятельств. Если вы планируете со временем добавить этот метод к большему количеству классов, выберите интерфейс. Я бы не считал плохой практикой, если бы были классы, которые все еще должны реализовывать этот метод, но с пустым телом. Таким образом, вы можете избавиться от своего if и не возвращаться к своему коду, если класс C также реализует этот метод.

Если это будет ТОЛЬКО этот особый случай, я (лично) выберу то, что уже есть у вас.


Или, может быть, вы можете вызвать специальный метод, в котором вы вызываете someBusinessLogic(), поскольку после этого оператора за ним ничего не следует. Я имею в виду что-то вроде:

someBusinessLogic($type)->specialMethod();

Если этот подход работает для вашего существующего кода, я настоятельно рекомендую пойти этим путем вместо if.

Ответ принят как подходящий

Я бы сделал это

$product = $factory->getProduct($type);

if (is_a($product, ProductB::class)) {
   $product->SpecialMethod();
}

Или используйте интерфейс, если у вас будет много классов с SpecialMethod, тогда вы можете проверить интерфейс вместо одного класса.

Для справки это

Checks if the given object is of this class or has this class as one of its parents.

Вы также можете использовать экземпляр

if ($product instanceof ProductB::class) {
   $product->SpecialMethod();
}

Вот полезный пост о разнице между двумя

В чем разница между is_a и instanceof?

Также

Я тоже не вижу в этом ничего плохого.

interface ProductInterface
{
    public function getName();
    public function SpecialMethod();
}

Однако я бы также добавил в него абстрактный класс.

abstract class AbstractProduct impliments ProductInterface{
    abstract public function getName();
    public function SpecialMethod()
    {
        return ''; //or false
    }
}

class ProductA extends AbstractProduct {
    public function getName()
    {
        return 'A'; 
    }
}

class ProductB extends AbstractProduct {
    public function getName()
    {
        return 'A'; 
    }
    //override the concrete method in abstract class
    public function SpecialMethod()
    {
        return 'only B'; 
    }
}

Таким образом, вы можете просто оставить этот метод пустым в других классах, если вы не полагаетесь на SpecialMethod для какого-либо типа возвращаемого значения. Я бы также изменил имя, как и я, но это только мое предпочтение.

Тогда ты сможешь это сделать

$product = $factory->getProduct($type);
$product->SpecialMethod();  //return "only B" or ""

Потому что большинству классов будет присущ пустой метод абстрактного класса, и только те продукты, которые реализуют их переопределение, будут иметь значение. Это имеет смысл, если этот метод будет иметь несколько продуктов, но также имеет смысл создать отдельный интерфейс и проверить его. Так что это больше зависит от специфики того, что на самом деле делает SpecialMethod.

Заводской узор: В соотв. Согласно Википедии, шаблон фабричного метода - это шаблон создания, который использует фабричные методы для решения проблемы создания объектов без необходимости указывать точный класс объекта, который будет создан. Здесь проблема, о которой вы упомянули, связана с проверкой перед вызовом $ product-> SpecialMethod ();

. Если мы пройдемся по реализациям классов, станет ясно, что ПродуктB требует только specialMethod. Итак, в someBussinessLogic ($ type), независимо от дизайна кода, практически вы хотите вызывать specialMethod только в случае typeB, поскольку в других случаях нет функциональности specialMethod (например, почему вы хотите вызывать specialMethod во всех случаях? ). Значит, вам автоматически нужен чек.
С ​​другой стороны, если вы пишете общий код, ваши сущности не должны иметь определенного поведения. И если они есть, вы должны подключить их к общему коду, а не ставить галочку внутри общего кода.

Теперь, поскольку вам придется обрабатывать такие проверки явным образом, вы можете задаться вопросом, как шаблон фабрики помогает вам ?. Что ж, фабричный шаблон засветится в тот момент, когда вы захотите изменить функциональность определенного типа, у вас будет единственная точка, откуда вы можете подключить класс и подключить обновленный класс с новыми функциями. Суть в том, что если вы хотите обрабатывать определенное поведение в своем универсальном коде, как в приведенном выше случае, вам будут необходимо написать чек где-нибудь в вашем коде. Лучше быть вне общих модулей, чем ставить под угрозу целостность общего кода.

Другие вопросы по теме