Как сломать и улучшить циклическую зависимость без использования шаблона прокси?

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

У меня есть классы 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. Это хорошая практика, чтобы зависеть от нескольких заводов?

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

2 ответа

Решение

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

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

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 с функцией calcArea() и несколько классов, реализующих его (например, 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;
    }
Другие вопросы по тегам