我知道大多数OOP语言(如果不是全部的话)中的私有可见性在类的基础上定义了隐私,即同一类的不同实例可以访问彼此的私有属性/方法。
我想防止这种情况发生,我想知道什么是最好的设计/实现,以便在不影响性能的情况下实现。
例如,我知道我可以实现AOP并使用注释,但这会导致性能下降,因为语言引擎必须创建类的反射并检查注释。所以,基本上,我的问题是,避免同一类的实例访问彼此的私有方法/属性的最佳方法是什么?
示例:
class Product
{
private $_prize;
public function __construct($prize)
{
$this->_prize = $prize;
}
public function calculateDiscount(Product $extraProduct)
{
$extraProduct->_prize = 0; //How to avoid this?
}
}
$productA = new Product(10);
$productB = new Product(25);
$productA->calculateDiscount($productB);
简单地说,不要编写访问其他实体私有的代码。可见性修改器可以帮助你不要太容易地射中自己的脚。它们不是锁和钥匙。您仍然可以通过多种方式"规避"访问保护"。做一个负责任的成年人,不要修改属性,除非你在它之前写了一个$this->
。
您也可以使用ReflectionClass
实现这一点
class Product
{
private $_prize;
public function __construct($prize)
{
$this->_prize = $prize;
}
public function calculateDiscount(Product $extraProduct)
{
if(!(new ReflectionClass($extraProduct))->getProperty('_prize')->isPrivate()){
$extraProduct->_prize = 0; //How to avoid this?
} else {
echo "Is private property";
}
}
}
$productA = new Product(10);
$productB = new Product(25);
$productA->calculateDiscount($productB);
我不确定,但:
class Product
{
private $_prize;
public function __construct($prize)
{
$this->_prize = $prize;
}
public function calculateDiscount(Product $extraProduct)
{
$extraProduct->setPrize(0);
}
public function setPrize( $v ) {
$this->_prize = $v;
}
}
$productA = new Product(10);
$productB = new Product(25);
$productA->calculateDiscount($productB);
如果没有getter和setter,就不要访问任何属性。然后在getter和setter中,通过(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT)[0]["object"] ?? null) === $this
检查调用上下文是否是同一个类。
例如:
class Foo{
private $bar;
public function getBar(){
return $this->bar;
}
private function setBar($bar){
self::assertCalledByThis();
$this->bar = $bar;
}
private static function assertCalledByThis(){
$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS);
$fromObject = $trace[1]["object"] ?? null; // context calling setBar()
$toObject = $trace[0]["object"] ?? null; // context calling assertCalledByThis()
assert($fromObject === $toObject);
}
}
当然,您的getBar
和setBar
可以用__get()和__set()代替,但您不能声明字段,否则将不会调用魔术方法。