如何在不使用代理模式的情况下打破和改善循环依赖?

时间:2015-12-25 17:48:35

标签: design-patterns solid-principles cyclic-dependency

我的课程依赖于太多其他课程,我找不到改进它的方法。问题如下:

我有一个ProductRepo,ProductFactory和一个ImageFactory类。 ProductRepo在products表上执行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

    要跳过此步骤,我可以使用setter注入或使用代理模式。但我认为这不是一个好的解决方案。你们是如何处理这类问题的?

  2. ProductRepo取决于ProductFactory和ImageFactory。这是一个依赖多个工厂的好习惯吗?
  3. 我认为问题很明显。 :) 谢谢

2 个答案:

答案 0 :(得分:2)

根据我所知道的,您不需要工厂模式,而不是不同类型的图像和产品类别,您只有一个产品类别具有不同的细节。

我建议使用构造函数创建单个产品类,该构造函数接收来自产品数据库的单行信息以及属于它的图像集合。然后构造函数可以设置产品类。

然后,在产品仓库类中创建一个产品集合或数组,并返回该产品。

像这样(用伪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
        }
    }

工厂模式适用于需要在具体对象中具有不同功能的接口的多个实现的情况,并且您需要一种方法来选择正确的对象。例如,如果你有一个应用程序,你试图计算各种形状的面积,你可能有一个带有calculateArea()函数的IShapes接口和几个实现它的类(例如,圆形,三角形,矩形等)所有人都使用不同的公式来计算形状的面积。然后,您可以使用工厂为一组通用参数构造和获取特定形状名称的正确实现。

编辑: 如果不同产品类型之间存在功能差异,例如奖励积分的计算方式,您可以执行以下操作:

    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;
    }

答案 1 :(得分:1)

有几种方法可以打破循环依赖,但最有趣的问题似乎是ProductFactory需要一个ProductRepo,它本身必须能够构建产品,即使这个功能不会被使用而且它可能没有&#39 ; t传递使用不同工厂的ProductRepo(隐藏规则)是有意义的。所以:

1)创建一个只有getImages方法的ImageRepositoryInterface。 ProductRepositoryInterface可以扩展此接口,也可以ProductRepo独立实现。然后,将图像存储库传递给ProductFactoryInterface.make,而不是在构造时要求它。您可以在此时传递ProductRepo。

2)是的,取决于多种工厂,没有问题