工厂方法:防止 class 直接实例化
Factory Method: Prevent a class from Direct Instancing
我有一个 Factory Method
来实例化一个 class。有没有办法防止这个 class 直接实例化?
我看到的唯一选择是使用传递给 __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');
}
}
}
本质上,您的代码应该是这样的:
工厂
<?php
class Factory {
public static function instance(){
return new MyClass(true); //HERE YOU ARE INSTANTIATING
}
}
CLASS 将通过工厂实例化
<?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
有技巧可以做到这一点:
- 滥用继承来使用
protected
构造函数
- 将工厂方法放在class中,这样它就可以调用私有构造函数,这实际上不是hack。但是为什么不首先使用构造函数呢?
- 使用反射访问私有构造函数
我不是在推销任何东西。我个人所做的是用 @internal
之类的东西记录 API,然后根据合同将其留给客户。
最简单的方法是将基数 class 定义为 abstract
。 abstract
classes不能直接实例化,所以你必须在继承的classes中重新定义它们的抽象成员:
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
class 的更多信息:PHP: Class Abstraction - Manual。
对我来说,最好的方法是使用 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 中,您可以定义在 class 中可以实例化 class。
使用 trait 是因为此功能可用于不相关的不同工厂。
如果您需要将参数放入 class 的构造函数中,请将它们作为 createObject 的第二个参数。
中描述的细节
我有一个 Factory Method
来实例化一个 class。有没有办法防止这个 class 直接实例化?
我看到的唯一选择是使用传递给 __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');
}
}
}
本质上,您的代码应该是这样的:
工厂
<?php
class Factory {
public static function instance(){
return new MyClass(true); //HERE YOU ARE INSTANTIATING
}
}
CLASS 将通过工厂实例化
<?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
有技巧可以做到这一点:
- 滥用继承来使用
protected
构造函数 - 将工厂方法放在class中,这样它就可以调用私有构造函数,这实际上不是hack。但是为什么不首先使用构造函数呢?
- 使用反射访问私有构造函数
我不是在推销任何东西。我个人所做的是用 @internal
之类的东西记录 API,然后根据合同将其留给客户。
最简单的方法是将基数 class 定义为 abstract
。 abstract
classes不能直接实例化,所以你必须在继承的classes中重新定义它们的抽象成员:
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
class 的更多信息:PHP: Class Abstraction - Manual。
对我来说,最好的方法是使用 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 中,您可以定义在 class 中可以实例化 class。
使用 trait 是因为此功能可用于不相关的不同工厂。
如果您需要将参数放入 class 的构造函数中,请将它们作为 createObject 的第二个参数。
中描述的细节