目的
『良いコード/悪いコード』第八章に記載されているサンプルコードのうち、以下の❶~❸をPHP言語のコードに直すことで、更なる理解を深める
❶定数クラス
❷通常割引価格クラス
❸夏季割引価格クラス
❶定価クラス
// ①本書で登場するサンプルコード
class RegularPrice {
final int MIN_AMOUNT = 0;
final int amount;
// Javaではクラス名と同じ名前のメソッドをコンストラクタとして使用できる
RegularPrice(fainal int amount) {
// JavaはPHPよりも静的な構文がしっかりしており、直接アクセスできる。
if (amount < MIN_AMOUNT) {
throw new ILLegalArgumentException("価格が0以上でありません。");
}
this.amount = amount;
}
}
// ②PHPに書き直したもの(private & ゲッターメソッドを使用)
class RegularPrice {
const int MIN_AMOUNT = 0; // php8.3からconstに型を指定できる!
private int $amount;
RegularPrice(int $amount) {
if ($amount < self::MIN_AMOUNT) {
throw new ILLegalArgumentException("価格が0以上でありません。");
}
$this->amount = $amount;
}
public function getAmount(): int {
return $this->amount;
}
}
// ③PHPに書き直したもの(readonlyを使用)
class RegularPrice {
const int MIN_AMOUNT = 0;
public readonly int $amount;
public function __construct(int $amount)
{
if ($amount < self::MIN_AMOUNT) {
throw new ILLegalArgumentException("価格が0以上でありません。");
}
$this->amount = $amount,
}
}
// ④参考にするためにChatGPTに生成させたもの
class RegularPrice {
// PHPではconstを使うと、その値が変更不可になり、final修飾子と同じ役割を果たす
// constで定義すると、変数ではなくクラス定数になり、そもそも変更が許されない
const MIN_AMOUNT = 0; // 定数なので先頭に$の記号は付けない
private $amount; // ??この変数の値はどこから来ているか?? → この時点では変数は空っぽ。「$this->amount = $amount;」で値を代入するための箱。
public function __construct(int $amount) {
if ($amount < self::MIN_AMOUNT) { // 「self::」は定数などにアクセルするために使うもの。定数はクラス全体で共通の値として扱われる。
throw new InvalidArgumentException("価格が0以上でありません。");
}
$this->amount = $amount;
}
// Javaでは変数にfinal修飾子をつけることが可能であり、それが今回でいうところの「final int amount;(①本書)」にあたる。PHPではそれができないため、privateプロパティ(変数)をクラス外から直接呼び出せる [呼び出せる = 中身を変更できる] ようにしないために、このように呼び出す専用のメソッド(ゲッターメソッド)を使って、クラス外から値を変更せずに値を使えるようにする。
public function getAmount(): int {
return $this->amount;
}
}
try {
$price = new RegularPrice(-1); // テストケースとして不正な値を入れてみる
} catch (InvalidArgumentException $e) {
echo $e->getMessage();
}
constの型指定1
PHPのfainal修飾子について
fainal修飾子はPHPでは以下の制約がある
使える場面 :クラス(ただしfainalクラスは継承が不可能)、メソッド
使えない場面 :プロパティ(メンバ変数)、グローバル関数(クラスの外の関数)
つまり、phpではfinal修飾子をクラスとメソッドのみでしか使えない。
constについて
constは定数を定義するためのもの。値が変わらないので、変数の印である$
はつけない2
readonly概要
- PHPのfinalはプロパティには使えないが、Readonlyはプロパティにしか使えない
- プロパティは初期化後に変更できない。初期化はコンストラクタ内で行う。
- php8.2からはクラスとメソッドでも使用可能
- Readonlyはfinalと全く同じ使い型ができるわけじゃなくて、こまかい癖がある
- readonly クラスは継承できるが、親も子も readonly クラスでないといけない。
- プロパティには型を付けなければならない(動的プロパティの作成禁止)。ただし、どうしても型を付けたくない場合にはmixed型(mixed:混合された)を付けることで対処可能(mixed型は"何でもあり"型を指す)。
- PHPのReadonlyとconstの違いはインスタンスごとに生成されるか、クラス共通であるかの違い。
- 変更不可の変数はReadonly、定数はconstを使う。
readonlyの使い方345
class Fruit {
public readonly string $apple = "delicious";
}
ただの定数として機能するのでreadonlyの意味がない…
// ①
class Fruit {
public readonly string $apple;
public function __construct(string $taste = "delicious")
{
$this->apple = $taste;
}
}
// ②
class Fruit {
public function __construct(
public readonly string $apple = "delicious",
) {}
}
①と②は同じ動きをする
「public readonly property」が「private property & getter(ゲッターメソッド)」よりも優れている点は、readonlyは__contruct()
以外でプロパティの値が変更できないという制約があり、privateとは違い、ほかのクラス内のメソッドによって値が変わる可能性を考える必要がないこと。
しかし、readonlyでは外部からプロパティに直接アクセスできてしまうという意味ではカプセル化が弱まってしまう?
readonlyは直接アクセスされても変更される心配はないので基本的にはpublicを使う。
❷通常割引価格クラス
// ①本書で登場するサンプルコード
class RegularDiscountedPrice {
private static final int MIN_AMOUNT = 0;
private static final int DISCOUNT_AMOUNT = 400;
final int amount;
RegularDiscountedPrice(final RegularPrice Pirce) {
int discountedAmount = price.amount - DISCOUNT_AMOUNT;
if (discountedAmount < MIN_AMOUNT) {
discountedAmount = MIN_AMOUNT;
}
amount = discountedAmount;
}
};
❸夏季割引価格クラス
// ①本書で登場するサンプルコード
class SummerDiscountedPrice {
private static final int MIN_AMOUNT = 0;
private static final int DISCOUNT_AMOUNT = 300;
final int amount;
SummerDiscountedPrice(final RegularPrice price) {
int discountedAmount = price.amount - DISCOUNT_AMOUNT;
if (discountedAmount < MIN_AMOUNT) {
discountedAmount = MIN_AMOUNT;
}
amount = discountedAmount;
}
};