laravelの「__construct()ってなんだ」について結構適当に理解して過ごしてしまったコンストラクタの扱い。現在動いているプロジェクトで【継承より合成】との方針が出てサブクラスのConstructオーバーライドを合成でやるとどうなる?などしっかり理解してこなかったツケが湧いてきてしまいました...
目次
laravelの「__construct()ってなんだ」について
私もつい最近までちゃんと理解することを逃げてました。
だってねぇ、1人で作る分には使わなくても作れるし。
functionだってstaticにすればコンストラクタ関係なく構築できるし。
でも、こいつの扱いをコントロールできるか否かは初心者と中級者の分かれ目だったりします。
ハードコーディングとコンストラクタ
HardCoding
は良くないと言われますよね。
で、ハードコーティングを回避しようとこの様な記載をする人がいます。
class CalcSalesPrice
{
public function tax(int $price):?int
{
# 消費税率を指示
$taxRate = 1.1;
# 税込価格を計算
$priceIncludingTax = $price * $taxRate;
return $priceIncludingTax;
}
}
これはこれでSoftCodingとして間違いではないです。固定値ではなく変数化してますから。
もちろん、ちゃんと計算しますよね。
※ 2022-07-23|ご指摘いただきテキストのインデント揃えました。
コンストラクタやプライベート変数は共通して利用する固定値で大活躍
もちろんこれだけではないのですが、活用シーンとしてはこんな利用が多いと思います。
上の例で継続して考えてみます。上記例はfunctionが1つだけですが、これが複数あった場合はどうでしょう。
class CalcSalesPrice
{
# 単品購入の計算
public function tax(int $price):?int
{
# 消費税率を指示
$taxRate = 1.1;
# 税込価格を計算
$priceIncludingTax = $price * $taxRate;
return $priceIncludingTax;
}
# 複数購入時の計算
public function taxs(array $prices):?int
{
# 消費税率を指示
$taxRate = 1.1;
# 加算用の箱を用意
$totalPrice = 0;
# 1件づつ消費税をかける
foreach($prices as $price){
# 税込価格を計算
$priceIncludingTax = $price * $taxRate;
# 合算
$totalPrice = $totalPrice + $priceIncludingTax;
}
return $totalPrice;
}
}
function内で毎回消費税率を変数にしています。
これは税率変更があった場合とても面倒になります。
で、「呼び出す先で指定して引数で渡せば良いじゃん」という発想が出てきます。
具体的にはこんな感じになります。
class CalcSalesPrice
{
# 単品購入の計算
public function tax(int $price, float $taxRate):?int
{
# 税込価格を計算
$priceIncludingTax = $price * $taxRate;
return $priceIncludingTax;
}
# 複数購入時の計算
public function taxs(array $prices, float $taxRate):?int
{
# 加算用の箱を用意
$totalPrice = 0;
# 1件づつ消費税をかける
foreach($prices as $price){
# 税込価格を計算
$priceIncludingTax = $price * $taxRate;
# 合算
$totalPrice = $totalPrice + $priceIncludingTax;
}
return $totalPrice;
}
}
このクラスの中はスッキリしましたが、今度は呼び出す先が複数になった場合「どこで呼んでいるか」を探すのが超面倒になります。
ということで、こういったシーンの場合にコンストラクタやメンバ変数を利用します。
コンストラクタって何?
一言で言うと下の通りです。
クラスをnewしたときに実行される関数
PHPの場合は __construct
という名前のメソッドを作ることでコンストラクタとして扱われnew Class名
をコールした際に実行されます。
具体的にコードで説明してみます。
class CalcSalesPrice
{
# コンストラクタ
public function __construct()
{
$this->taxRate = 1.1;
}
public function tax(int $price):?int
{
# 税込価格を計算
$priceIncludingTax = $price * $this->taxRate;
return $priceIncludingTax;
}
}
new CalcSalesPrice;
を実行すると、最初にコンストラクタが実行されるので、このClassの中では$this->taxRate
をコールすれば1.1が返却されます。
メンバ変数って何?
上のような共通項目の記載の仕方としてもう一つメンバ関数というものがあります。
具体的には下のように記載するやつです。
class CalcSalesPrice
{
# メンバ関数
Private $taxRate = 1.1;
public function tax(int $price):?int
{
# 税込価格を計算
$priceIncludingTax = $price * $this->taxRate;
return $priceIncludingTax;
}
}
thisで呼び出すなどコールの方法は同じですが、何が違うのでしょうか。
メンバ変数 = クラスの中に定義するそのクラス固有の変数
newした時に実行されるわけではなく、このClassでの固定値 = メンバ変数となります。
そのため環境変数(.env)を呼び出して利用するなど調べると様々な構築例が出てくると思います。
まとめ
脱初心者のアップグレード知識としてはこれくらいを理解していれば問題ないはずです。
中級の工程ではココに合成と継承の概念が盛り込まれ次のような話題が議題に上がります。
- サブクラスのコンストラクタでオーバーライドする(またはしない)
- Class合成でメンバ変数を変更したい(またはしたくない)
私は今まで、ここら辺を深く考えることなく何とか熟せて来てしまったのですが、知識ないと結構しんどくなる場面に遭遇しました。
そもそも合成と継承が頭の中で整理整頓されてなかったりとか…。
なので、プロジェクトの方針として継承より合成を使うべきとなった時に「あれ? この書き方は継承?」とか…大問題ですね。
引数で渡せばコード自体は書けてしまいます。
ですが、その発想では越えられない現場の壁があります。
コード書きながら、確実に理解していきましょう。