我有一个
Factory Method
来实例化一个类。有没有办法阻止这个类直接实例化?
我看到的唯一选择是使用传递到
__construct()
的参数,但这不是我正在寻找的东西。
另一方面,将
__construct()
设为私有将是理想的选择,但我不希望 MyClass
在没有实际需要的情况下扩展 Factory
。
大家觉得怎么样?
工厂方法:
class Factory
{
public static function instance()
{
return new MyClass(true);
}
}
我的班级:
class MyClass
{
public function __construct($isFactory = false)
{
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
}
有一些技巧可以做到这一点:
protected
构造函数@internal
之类的内容来记录 API,然后按照合同将其留给客户。
本质上,你的代码应该是这样的:
工厂
<?php
class Factory {
public static function instance(){
return new MyClass(true); //HERE YOU ARE INSTANTIATING
}
}
通过工厂实例化的类
<?php
//NOT MyClass() <--- YOU ARE DEFINING.... NOT INSTANTIATING...
class MyClass {
public function __construct($isFactory = false) {
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
//...MORE METHODS
}
你可以试试这个吗?
<?php
class Factory
{
private static $FACTORY_GUARANTOR; //ONLY SET DURING INSTANTIATION
public static function instance($type) {
if (class_exists($type)) {
self::$FACTORY_GUARANTOR = 1;
$instance = new $type();
self::$FACTORY_GUARANTOR = null;
return $instance;
}
else {
throw new Exception("Class not found...");
}
}
//YOU CAN GET $FACTORYGUARANTOR EXTERNALLY BUT NEVER SET IT;
public static function getGuarantor(){
return self::$FACTORY_GUARANTOR;
}
}
class MyClass {
protected $property1;
protected $property3;
protected $property2;
public function __construct() {
// IF SOMEONE TRIES TO INSTANTIATE THE CLASS OUTSIDE OF THE FACTORY... BLOW A WHISTLE
if(!Factory::getGuarantor()){
throw new Exception('Use Factory::instance() to create an object');
}
// IF THE PROGRAM MADE IT TO THIS POINT;
// JUST INSTANTIATE THE CLASS BECAUSE MOST LIKELY IT IS COMING FROM THE FACTORY
var_dump($this); // A LITTLE CONFIRMATION....
}
//...MORE METHODS
}
// TRY IT OUT:
/*INSTANCE A: RIGHT*/ $theClass = Factory::instance("MyClass"); //INSTANTIATES THE CLASS
/*INSTANCE B: WRONG*/ $theClass = new MyClass(); //THROWS AN EXCEPTION
最简单的方法是将基类定义为
abstract
。 abstract
类不能直接实例化,因此您必须在继承的类中重新定义它们的抽象成员:
abstract class Factory
{
abstract public function foo();
}
class InheritedClass extends Factory
{
public function foo()
{
// Do something
}
}
// $obj1 = new Factory(); // Will produce an error
$obj1 = new InheritedClass(); // Will be executed successfully
您可以在此处阅读有关
abstract
类的更多信息:PHP:类抽象 - 手册。
对我来说,最好的方法是使用ReflectionClass:
class MyClass
{
public const FRIEND_CLASSES = [Factory::class];
protected function __construct() {}
}
trait Constructor
{
protected function createObject(string $className, array $args = [])
{
if (!in_array(static::class, $className::FRIEND_CLASSES)) {
throw new \Exception("Call to private or protected {$className}::__construct() from invalid context");
}
$reflection = new ReflectionClass($className);
$constructor = $reflection->getConstructor();
$constructor->setAccessible(true);
$object = $reflection->newInstanceWithoutConstructor();
$constructor->invokeArgs($object, $args);
return $object;
}
}
class Factory
{
use Constructor;
public function MyClass(): MyClass
{
return $this->createObject(MyClass::class);
}
}
在常量 FRIEND_CLASSES 中,您可以定义可以在哪些类中实例化该类。
使用trait是因为该功能可以在不相关的不同工厂中使用。
如果需要将参数放入类的构造函数中,请将它们作为createObject的第二个参数。
详细内容我在《禁止在 PHP 中在工厂外创建对象》一文中描述过
匿名类就是答案:
<?php
namespace Paranoid;
interface Wei
{
function get_wei_str(): string;
}
class Factory {
/**
* makeWei
*
* @param string $wei
* @return Wei
*/
static function makeWei($wei)
{
return new class($wei) implements Wei
{
private $wei;
function __construct(string $wei)
{
$this->wei = $wei;
}
function get_wei_str(): string
{
return $this->wei;
}
};
}
}