2012-01-30 65 views
0

这可能是一个普通的面向对象的问题,但我使用PHP,所以如果它一般存在,我想知道,但给我警告,如果你知道它缺少或在PHP中扭曲。面向对象的可见性,一般来说,特别是在PHP

是否有可能实例化另一个类(它不扩展)的对象来设置实例化对象的属性的可见性?

例子:

Class Widget { 

    public $property1; 
    public $property2; 
    public $property3; 
    } 

Class ClockMaker { 

    public function getWidget() { 

     $this -> widget = new Widget(); 
     $this -> widget -> property1 = "special stuff"; 
    } 
} 

$my_clock = new ClockMaker(); 
$my_clock -> getWidget(); 
$my_clock -> widget -> property2 = "my tweak"; // Totally fine and expected... 
$my_clock -> widget -> property1 = "my stuff"; // Should throw an error... 

据我了解,如果我设置property1来保护,钟表匠将无法设定值。但是,如果现在看来,$ my_clock可以通过widget对象实现。

无论如何,以防止财产被设置一旦设置?

回答

1

您的代码不会更改任何属性,getWidget也不会被调用。它将在您的$my_clock对象中创建widget属性,然后从空值创建另一个对象(如果您启用严格错误,则会看到严格的通知)。在您的课程中声明widget

Class ClockMaker { 
    private $widget; 

    public function getWidget() { 

     $this -> widget = new Widget(); 
     $this -> widget -> property1 = "special stuff"; 
    } 
} 
+0

缺乏getWidget()是一个疏忽,缺乏财产变化是因为我不知道它会是什么样子(因此是问题)。第一个错误已被更正。在你的例子中,'$ my_clock'不会有任何对widget的访问,是吗?我想(事实上需要)允许一些属性是公开和可写的,而其他属性却是可见的。但是,我更接近您的建议,谢谢。 – Anthony 2012-01-30 07:24:09

+0

这样,每次从getWidget方法获取小部件时,以前的小部件都会被覆盖。如果你想创建一个新的小部件,我建议将它命名为createWidget以提高可读性。如果您只想返回小部件,请不要创建新实例,而是在构造函数中创建实例并返回该实例。 – 2012-01-30 07:29:06

0

为什么不使用get_和set_方法?似乎它应该可以解决你的问题。这些也是OOP中用于播放对象本身之外的对象属性的最佳实践。

class Widget { 
    private $foo; 

    public function setFoo($val) { $this->foo = $val;) 
    public function getFoo() { return $this->foo;) 
} 

您还问:“无论如何要防止财产被设置一旦设置?”

class Widget { 
    private $foo; 

    public function setFoo($val) { 
    if (!isset($this->foo)) { 
     $this->foo = $val; 
    } 
    //If you want to throw an error or something just use an else clause here. 
    } 
    public function getFoo() { return $this->foo;) 
} 

$w = new Widget(); 
$w->setFoo('Foooooo'); 
echo $w->getFoo(); //Foooooo 
$w->setFoo('Magic.'); 
echo $w->getFoo(); //Foooooo 

如果你正在寻找别的东西,你能否提出更具体的问题? :)

0

如果一个类声明了一些公开的东西,那么它是公共的。如果你不打算让这个集体成员公开,那么它应该被宣布为受保护或私有。除非你诉诸内省和反思(由于它通常会导致意大利面条代码并且会导致死亡而不鼓励性能),所以你可以改变一个成员的可见性的唯一方法就是在子类中继承这个类,并重新声明这个成员新的知名度。当你这样做时,你只能让一个成员变得更加明显,然后才可以(私人成员可以成为公众,而不是相反)。

一般来说,您通常不希望公开属性(变量),因为如果您这样做,那么外部代理可以随意更改该类的状态,而无需任何控制,并且您可以快速进入大混乱。在你的例子中,如果你不希望外部代理改变它,你需要使property2受保护或私人。

但是,如果外部代理仍然需要访问它,那么您应该为此作业提供getter和setter方法。 getter返回属性的值,setter让它以受控的方式被改变。 Getters和setter可以让你执行诸如使属性只读或者实现延迟初始化(这意味着如果类需要执行一些昂贵的操作来初始化诸如DB查找之类的属性,那么它可以被推迟到实际需要的值)

class Widget() 
{ 
    protected $property2; 

    public function getProperty2() 
    { 
     // This is an example of lazy initialization in a getter. 
     if ($this -> property2 === NULL) 
     { 
      $this -> property2 = 'Value has been initialized' 
     } 
     return ($this -> property2); 
    } 

    public function setProperty2 ($newProp) 
    { 
     // Example of input validation 
     if {($newProp !== NULL) && ($newProp !== 'This is an illegal value!')} 
     { 
      $this -> property2 = $newProp; 
     } 
     else 
     { 
      throw new InvalidArgumentException; 
     } 
     return ($this); 
    } 
} 

如果您需要property2进行只读,那么就干脆省略的setter(或使其非公如果你还需要你的类中的验证功能)。由于无法直接从外部访问该属性,因此您的获取者和设置者可以完全控制进入该属性的内容以及该属性如何再次出现。

顺便提一下,在大多数情况下,对于类来说,初始化它所依赖的类是不好的做法。它使得单元测试更加困难(因为你不能测试一个独立初始化它自己的依赖的类)并且可以让你的代码变得更加清晰(因为隐藏在类内部的依赖对于使用你的类的人来说并不明显只有公共API作为指导)。它也会让你失去一些灵活性,因为如果一个类初始化了它自己的依赖对象,那么你就不能用它们替代提供相同接口的其他对象。规则有例外(例如发生错误时生成异常),但作为一般规则,类不应在其中包含new语句,并且不应将对象与Registry或Singleton类一起提取。他们也应该避免对其他类的静态调用。

改为使用Dependency Injection代替它是个好主意。

Class Clockmaker 
{ 
    protected $widget; 

    public function __construct (Widget $newWidget) 
    { 
     $this -> widget = $newWidget; 
    } 
} 

为什么这样更好?现在的答案很明显,你的ClockMaker类依赖于一个Widget类,所以它提高了可读性。这也意味着您可以使用任何Widget子类的对象代替Widget本身,而不会出现问题。如果您要进行单元测试,您可以将Widget替换为所有方法始终返回相同值的子类,因此您现在可以单独测试ClockMaker类(如果在测试过程中发生错误,则可以确定错误是在ClockMaker中,使用ClockMaker初始化Widget本身,您不知道测试失败是由于Clockmaker中的错误还是Widget中的错误)。