我有一个名为 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 构造函数是公共(public)的,用户可以这样做:
$page = new Page($valid_page_id);
并最终调用 ContentAbstract::__construct() 但无法初始化页面本身的数据(template_file 和 short_name) 因为它是在 Page::newFromName() 和 Page::newFromID() 之外调用的。所以我最终得到了一个半生不熟的内容数据。一种解决方案是使用类似于 Page::newFromID() 的方法覆盖父构造函数,确保我们能够在实例化 Page 时设置所有字段(当然,仍然通过在 Page::__construct()) 中调用父构造函数。
现在问题出在 Page::newFromName() 方法中,因为这种方法需要我进行 2 个查询,一个是使用 获取页面的内容 ID short_name 列,然后当在 Page::newFromName() 中调用页面构造函数时,它将创建一个新查询以获取与页面关联的数据。这是不可取的,不是吗?
所以我看到的唯一解决方案是将 Page::__construct() 设为私有(private)并强制最终用户使用静态方法来实例化对象。
我的问题是,这是我想作为开源项目发布的东西,它允许用户通过简单地子类化 ContentAbstract 类来添加更多类型的内容。要求私有(private)构造函数是否不利于上述目标(考虑到人为错误和懒惰阅读文档)?或者这些事情应该是我最不关心的?还是实际类的结构本身导致了这个问题?
最佳答案
“现在我的问题在于 Page 构造函数是公共(public)的,用户可以这样做:
$page = new Page($valid_page_id);"
根据您在 OP 中发布的代码,情况并非如此。您的页面在构造所在的位置扩展了 ContentAbstract,但您实际上并没有从 Page 类调用构造。您将不得不强制 child 给 parent 打电话:
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);
}
}
关于PHP 设计模式 : Are private constructors bad for classes that you will let others to extend?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8440848/