Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
OOP可视性,一般而言,特别是在PHP中_Php_Oop_Visibility - Fatal编程技术网

OOP可视性,一般而言,特别是在PHP中

OOP可视性,一般而言,特别是在PHP中,php,oop,visibility,Php,Oop,Visibility,这可能是一个普通的OOP问题,但我使用的是PHP,所以如果它普遍存在,我想知道,但如果您知道它在PHP中丢失或扭曲,请给我警告 实例化另一个类(它没有扩展)的对象的类是否可以设置实例化对象属性的可见性 例如: Class Widget { public $property1; public $property2; public $property3; } Class ClockMaker { public function getWidget() {

这可能是一个普通的OOP问题,但我使用的是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设置为protected,ClockMaker将无法设置该值。但如果它现在是这样的话,$my_clock可以和widget对象保持一致


设置属性后,是否仍要防止该属性被设置?

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

Class ClockMaker {
  private $widget;

  public function getWidget() {

      $this -> widget = new Widget();
      $this -> widget -> property1 = "special stuff";
   }
}
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

为什么不使用get_uu和set_uu方法?看起来它应该能解决你的问题。这些也是OOP中在对象本身之外播放对象属性的最佳实践

class Widget {
  private $foo;

  public function setFoo($val) { $this->foo = $val; )
  public function getFoo() { return $this->foo; )
}
您还问,“是否要阻止设置属性后再设置属性?”


如果你在找别的东西,你能把问题说得更具体一点吗?:)

如果一个类声明了公共的东西,那么它就是公共的。如果您不希望类成员是公共的,那么应该将其声明为protected或private。除非您求助于内省和反射(这是非常不鼓励的,因为它通常会导致意大利面代码,并对性能造成致命影响),否则更改成员可见性的唯一方法是继承子类中的类,并使用新的可见性重新声明成员。当你这样做的时候,你只能使一个成员比以前更可见(私人成员可以公开,但不能反过来)

一般来说,您通常不希望将属性(变量)公开,因为如果这样做,外部代理可以随意更改类的状态,而无需任何控制,并且很快就会陷入混乱。在您的示例中,如果不希望外部代理更改属性2,则需要将其设置为受保护或私有

但是,如果外部代理仍然需要访问它,那么您应该为此作业提供getter和setter方法。getter返回属性的值,setter允许以可控的方式对其进行更改。getter和setter允许您执行使属性为只读的操作,或者实现延迟初始化(这意味着如果类需要执行一些昂贵的操作来 初始化一个属性,例如DB查找,然后可以将其延迟到实际需要该值为止)

如果需要将property2设置为只读,那么只需完全省略setter(或者,如果仍然需要类中的验证功能,则将其设置为非公共)。由于无法从外部直接访问属性,因此getter和setter让您完全控制进入属性的内容以及如何再次输出属性

顺便说一句,在大多数情况下,初始化类所依赖的类被认为是一种不好的做法。它使单元测试变得更加困难(因为你不能单独测试一个初始化自己依赖项的类),也会使你的代码变得不那么清晰(因为隐藏在类中的依赖项对于只使用公共API作为指导的类来说并不是很明显)。它还剥夺了您的一些灵活性,因为如果一个类初始化它自己的依赖对象,那么您就不能用它们替换提供相同接口的其他对象。该规则存在异常(例如在发生错误时生成异常),但作为一般规则,类内部不应包含
new
语句,也不应使用注册表或单例类拉入对象。它们还应该避免对其他类的静态调用

这被认为是一个更好的方法

Class Clockmaker 
{
    protected $widget;

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

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

缺少getWidget()是一个疏忽,缺少属性更改是因为我不知道它会是什么样子(这就是问题所在)。第一个错误已经更正。在您的示例中,
$my_clock
将无法访问小部件,对吗?我想要(事实上,需要)允许某些属性是公共的和可写的,而其他属性是受保护的但可见的。但是我觉得你的建议离我更近了一步,谢谢。这样,每次你从getWidget方法获得一个小部件时,以前的小部件都会被覆盖。如果你想创建一个新的小部件,我建议将其命名为createWidget以便于阅读。如果你只是想要返回小部件,不要创建新实例,而是在构造函数中创建实例并返回该实例。
Class Clockmaker 
{
    protected $widget;

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