[英]Accessing non-public members
I am creating domain objects. 我正在创建域对象。 I want these objects to have setters that will check the validity of the value to be assign to each property, but I also want to be able to set this property without performing the check if the data is assumed correct. 我希望这些对象具有设置器,这些设置器将检查要分配给每个属性的值的有效性,但是我也希望能够设置该属性而无需执行检查以确保数据正确。
I could do the following : 我可以执行以下操作:
class DO_Group
{
protected $_name = null;
public function getName()
{
return $this->_name;
}
public function setName( $name )
{
$length = strlen( $name );
if( 4 <= $length && $length <= 16 && preg_match( '^[A-Z]*(_[A-Z]*)*$', $name ) == 1 )
$this->_name = $name;
else
throw new Exception( '::DO_Group::setName() - Invalid name.' );
}
}
But in a case where the $name passed to setName() is known to be valid, I would have to call setName, and thus perform an unneeded check, to set that property. 但是在已知传递给setName()的$ name有效的情况下,我将不得不调用setName,从而执行不需要的检查来设置该属性。
I could use the reflection API or the debug trace hack, but I want to use none of these two ways because they are bad performance wise when used many times (and are dirty, IMO). 我可以使用反射API或调试跟踪技巧,但是我不想使用这两种方式,因为当多次使用时,它们会降低性能(并且很脏,IMO)。
I thought about adding a method that doesn't check the value : 我考虑过添加一种不检查值的方法:
public function setNameNoCheck( $name )
{
$this->_name = $name;
}
But in this case, making the property non-public doesn't make sense. 但是在这种情况下,将财产设为非公开是没有道理的。 And if I chose to make it public, a code user might forget to check it when needed. 而且,如果我选择将其公开,则代码用户可能会在需要时忘记对其进行检查。
Could I do : 我可以做:
abstract class BaseClass
{
public function setProperty( $name, $value )
{
$this->$name = $value;
}
}
And make my domain objects extends this class ? 并让我的领域对象扩展此类? It is correct PHP to refer undeclared properties ? PHP引用未声明的属性是正确的吗? (since they doesn't exist in the base class) (因为它们在基类中不存在)
I guess my suggestion would be to just use the method you have now for both instances: 我想我的建议是仅对两个实例使用您现在拥有的方法:
public function setName( $name )
{
$length = strlen( $name );
if( 4 <= $length && $length <= 16 && preg_match( '^[A-Z]*(_[A-Z]*)*$', $name ) == 1 )
$this->_name = $name;
else
throw new Exception( '::DO_Group::setName() - Invalid name.' );
}
In this case, you can be certain your database will always retain its integrity. 在这种情况下,您可以确定数据库将始终保持其完整性。 And even there are a few instances where you are POSITIVE the value is valid, the overhead would be minimal. 即使在某些情况下,您对POSITIVE的值仍然是有效的,开销也很小。 It just seems to be a clean solution that is guaranteed to work. 看来这是一个可以正常使用的干净解决方案。
If the object already exists in the database, why not just create a class method to handle that? 如果对象已经存在于数据库中,为什么不创建一个类方法来处理呢? Then call it when you are pulling values directly out of the database.. 直接从数据库中提取值时,请调用它。
public function populateFields( $row )
{
$this->_name = $row['name'];
// ... populate other fields here
}
This would populate all of the fields at once instead of having to write and call individual functions for each property. 这将立即填充所有字段,而不必为每个属性编写和调用各个函数。
If you use PHP 5.4+ then here is a good pice of code for you. 如果您使用PHP 5.4+,那么这里是您的理想选择。 I came up with this a couple of months back. 几个月前,我想到了这个。 If you're on PHP 5.3 or less, you can still make it work using call_user_func
. 如果您使用的是PHP 5.3或更低版本,则仍可以使用call_user_func
使它正常工作。 However with the help of anonymous functions it is much more clean, and verbose. 但是,借助匿名函数,它更加干净和冗长。
The idea is to make a kind of a friend
object. 这个想法是使一种friend
对象。 Similar to friend
classes in C++, but operating on specific objects instead of classes. 与C ++中的friend
类相似,但是对特定对象而不是类进行操作。
So let's get started. 因此,让我们开始吧。 First we need to prepare your class a bit: 首先,我们需要为您的课程做一些准备:
class DoGroup
{
protected $_name = null;
public function makeFriend(IDoGroupFriend $newFriend)
{
$newFriend->plugNameSetter(function($name){
$this->_doSetName($name);
});
}
public function setName( $name )
{
// do the costly checking here...
/// and set the variable:
$this->_doSetName($name);
}
protected function _doSetName($name)
{
$this->_name = $name;
}
}
Next, we would define that friend interface: 接下来,我们将定义该朋友界面:
interface IDoGroupFriend
{
public function plugNameSetter(callable $nameSetterFn);
}
And now we can create friends: 现在我们可以创建朋友:
class ExampleFriend implements IDoGroupFriend
{
private $_nameSetterFn = null;
public function plugNameSetter(callable $nameSetterFn)
{
$this->_nameSetterFn = $nameSetterFn;
}
public function setTheNameFromFriend($name)
{
$nameSetterFn = $this->_nameSetterFn;
$nameSetterFn($name);
}
}
Finally, we can use the friends as follows: 最后,我们可以如下使用这些朋友:
$Main = new DoGroup();
$Friend = new ExampleFriend();
$Main->makeFriend($Friend);
// tadam!
$Friend->setTheNameViaFriend('name via friend');
Ofcourse your real implementation could be much more sophisticated then just blindly passing the name. 当然,您的真正实现可能比盲目地通过名称来得复杂得多。 If the source of the $name
(the ExampleFriend
object in this case) is trusted then I'm assuming it comes from a different trusted source. 如果$name
的来源(在本例中为ExampleFriend
对象)是受信任的,那么我假设它来自其他受信任的来源。 In such case you would implement the setTheNameFromFriend
differently - for example as an databese fetch method. 在这种情况下,您将以不同的方式实现setTheNameFromFriend
,例如,作为一种数据获取方法。
Also you'd probably don't want to have the DoGroup::makeFriend()
method public. 另外,您可能不想公开DoGroup::makeFriend()
方法。 I usually invoke this method only from constructor of the DoGroup
class, so I like to meke it private. 我通常仅从DoGroup
类的构造函数中调用此方法,因此我想将其私有化。
Create safe mode ($this->safeMode = false) and 创建安全模式($ this-> safeMode = false),然后
public function setName( $name )
{
if ($this->safeMode===true){ $this->_name = $name; return;}
$length = strlen( $name );
if( 4 <= $length && $length <= 16 && preg_match( '^[A-Z]*(_[A-Z]*)*$', $name ) == 1 )
$this->_name = $name;
else
throw new Exception( '::DO_Group::setName() - Invalid name.' );
}
set safeMode to true if data is from database or other "safe" source (or class extends database class) 如果数据来自数据库或其他“安全”源(或类扩展数据库类),则将safeMode设置为true
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.