【问题标题】:Is this a reasonable way to handle getters/setters in a PHP class?这是在 PHP 类中处理 getter/setter 的合理方法吗?
【发布时间】:2010-09-07 02:59:07
【问题描述】:

我将尝试使用这个问题的格式,我非常愿意接受有关更好的处理方法的建议。

我不想只是在问题中转储一堆代码,所以我已在refactormycode 上发布了该课程的代码。

base class for easy class property handling

我的想法是人们可以在此处发布代码 sn-ps 或在 refactormycode 上进行更改并将链接发布回他们的重构。我会在此基础上投票并接受答案(假设有明确的“赢家”)。

无论如何,到班级本身:

我看到很多关于 getter/setter 类方法的争论,是直接访问简单的属性变量更好,还是每个类都定义了明确的 get/set 方法,等等等等。我喜欢使用显式方法的想法,以防您以后必须添加更多逻辑。然后您不必修改任何使用该类的代码。但是我讨厌有一百万个看起来像这样的函数:

public function getFirstName()
{
   return $this->firstName;
}
public function setFirstName($firstName)
{
   return $this->firstName;
}

现在我确定我不是第一个这样做的人(我希望有人可以向我建议更好的方法)。

基本上,PropertyHandler 类有一个 __call 魔术方法。任何通过 __call 以“get”或“set”开头的方法都会被路由到将值设置或检索到关联数组中的函数。进入数组的键是获取或设置后调用方法的名称。所以,如果进入__call的方法是“getFirstName”,那么数组键就是“FirstName”。

我喜欢使用 __call,因为它会自动处理子类已经定义了“getFirstName”方法的情况。我的印象(我可能错了)是 __get 和 __set 魔术方法不这样做。

下面是它如何工作的示例:

class PropTest extends PropertyHandler
{
    public function __construct()
    {
        parent::__construct();
    }
}

$props = new PropTest();

$props->setFirstName("Mark");
echo $props->getFirstName();

注意,PropTest 实际上没有“setFirstName”或“getFirstName”方法,PropertyHandler 也没有。所做的只是操作数组值。

另一种情况是您的子类已经在扩展其他内容。由于您在 PHP 中不能拥有真正的多重继承,因此您可以让您的子类将 PropertyHandler 实例作为私有变量。您必须再添加一个功能,但事情的行为方式完全相同。

class PropTest2
{
    private $props;

    public function __construct()
    {
        $this->props = new PropertyHandler();
    }

    public function __call($method, $arguments)
    {
        return $this->props->__call($method, $arguments);
    }
}

$props2 = new PropTest2();

$props2->setFirstName('Mark');
echo $props2->getFirstName();

注意子类如何有一个 __call 方法,它只是将所有内容传递给 PropertyHandler __call 方法。


反对以这种方式处理 getter 和 setter 的另一个很好的论据是它很难记录。

事实上,基本上不可能使用任何类型的文档生成工具,因为不存在不记录的显式方法。

我现在几乎已经放弃了这种方法。这是一个有趣的学习练习,但我认为它牺牲了太多的清晰度。

【问题讨论】:

  • 出于好奇,你现在用什么方法?
  • 在同一个类中拥有 getter setter 表明该类缺少其代码。调查使用类的 getter 和 setter 的代码并将其移动到类中,因为这是它所属的地方。

标签: php oop


【解决方案1】:

我的做法如下:

class test {
    protected $x='';
    protected $y='';

    function set_y ($y) {
        print "specific function set_y\n";
        $this->y = $y;
    }

    function __call($function , $args) {
        print "generic function $function\n";
        list ($name , $var ) = split ('_' , $function );
        if ($name == 'get' && isset($this->$var)) {
            return $this->$var;
        }
        if ($name == 'set' && isset($this->$var)) {
            $this->$var= $args[0];
            return;
        }
        trigger_error ("Fatal error: Call to undefined method test::$function()");
    }
}

$p = new test();
$p->set_x(20);
$p->set_y(30);
print $p->get_x();
print $p->get_y();

$p->set_z(40);

将输出哪个(为清楚起见添加了换行符)

generic function set_x
specific function set_y

generic function get_x
20
generic function get_y
30

generic function set_z
Notice: Fatal error: Call to undefined method set_z() in [...] on line 16

【讨论】:

  • (来自 OP 的移动评论):@Pat 这很有趣。当然少得多的代码。但是您仍然必须显式声明每个底层属性变量,对吧?
  • "split" 自 PHP 5.3.0 起已弃用
【解决方案2】:

是的,变量必须手动声明,但我发现这样更好,因为我担心设置器中的拼写错误

$props2->setFristName('Mark');

将自动生成一个新属性(FristName 而不是 FirstName),这将使调试更加困难。

