2015-12-25 2 views
0

Мои классы зависят от слишком большого количества других классов, и я не мог найти способы его улучшить. Проблема выглядит примерно так:Как разбить и улучшить циклическую зависимость без использования прокси-шаблона?

У меня есть классы ProductRepo, ProductFactory и ImageFactory. ProductRepo делает db вещь в таблице продуктов и выбирает строки как массив. Этот массив передается в ProductFactory для создания модальности продукта. В товарных модалях также есть изображения, связанные с ним.

код клиента:

$products = $this->productRepo->findAll('...'); 
foreach($products as $product){ 
    ... 
    //get images 
    $images = $product->getImages(); 
    ... 
} 

Class ProductRepo implements ProductRepositoryInterface{ 
    protected $productFactory; 
    protected $imageFactory; 
    public function __construct(ProductFactoryInterface $productFactory, ImageFactoryInterface $imageFactory) 
    { 
     $this->productFactory = $productFactory; 
     $this->imageFactory = $imageFactory; 
    } 

    public function findAll(...) 
    { 
     $result = $this->execute('....'); 
     $products = $this->productFactory->make($result); 
     return $products; 
    } 

    public function getImages($productId) 
    { 
     $result = $this->execute('....'); 
     $images = $this->imageFactory->make($result); 
     return $images; 
    } 
} 

Class ProductFactory implements ProductFactoryInterface{ 
    protected $productRepo; 
    public function __construct(ProductRepositoryInterface $productRepo) 
    { 
     $this->productRepo = $productRepo; 
    } 

    public function make($items) 
    { 
     ... 
     $products = []; 
     foreach($items as $item){ 
      $product = new Product($item); 
      $item->setImages($this->productRepo->getImages($product->getId())); 
      $products[] = $product; 
     } 
     ... 
     return $products; 
    } 
} 

Class ImageFactory implements ImageFactoryInterface{ 
    public function make($items) 
    { 
     ... 
     $images = []; 
     foreach($items as $item){ 
      $image = new Image($item); 
      $images[] = $image; 
     } 
     ... 
     return $images; 
    } 
} 

Итак, я следующие проблемы:

  1. циклическая зависимость ProductRepo -> ProductFactory -> ProductRepo

    Чтобы пропустить это, я могу используйте инъекцию сеттера или используйте прокси-шаблон. Но я думаю, что это не было бы хорошим решением. Как вы, ребята, справляетесь с такими проблемами?

  2. ProductRepo зависит как от ProductFactory, так и от ImageFactory. Это хорошая практика, зависящая от более чем одной фабрики?

Я думаю, что проблемы ясны. :) Спасибо

+0

BTW, я использую PHP и удалил реализацию ProductHydrator и ImageHydrator экземпляров внутри классов Factory, чтобы сделать эту проблему просто. – Laxman

ответ

1

есть несколько способов, чтобы разорвать циклическую зависимость, но наиболее funadmental проблема, кажется, что ProductFactory requries в ProductRepo, который сам по себе должен быть в состоянии построить продукты, даже ты gh эта функция не будет использоваться, и, вероятно, не имеет смысла передавать ProductRepo, который использует другую фабрику (скрытое правило). Итак:

1) Создайте ImageRepositoryInterface, который имеет только метод getImages. ProductRepositoryInterface может либо расширить этот интерфейс, либо ProductRepo может реализовать его самостоятельно. Затем передайте репозиторий изображений в ProductFactoryInterface.make вместо того, чтобы требовать его при построении. Вы можете передать свой ProductRepo в это время.

2) Да, нет никаких проблем с в зависимости от более чем одного вида завода

+0

Спасибо Matt (Y). Намного лучше передать ImageRepo в ProductFactoryInterface.make вместо того, чтобы требовать его при построении. – Laxman

2

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

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

Затем создайте коллекцию или массив продуктов в классе repo продукта и верните это.

Нечто подобное (написано в псевдо-PHP)

Class Product 
    { 
     public function __construct(productInfo, imageArray) 
     { 
      //contruct product here 
     } 
    } 

    Class ProductRepo 
    { 

     public function getProducts() 
     { 
      //retrieve products 
      $items = getProducts(); 

      //setup products 
      return setupProducts($items); 
     } 

     private function setupProducts($items) 
     { 

      foreach($items as $item){ 
       $images = $this->getImages($product->getId()); 

       $product = new Product($item, $images); 

       $products[] = $product; 
     } 
      return $products; 
     } 

     private function getImages($itemId) 
     { 
      //get and return images for this product 
     } 

     private function loadProducts() 
     { 
      //load from database and return all products 
     } 
    } 

Узор завода для ситуаций, когда вам нужны несколько реализаций интерфейса с различной функциональностью в конкретных объектах и ​​вам нужен способ, чтобы выбрать правильный. Например, если у вас есть приложение, в котором вы пытаетесь вычислить область различных фигур, у вас может быть интерфейс IShapes с функцией calculateArea() и несколькими классами, которые его реализуют (например, Circle, Triangle, Rectangle и т. Д.) С все используют разные формулы для вычисления площади формы. Затем вы можете использовать фабрику для создания и получения правильной реализации для определенного имени формы для общего набора параметров.

редактировать: Если есть что-то функционально отличается от различных видов продукции, например, точки пути вознаграждения рассчитывается, вы могли бы сделать что-то вроде этого:

class ProductFactory 
    { 
     public Iproduct getProduct($productType, $productInfo, $images) 
     { 
      switch(productType) 
      { 
       case: featured 
        return new featuredProduct($productInfo) 
       case: standard 
        return new standardProduct($productInfo) 
      } 
     } 
    } 

    Interface Iproducts 
    { 
     //suppose different product types have different reward point formula's 
     calculateRewardPoints(); 

     .... 
     //other functions 
    } 

которые затем могут быть использованы в репо продукции выше примерно так:

private function setupProducts($items) 
    { 
     foreach($items as $item){ 
     $images = $this->getImages($product->getId()); 

      $product = ProductFactory.getProduct($item.type, $item, $images); 

      $products[] = $product; 
    } 
+0

спасибо за быстрый ответ. Я думаю, что хорошо держать фабрику. Например, если мне нужно создать базовый продукт или продукт, зависящий от него, тогда было бы хорошо, если бы productfactory решила, какой именно. Как вы думаете? – Laxman

+0

Если вы хотите обработать его таким образом, вы хотите, чтобы ваш интерфейс каждый абстрагировал каждый элемент продукта. Есть ли что-то функционально другое между обычным продуктом и отличным продуктом или они отображаются только по-разному? Если они отображаются только по-разному, вы можете использовать фабрику, чтобы выбрать, какой частичный вид/стиль рендеринга использовать в зависимости от того, включен ли продукт, который может быть просто свойством класса продукта. – Sworhard

Смежные вопросы