PHP 设计模式:私有构造函数是不是不利于您让其他人扩展的类?

Posted

技术标签:

【中文标题】PHP 设计模式:私有构造函数是不是不利于您让其他人扩展的类?【英文标题】:PHP Design Patterns: Are private constructors bad for classes that you will let others to extend?PHP 设计模式:私有构造函数是否不利于您让其他人扩展的类? 【发布时间】:2012-01-16 11:18:53 【问题描述】:

我有一个名为 ContentAbstract 的抽象类,看起来像这样

abstract class ContentAbstract

  protected static $type;
  protected $id;
  protected $title;
  protected $description;

  protected $page;
  protected $section;
  ...

  function __construct($id = NULL, Page $page = NULL, Section $section = NULL)
  
    if($id != NULL)
    
      $data = get_data_from_content_table_by_id($id);

      if($data['type'] == static::$type)
      
        initialize_fields_with_data($data);

        $this->page = fetch_page_object_from_registry($data['page_id']);
        $this->section = fetch_section_object_from_registry($data['section_id']);
      
      else
        throw new IncompatibleContentTypeException('Foo');
    
    else if($page != NULL && $section != NULL)
    
      $this->page = $page;
      $this->section = $section;
    
    else
      throw new OphanContentException('Foo');
  

那么Page类也是ContentAbstract的子类

class Page extends ContentAbstract

  protected static $type = 'Page';

  private $template_file;
  private $short_name;

  static function newFromName($name)
  
    $data = get_content_id_from_page_table_using_the_short_name($name);
    $page = new Page($data['id']);
    $page->template_file = $data['template_file'];
    ...
  

  static function newFromID($id)
  
    $data = get_content_id_from_page_table_using_the_ID($id);
    $page = new Page($data['id']);
    $page->template_file = $data['template_file'];
    ...
  

现在我的问题在于 Page 构造函数是公共的,用户可以这样做:

$page = new Page($valid_page_id);

并最终调用 ContentAbstract::__construct() 但无法初始化页面本身的数据(template_fileshort_name),因为它是在 Page::newFromName()Page::newFromID() 之外调用的。所以我最终得到了一个半熟的内容数据。一种解决方案是用类似于Page::newFromID() 的东西覆盖父构造函数,确保我们能够在Page 被实例化时设置所有字段(当然仍然在Page::__construct() 中调用父构造函数)。

现在问题出在Page::newFromName()方法上,因为这种方法需要我进行2次查询,一个是使用short_name列获取页面的内容id,然后当页面的构造函数在Page::newFromName() 中调用,然后它将创建一个新查询来获取与页面关联的数据。这不是可取的,不是吗?

所以我看到的唯一解决方案是将Page::__construct() 设为私有并强制最终用户使用静态方法来实例化对象。

我的问题是,这是我想作为一个开源项目发布的东西,它允许用户通过简单地继承 ContentAbstract 类来添加更多类型的内容。是否要求私有构造函数不利于上述目标(考虑人为错误和懒惰阅读文档)?或者这些事情应该是我最不关心的事情?还是实际类的结构本身导致了这个问题?

【问题讨论】:

Ahhhh...但是我怎么知道构造函数是否是从类本身调用的呢? 我意识到我误读了您想要做的事情,因此我的建议被误导了——对于那个偏离主题的问题感到抱歉。为了不混淆任何人,我删除了我的 cmets! 【参考方案1】:

“现在我的问题在于Page构造函数是公共的,用户可以这样做:

$page = new Page($valid_page_id);"

根据您在 OP 中发布的代码,情况并非如此。您的页面扩展了构造所在的 ContentAbstract,但您实际上并未从 Page 类调用构造。您将不得不强制从孩子呼叫父母:

class Page extends Content // I wouldn't name this abstract as it may change in the future

    public function __construct($args)
    
        // Here I forced a call to parent
        // If I comment this out, the parent construct will never be accessed
        parent::__construct($args); 
    

【讨论】:

是的,问题是我从未重写父构造函数,所以Page 构造函数也是公共的。因此,只要我在new Page($id) 中传递一个有效的ID,脚本就不会抱怨。但是,由于 Page 的所有属性的初始化都是在静态方法中完成的,因此只有继承的属性会这样设置。 @RolandoCruz:当然。当我实现一个像你这样的结构时,我总是覆盖它。 @RolandoCruz:刚刚有了一个想法,为什么不将 ContentAbstract::__construct() 的内容移动到 protected init() 方法中。这样在实例化的时候不能直接访问代码,而你的静态方法仍然可以初始化成员变量? @RolandoCruz:你最终做了什么来解决你的问题?

以上是关于PHP 设计模式:私有构造函数是不是不利于您让其他人扩展的类?的主要内容,如果未能解决你的问题,请参考以下文章

PHP设计模式-单例模式

php设计模式之单例模式

PHP单例模式 要点

PHP设计模式

为啥我们需要私有构造函数?

在 PHP5 类中,何时调用私有构造函数?