【问题标题】:Looking for a loader design pattern寻找加载器设计模式
【发布时间】:2015-04-27 04:07:39
【问题描述】:

假设我有以下类,它们的实例存储它们的属性 somewhere for e.x.在 JSON 文件或数据库中:

类 Foo

abstract class Foo
{
    protected $ID;
    protected $title;

    // getters and setters
}

类栏(扩展 Foo)

class Bar extends Foo
{
    protected $text;

    // getters and setters
}

Baz 类(也扩展 Foo)

class Baz extends Foo
{
    protected $tabs = array();

    // getters and setters
}

从数据源加载它们的最佳方式是什么?

我现在怎么做(不满意)

我的抽象类Foo 有一个方法load($ID, PDO $pdo)。此方法被BarBaz 覆盖,这扩展了Foo 类中title 属性的一般加载,具有必须加载的它们自己的属性。

所以在代码中它看起来像这样:

类 Foo

abstract class Foo
{
    protected $ID;
    protected $title;

    public static function load($ID, PDO $pdo)
    {
        $result = null;

        // SQL query for getting $title property into $result

        return $result;
    }

    // getters and setters
}

Bar 类中,我这样做:

class Bar extends Foo
{
    protected $text;

    public function __construct(stdClass $data)
    {
        $this->ID = $data->ID;
        $this->text = $data->text;
    }

    public static function load($ID, $pdo)
    {
        $generalInfo = parent::load($ID, $pdo);

        $result = null;

        // PDO query for getting $text property into $result

        $generalInfo->text = $result;

        return $generalInfo;
    }

    // getters and setters
}

所以这让我调用 $dataToCreateInstance = Bar::load(4, $pdoInstance) 并返回所需的信息来实例化 ID 为 4 的特定 Bar 对象。

这里的问题是(如您所见)我的类绑定到 PDO,而且为每个数据源实现 load 方法真的很难看,所以它根本不是通用的。

我想要达到的目标

我现在正在寻找一种模式,可以让我从任何来源加载这些类。我想到了一个看起来像这样的Loader 类:

class LoaderManager
{
    /**
     * @var Loader[]
     */
    protected $loaders = array();

    public function registerLoader($className, $loaderClass)
    {
        $this->loaders[$className] = $loaderClass;
    }

    public function loadClass($class, $ID)
    {
        return $this->loaders[$class]->load($ID);
    }
}

还有一个抽象的 Loader 类

abstract class Loader
{
    public abstract function load($ID);
}

所以现在我已经解耦了。这个尝试的问题是我总是为类本身提供一个额外的Loader。所以对于Bar 类,我必须至少提供BarLoaderPDOBarLoaderJSON 之一。这在某种程度上不是那么优雅,感觉有点“错误”。

还有一个问题是我必须将当前应用程序中的哪个类必须使用哪个加载器的映射存储在某个地方。

但这是我能想到的唯一尝试,它使我达到了我想要实现的目标。

我现在想讨论和听听是否有其他(更好的)尝试以及如何实现它们。

【问题讨论】:

    标签: php design-patterns logic


    【解决方案1】:

    我想说,对于一个好的设计,你最终会在你的代码中使用不同的设计模式,所以通常,模板方法、依赖注入、工厂模式和诸如封装和数据隐藏之类的概念是诀窍。尽量封装行为并使用数据隐藏,不要让对象泄露内部信息,看Demeter´s Law。这里有一些示例设计:

    namespace DesignPatterns.LoaderPattern
    {
    public interface IBusiness
    {
        void Operation1();
    }
    
    public abstract class BusinessTemplate : IBusiness
    {
        protected readonly string title;
        protected readonly int id;
    
        protected BusinessTemplate(int id, string title)
        {
            this.id = id;
            this.title = title;
        }
        public abstract void Operation1();
    }
    
    public class FooBusinessConcrete : BusinessTemplate
    {
        public FooBusinessConcrete(int id, string title) : base(id, title)
        {
    
        }
    
        public override void Operation1()
        {
            throw new NotImplementedException();
        }
    }
    
    public class BarBusinessConcrete : BusinessTemplate
    {
        private readonly string text;
    
        public BarBusinessConcrete(int id, string title, string text) : base(id, title)
        {
            this.text = text;
        }
    
        public override void Operation1()
        {
            //Do something
        }
    }
    
    public sealed class BazBusinessConcrete : BarBusinessConcrete
    {
        private readonly string[] arrayString;
    
        public BazBusinessConcrete(int id, string title, string text, string[] arrayString) : base(id, title, text)
        {
            this.arrayString = arrayString;
        }
    
        public override void Operation1()
        {
            //Do something
        }
    }
    
    /// <summary>
    /// Create an itnerface for a factory that will return a single object
    /// </summary>
    public interface IBusinessFactory
    {
        IBusiness CreateBusiness();
    }
    
    /// <summary>
    /// Create a factory that will create the concrete type via constructor injection, 
    /// so for each type you will create a single constructor
    /// </summary>
    public sealed class ConcreteFactory : IBusinessFactory
    {
        protected IBusiness business;
        public ConcreteFactory(int id, string title)
        {
            business = new FooBusinessConcrete(id, title);
        }
    
        public ConcreteFactory(int id, string title, string text)
        {
            business = new BarBusinessConcrete(id, title, text);
        }
    
        public ConcreteFactory(int id, string title, string text, string[] arrayString)
        {
            business = new BazBusinessConcrete(id, title, text, arrayString);
        }
    
        public IBusiness CreateBusiness()
        {
            return business;
        }
    }
    
    /// <summary>
    /// In Application code use dependency injection of the encapsulated business object
    /// </summary>
    public class ApplicationCode
    {
        private readonly IBusiness business;
    
        public ApplicationCode(IBusiness business)
        {
            this.business = business;
        }
    
        public void Execute()
        {
            business.Operation1();
        }
    }
    
    /// <summary>
    /// So from your client code you can use a factory that create the business object with encapsulated interface, behavior
    /// </summary>
    public class ClientCode
    {
        public void RunType1(int id, string title)
        {
            var business = new ConcreteFactory(id, title).CreateBusiness();            
            new ApplicationCode(business).Execute();
        }
    
        public void RunType2(int id, string title, string text)
        {
            var business = new ConcreteFactory(id, title, text).CreateBusiness();
            new ApplicationCode(business).Execute();
        }
    
        public void RunType3(int id, string title, string text, string[] arrayString)
        {
            var business = new ConcreteFactory(id, title, text, arrayString).CreateBusiness();
            new ApplicationCode(business).Execute();
        }
    }
    
    }
    

    【讨论】:

      【解决方案2】:

      你可以实现它。 请参阅 CodeIgnitor 中的 Loader 类实现。 您还需要使用自动加载类并从变量动态创建对象的实例。

      这里是可能实现的链接。 https://github.com/bcit-ci/CodeIgniter/blob/develop/system/core/Loader.php

      祝你好运。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多