【讨论】:

    【解决方案3】:

    我也喜欢使用方法而不是仅仅使用公共字段,但是我对 PHP 的默认实现(使用 __get() 和 __set())或您的自定义实现的问题是您没有在每个属性的基础。我的问题是,稍后添加“更多逻辑”需要您添加适用于使用 getter/setter 访问的所有属性的总括逻辑,或者您使用 if 或 switch 语句来评估您正在访问的属性,以便您可以应用具体逻辑。

    我喜欢你的解决方案,我为你喝彩——我只是对 PHP 在隐式访问器方法方面的限制不满意。

    【讨论】:

    • (来自 OP 的移动评论)@Brian 这是一个有趣的观点。我将不得不考虑一下。
    【解决方案4】:

    @Brian

    我的问题是,添加“稍后添加更多逻辑”需要您添加适用于使用 getter/setter 访问的所有属性的一揽子逻辑,或者您使用 if 或 switch 语句来评估您正在访问的属性,以便你可以应用特定的逻辑。

    这并不完全正确。以我的第一个例子:

    class PropTest extends PropertyHandler
    {
        public function __construct()
        {
            parent::__construct();
        }
    }
    
    $props = new PropTest();
    
    $props->setFirstName("Mark");
    echo $props->getFirstName();
    

    假设我需要添加一些逻辑来验证名字。我所要做的就是向我的子类添加一个 setFirstName 方法,然后自动使用该方法。

    class PropTest extends PropertyHandler
    {
        public function __construct()
        {
            parent::__construct();
        }
    
        public function setFirstName($name)
        {
            if($name == 'Mark')
            {
                echo "I love you, Mark!";
            }
        }
    }
    

    我只是对 PHP 在隐式访问器方法方面的限制不满意。

    我完全同意。我喜欢 Python 的处理方式(我的实现只是一个笨拙的抄袭)。

    【讨论】:

      【解决方案5】:

      @马克

      但是即使你的方法也需要一个新的方法声明,它在某种程度上剥夺了将它放在一个方法中以便你可以添加更多逻辑的优势,因为添加更多逻辑需要方法的老式声明, 反正。在其默认状态下(这是它检测/执行的令人印象深刻的地方),您的技术在公共字段中没有提供任何优势(在 PHP 中)。您正在限制对该字段的访问,但通过自身没有任何限制的访问器方法给予全权委托。我不知道未经检查的显式访问器比 任何 语言的公共字段有任何优势,但如果我错了,人们可以而且应该随时纠正我。

      【讨论】:

      • 所有适用于基本 getter/setter 的属性都将使用魔术调用。如果他需要添加额外的代码,他可以只为这些属性创建 getter/setter。同样,这可能会造成一些混乱,但这是一种选择。
      【解决方案6】:

      我总是用 __call 来处理这个问题,这在我的许多课程中几乎都是样板代码。但是,它很紧凑,并且使用反射类只为您已经设置的属性添加 getter/setter(不会添加新的)。简单地显式添加 getter / setter 将添加更复杂的功能。它预计是

      代码如下:

      /**
      * Handles default set and get calls
      */
      public function __call($method, $params) {
      
          //did you call get or set
          if ( preg_match( "|^[gs]et([A-Z][\w]+)|", $method, $matches ) ) {
      
              //which var?
              $var = strtolower($matches[1]);
      
              $r = new ReflectionClass($this);
              $properties = $r->getdefaultProperties();
      
              //if it exists
              if ( array_key_exists($var,$properties) ) {
                  //set
                  if ( 's' == $method[0] ) {
                      $this->$var = $params[0];
                  }
                  //get
                  elseif ( 'g' == $method[0] ) {
                      return $this->$var;
                  }
              }
          }
      }
      

      将此添加到您已声明默认属性的类中,例如:

      class MyClass {
          public $myvar = null;
      }
      
      $test = new MyClass;
      $test->setMyvar = "arapaho";
      
      echo $test->getMyvar; //echos arapaho    
      

      反射类可能会为您的提议添加一些有用的东西。整洁的解决方案@Mark。

      【讨论】:

        【解决方案7】:

        就在最近,我还考虑过按照您建议的方式处理 getter 和 setter(第二种方法是我最喜欢的方法,即私有 $props 数组),但我放弃了它,因为它在我的应用程序中不会奏效。

        我正在开发一个相当大的基于 SoapServer 的应用程序,PHP 5 的soap 接口将通过soap 传输的值直接注入到关联的类中,而无需担心类中现有或不存在的属性。

        【讨论】:

          【解决方案8】:

          我忍不住投入我的 2 美分...

          我已经开始在这个庄园http://gist.github.com/351387 中使用__get__set(类似于教义的做法),然后只在课堂外通过$obj->var 访问属性。这样您就可以根据需要覆盖功能,而不是创建一个巨大的 __get__set 函数,或者覆盖子类中的 __get__set

          【讨论】:

            猜你喜欢
            • 2016-02-11
            • 1970-01-01
            • 1970-01-01
            • 2015-02-07
            • 2016-08-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多