2
2

More than 1 year has passed since last update.

PHP の private property + getter と public readonly property どちらを使うべきか?

Last updated at Posted at 2023-08-14

1行結論

僕では絶対こちらがいい、という決め手を見つけられませんでした。プロジェクト次第。

サンプルコードを書いてみる

サンプルコードを書いてメリット・デメリットを考える。

  • 姓、名、フルネームを持つユーザークラスを作成したい
  • User, FirstName, LastName, FullName クラスをそれぞれ作成する
  • PHP8.2
  • Qiita 上でササッと書いたのでコードに間違いあるかも

private property + getter の場合

namespace Kazumacchi\Sample;

class FirstName
{
    /**    
     * @param  string  $firstName
     */
    public function __construct(
        private string $firstName,
    ) {
    }

    /**
     * @return string
     */
    public function getValue(): string
    {
        return $this->firstName;
    }
}
namespace Kazumacchi\Sample;

class LastName
{
    /**     
     * @param  string  $lastName
     */
    public function __construct(
        private string $lastName,
    ) {
    }

    /**
     * @return string
     */
    public function getValue(): string
    {
        return $this->lastName;
    }
}
namespace Kazumacchi\Sample;

class FullName
{
    /**
     * @param  FirstName  $firstName
     * @param  LastName  $lastName
     */
    public function __construct(
        private FirstName $firstName,
        private LastName $lastName,
    ) {
    }

    /**
     * @return string
     */
    public function getValue(): string
    {
        return $this->firstName->getValue() . ' ' . $this->lastName->getValue();
    }
}
namespace Kazumacchi\Sample;

class User
{
    /**
     * @var FullName
     */
    private FullName $fullName;

    /**
     * @param  FirstName  $firstName
     * @param  LastName  $lastName
     */
    public function __construct(
        private FirstName $firstName,
        private LastName $lastName
    ) {
        $this->fillName = new FullName(
            $this->firstName,
            $this->lastName,
        );
    }

    /**
     * @return FirstName
     */
    public function getFirstName(): FirstName
    {
        return $this->firstName;
    }

    /**
     * @return LastName
     */
    public function getLastName(): LastName
    {
        return $this->lastName;
    }

    /**
     * @return FullName
     */
    public function getFullName(): FullName
    {
         return $this->fullName;
    }
}
$user = new User(
    new FirstName('タナカ'),
    new LastName('タロウ'),
);

// 名を出力
echo $user->getFirstName()->getValue();
// 姓を出力
echo $user->getLastName()->getValue();
// フルネームを出力
echo $user->getFullName()->getValue();

private property + getter のメリット

interface, abstract で擬似的にプロパティを持つことを強制でき、クラスの振る舞いが明確になる

interface を利用して、メソッドを介して擬似的に firstName, lastName, fullName プロパティを強制する

interface UserInterface
{
    /**
     * @return FirstName
     */
    public function getFirstName(): FirstName;

    /**
     * @return LastName
     */
    public function getLastName(): LastName

    /**
     * @return FullName
     */
    public function getFullName(): FullName;
}
class User implements UserInterface
{
    // 省略
}

クラスメソッド内で取得方法の変更や追加のロジックを入れることが出来るためコード変更の柔軟性が高い

class FullName
{
    // 省略

    /**
     * @return string
     */
    public function getValue(): string
    {
        return $this->firstName->getValue() . ' ' . $this->lastName->getValue();
    }

    /**
     * 敬称をつける
     *
     * @return string
     */
    public function getSuffixValue(): string
    {
        return $this->getValue() . ' 様';
    }
}

private property + getter のデメリット

  • コード量が多い。経験上、getter メソッドのほとんどは private property をそのまま返却しているため、わざわざメソッドを介することが無駄に感じる。YAGNI原則だとこういうのはどうなるのか気になる。
class FirstName
{
    // 省略

    public function getValue()
    {
        // プロパティを返しているだけ
        return $this->firstName;
    }
}

public readonly property の場合

namespace Kazumacchi\Sample;

/**
 * @property-read string $value
 */
class FirstName
{
    /**
     * @param  string  $value
     */
    public function __construct(
        public readonly string $value;
    ) {
    }

    /**
     * @return string
     */
    public function __toString(): string
    {
        return $this->value;
    }
}
namespace Kazumacchi\Sample;

/**
 * @property-read string $value
 */
class LastName
{
    /**
     * @param  string  $value
     */
    public function __construct(
        public readonly string $value;
    ) {
    }

    /**
     * @return string
     */
    public function __toString(): string
    {
        return $this->value;
    }
}
namespace Kazumacchi\Sample;

/**
 * @property-read FirstName $firstName
 * @property-read LastName $lastName
 */
class FullName
{
    /**
     * @param  FirstName  $firstName
     * @param  LastName  $lastName
     */
    public function __construct(
        public readonly FirstName $firstName,
        public readonly LastName $lastName,
    ) {
    }

    /**
     * @return string
     */
    public function __toString(): string
    {
        return $this->firstName . ' ' . $this->lastName;
    }
}
namespace Kazumacchi\Sample;

/**
 * @property-read FirstName $firstName
 * @property-read LastName $lastName
 * @property-read FullName $fullName
 */
class User
{
    /**
     * @var FullName
     */
    public readonly FullName $fullName;

    /**
     * @param  FirstName  $firstName
     * @param  LastName  $lastName;
     */
    public function __construct(
        public readonly FirstName $firstName,
        public readonly LastName $lastName,
    ) {
        $this->fullName = new FullName(
            $this->firstName,
            $this->lastName,
        );
    }
}
$user = new User(
    new FirstName('タナカ'),
    new LastName('タロウ'),
);

// 名を出力
echo $user->firstName;
// or
echo $user->firstName->value;

// 姓を出力
echo $user->lastName;
// or
echo $user->lastName->value;

// フルネームを出力
echo $user->fullName;

public readonly property のメリット

コード量が少なめ

コード量が少ないということは、コードが読みやすくなるということ

プロパティの値をシンプルに取得できる

古いPHPのように外部からプロパティが書き換えられる心配もない

echo $user->fullName;

readonly なので __construct() 以外でプロパティの値が変更されないことを読み手に伝えられる

  • 他のクラスメソッドで値が変わる可能性は一切考慮する必要なし
  • setter メソッドも存在しないことが分かる

public readonly property のデメリット

interface, abstract でプロパティを持つことを強制できない

元々プロパティを持つことを強制は出来ませんが、メソッドで擬似的に強制することもできない
(※private setter メソッドでも書いて __construct() で呼び出しとかすれば行けるかもしれないですが…)

そのため何も書かれていない interface を継承することになりそう
このコードが無意味とまでは言わないが、interface を使うメリットは薄くなる

interface UserInterface
{
}
class User implements UserInterface
{
    // 省略
}

結論

どちらにも無視できないメリットデメリットがあって僕ではよく分からない
結局プロジェクト次第ということになるのかな

スーパーエンジニアにこういうのを聞いてみたいが、社内に見当たらない件

もし interface や abstract でプロパティそのものを強制出来るようになった場合、そこが getter の寿命かも
(そんな機能に需要があるかどうかは知りませんけど!)

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2