简体   繁体   English

访问非公共成员

[英]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. 看来这是一个可以正常使用的干净解决方案。

EDIT : 编辑:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM