2016-08-12 44 views
0

可以说我有一个很普通:php,如何防止直接类instantiazation?

class Money 
{ 
    public __construct($actualCountry) 
    { 
     $this->actualCountry = $actualCountry; 
    } 

    public function getValute() 
    { 
     return according to actual country 
    } 
} 

这个类需要创建一次,所以我有一个全球性的工厂:

final class Factory 
{ 
    private $money; 

    public function getMoney() 
    { 
     if ($this->money == null) 
     { 
      $this->money = new Money(Config::getCountryCode()); 
     } 
     return $this->money; 
    } 
} 

,每当我们想用:

Factory::getMoney()-> 

但今天我看到了我的同事试图做的事:

(new Money(Config::getCountryCode()))->getValute(); 

这显然是错误的,不需要多次出现。但是,那么一个班级自己怎么会说“嘿,不要示范我,使用工厂”呢?

我不能把它设置为单,因为那时每次:

Money::getInstance(Config::getCountryCode()); 

是毫无意义的。

但真正的问题不是因为它可能存在多个 - 它的方式我总是必须从配置中通过当前的国家。 Config成为什么GlobalConfig?这就是为什么工厂避免很多参数的传递(如果会有更多的参数Money?)

+1

我认为...使构造函数私人?您的工厂可以创建它,但没有其他的可以吗? –

回答

0

如果我正确地得到您的漂移,你需要的是Abstract Class

正如你所需要的,抽象的类不能直接初始化。

Classes defined as abstract may not be instantiated,.....

+0

不,你错了,那么即使工厂也无法创建它。 –

+1

工厂不能“创造它”,但它可以“扩展”它,这几乎是它应该被起诉的。我的$ 0.02) –

1

有没有办法完全防止外部的实例,因为你的工厂上班,构造函数将必须公开访问,直接或间接的影响。

您可以通过使私人,并添加静态工厂方法类模糊的构造:

class Money 
{ 
    private function __construct($actualCountry) 
    { 
     $this->actualCountry = $actualCountry; 
    } 

    public static function fromFactory($actualCountry) 
    { 
     return new static($actualCountry); 
    } 

    public function getValute() 
    { 
     // return according to actual country 
    } 
} 

final class Factory 
{ 
    private $money; 

    public function getMoney() 
    { 
     if ($this->money == null) 
     { 
      $this->money = Money::fromFactory(Config::getCountryCode()); 
     } 
     return $this->money; 
    } 
} 

或者你可以改变构造函数需要(类型)第二个参数,这是工厂用过的。但是,如果你的工厂使用静态方法这是行不通的,因为它在你的榜样作用:

class Money 
{ 
    public function __construct($actualCountry, Factory $factory) 
    { 
     $this->actualCountry = $actualCountry; 
    } 

    public function getValue() 
    { 
     // return according to actual country 
    } 
} 

final class Factory 
{ 
    private $money; 

    public function getMoney() 
    { 
     if ($this->money == null) 
     { 
      $this->money = new Money(Config::getCountryCode(), $this); 
     } 
     return $this->money; 
    } 
} 
2

我认为你应该再考虑Singleton模式。它更适合你想要的逻辑。

<?php 
class Money 
{ 
    private static $instance; 

    private function __construct($countryCode) 
    { 
     #your code here... 
    } 

    /** 
    * Do not include parameter for getInstance. 
    * Make the call internally. 
    * Now when you have to change Config to GlobalConfig will be painless. 
    */ 
    public static function getInstance() 
    { 
     if (null === self::$instance) { 
      return self::$instance = new Money(Config::getCountryCode()); 
     } 

     return self::$instance; 
    } 
} 
2

您可以使构造函数为private或protected以防止直接实例化。 (请参阅提及单例模式的其他答案)。

如果你不能/不想使用单例模式,你必须接受构造函数是public的,或者使用一些封闭绑定魔法来解决它。

声明:这只是一个概念证明。我不会在任何生产代码中使用它。在PHP支持名称空间/类可见性/朋友类之前,没有很好的方法来实现这一点。

class Factory 
{ 
    private static $money; 

    static function getMoney() 
    { 
     if (null === self::$money) { 
      self::$money = self::createInstance('Money', [Config::getCountryCode()]); 
     } 

     return self::$money; 
    } 

    private static function createInstance($class, array $args) 
    { 
     // bind a closure to the given class and call it 
     return call_user_func(Closure::bind(function() use ($class, $args) { 
      // create a new instance without calling the constructor 
      $object = (new ReflectionClass($class))->newInstanceWithoutConstructor(); 

      // call the constructor manually from this context (which has access to it now) 
      call_user_func_array([$object, '__construct'], $args); 

      // return the constructed object 
      return $object; 
     }, null, $class)); 
    } 
} 

使用工厂:

var_dump(Factory::getMoney()); 

输出:

object(Money)#3 (1) { ["actualCountry":"Money":private]=> string(10) "some_value" }

试图直接实例:

输出:

new Money(); 

Fatal error: Call to private Money::__construct() from invalid context in ... on line ...