正在寻找装载机设计模式


Looking for a loader design pattern

假设我有以下类,它们的实例将其属性存储在JSON文件或数据库中的某个地方

Foo类

abstract class Foo
{
    protected $ID;
    protected $title;
    // getters and setters
}

Class Bar(扩展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);
    }
}

和抽象加载器类

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

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

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

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

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

我想说,对于一个好的设计,你最终会在代码中使用不同的设计模式,所以通常,模板方法、依赖注入、工厂模式以及封装和数据隐藏等概念才是诀窍。尝试封装行为并使用数据隐藏,不要让对象泄露内部信息,因此请参阅德米特定律。以下是一些示例设计:

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

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

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

祝你好运。