如何处理类中的属性及其默认值?

时间:2017-12-06 16:07:40

标签: php php-7

我创建了一个具有严格属性的类'类型,它们的值可以传递给构造函数:

declare(strict_types=1);

class Position
{
    protected $hours;
    protected $minutes;
    protected $seconds;
    protected $frames;


    public function __construct(int $hours = 0, int $minutes = 0, int $seconds = 0, int $frames = 0)
    {
        $this->setHours($hours);
        $this->setMinutes($minutes);
        $this->setSeconds($seconds);
        $this->setFrames($frames);
    }

    public function getHours(): int
    {
        return $this->hours;
    }


    public function setHours(int $hours): void
    {
        $this->hours = $hours;
    }

    // and so on...
}
  1. 设置属性是否合适?构造函数中使用setter的值?

  2. 在课堂上获取自己的价值观是否合适? getter方法?

  3. 最好设置默认属性' values:用它们显式初始化属性,将它们设置为默认参数值(因为它现在已经完成)或在两个地方都指定它们?

  4. 感谢。

1 个答案:

答案 0 :(得分:1)

  

设置属性是否合适?构造函数中使用setter的值?

换句话说:在构造函数中初始化属性时委托给setter是否可以?简短的回答是:它并不重要。如果没有进行验证(除了类型),那么其中没有任何意义,但也没有缺点。如果 要进行更多验证,那么可以指出它保持构造函数的清洁,同时减少重复代码的数量。如果要进行其他检查,那么委托给setter进行公共验证并在构造函数中实现额外的检查将有助于将两个工作流分开。

最后,我不认为这里有一个真正令人担忧的问题。这样做既不好也不坏。为您的特定用例做最好的事情。

  

在课堂上获得他们的价值观是否合适? getter的方法?

如果您的意思是"我应该通过getter"来访问对象属性,那么:是。是一千次是的。对象属性应始终是私有的,并通过getter访问。公共属性违反了面向对象的 核心概念之一:Encapsulation。每个成员是否具有吸气剂并且是否具有逻辑完全取决于您并将取决于用例。

如果你的意思是"我应该使用getter从对象中访问一个 "那真的不重要,取决于场景。有时使用公共getter没有实际意义,也没有帮助代码的可读性/可维护性。可能有些情况下,呼叫公众吸气者是完全有意义的。例如,当处理isFoo()样式的函数时,根据几个条件返回truefalse,只需调用isFoo()而不是重复条件再次。

  

最好设置默认属性' values:用它们显式初始化属性,将它们设置为默认参数值(因为它现在已经完成)或者在两个地方都指定它们?

这个有点复杂。在我看来,选择如何初始化对象属性时有三种可能的情况。

  1. 将值必须传递给构造函数。
  2. 这是在实例化对象时必须指定某个属性的情况。如果未指定值,则代码将失败。例如,在创建User类型的对象时,可以安全地假设它需要firstNamelastNameemail。如果这些变量中的一个或全部未传递给构造函数,则代码应该失败:构造函数设置的契约未得到满足。

    class User {
        private $firstName;
        private $lastName;
        private $email;
    
        public function __construct(string $firstName, string $lastName, string $email) {
            $this->firstName = $firstName;
            $this->lastName = $lastName;
            $this->email = $email;
        }
    }
    
    $user = new User();
    
      

    异常:传递给User :: __ construct()的参数1必须是string类型,没有给出

    1. 将值 MAY 传递给构造函数。
    2. 在这种情况下,您希望允许在构造函数中设置值,但不是 required 来实例化对象。从前面的User示例中,我们还可以拥有用户可以拥有的middleName属性,并且我们希望能够将其设置为构造函数。

      class User {
          private $firstName;
          private $lastName;
          private $middleName;
          private $email;
      
          public function __construct(string $firstName, string $lastName, string $email, $middleName = null) {
              $this->firstName = $firstName;
              $this->lastName = $lastName;
              $this->email = $email;
              $this->middleName = $middleName;
          }
      }
      
      $user = new User('John', 'Doe', 'johndoe@email.com'); // does not throw an Exception
      

      在我看来以这种方式实际实现构造函数的理由很少。它为构造函数增加了不必要的复杂性,而实际上并没有解决任何实际问题当然,在不破坏任何现有代码的情况下向构造函数添加参数可能是一种快速而又脏的方法,但是如果要修改创建对象实例的规则,则可能应更新现有代码以反映此更改。

      1. 将值必须初始化为默认值。
      2. 在实例化对象时,您希望将某个属性初始化为特定的默认值。这样就不需要经常检查属性是否已初始化,或者在处理此属性时检查null值。如果特定用例要求立即覆盖该值,则只需在实例化对象后调用setter。

        class User {
            const USER_DEFAULT = 'default';
        
            private $firstName;
            private $email;
            private $propertyToInitialize = self::USER_DEFAULT;
        
            public function __construct(string $firstName, string $email) {
                $this->firstName = $firstName;
                $this->email = $email;
            }
        
            public function getInitializedProperty() {
                return $this->propertyToInitialize;
            }
        }
        
        $user = new User('John', 'johndoe@email.com');
        echo $user->getInitializedProperty(); // outputs: 'default'
        

        如果您需要将某个属性的值作为特定值的一部分,我建议您查看Enums