こんにちは、つかさです
今回も現在携わっている開発で学んだことを記事にまとめようと思います!
前回テストパターンについての記事を書きましたが、その中で出てきたextends
とtrait
の違いについてまとめたいと思います!
前提
- バックエンド
👉 Laravel - フロントエンド
👉 Inertia.js(Vue3) - アーキテクチャ
👉MVCS
extendsについて
クラスの親子関係を作りたい場合に、子クラス名の横にextends
と親クラスにしたいクラス名を書きます。これを別の言い方で◯◯クラスは✖︎✖︎クラスを継承してるとも言いますね。
・では継承すると何がいいんでしょうか?
- 親クラスのプロパティやメソッドを、子クラスで使うことができます。
- コンストラクタの継承も可能。
オーバライドとは
親クラスにあるメソッドと同じ名前のメソッドを子クラスで使用した場合に、子クラスのインスタンス内では子クラスにあるメソッドが処理されます。これを親クラスにあるメソッドを上書く意味合いからオーバーライドすると言われます。
class Animal {
public function makeSound() {
return "Some generic sound";
}
}
class Dog extends Animal {
public function makeSound() {
return "Bark";
}
}
$dog = new Dog();
echo $dog->makeSound(); // 出力: "Bark"
traitについて
クラスの機能を再利用するための仕組み。クラスの継承とは違って、複数のクラス間でコードを共有するために使用されます。
- 多重継承の代替手段: 複数のクラスで同じ機能を共有できる。
- コードの再利用を促進。
trait Logger {
public function log($message) {
echo "Log: " . $message;
}
}
class User {
use Logger;
}
class Product {
use Logger;
}
$user = new User();
$user->log("User logged in."); // 出力: "Log: User logged in."
$product = new Product();
$product->log("Product added."); // 出力: "Log: Product added."
複数使用
trait A {
public function methodA() {
echo "Method A";
}
}
trait B {
public function methodB() {
echo "Method B";
}
}
class MyClass {
use A, B;
}
$obj = new MyClass();
$obj->methodA(); // 出力: "Method A"
$obj->methodB(); // 出力: "Method B"
特徴 | extends | trait |
---|---|---|
多重使用 | × (単一継承のみ) | ○ (複数トレイト適用可能) |
機能の上書き | ○ (オーバーライド可能) | ○ (メソッドの上書き可能) |
コンストラクタ | ○ (親のものを継承) | × (トレイトにコンストラクタなし) |
使用用途 | "is-a" 関係 | "has-a" 関係 |
再利用の適用範囲 | 1つの継承先 | 複数のクラス |
extends,trait両方使用した場合について
extends
で親クラスの基本機能を継承しつつ、trait
で特定の機能(共通処理など)を追加するような実装をする事ができます。
extends+子クラスでtraitを使用
trait Logger {
public function log($message) {
echo "[LOG]: " . $message . "\n";
}
}
class BaseClass {
public function greet() {
echo "Hello from BaseClass\n";
}
}
class ChildClass extends BaseClass {
use Logger; // trait を追加
public function sayGoodbye() {
echo "Goodbye from ChildClass\n";
}
}
// インスタンス化
$child = new ChildClass();
$child->greet(); // 出力: Hello from BaseClass
$child->log("Test"); // 出力: [LOG]: Test
$child->sayGoodbye(); // 出力: Goodbye from ChildClass
extends+子クラスでtraitを複数使用
trait Logger {
public function log($message) {
echo "[LOG]: " . $message . "\n";
}
}
trait Notifier {
public function notify($message) {
echo "[NOTIFY]: " . $message . "\n";
}
}
class BaseClass {
public function greet() {
echo "Hello from BaseClass\n";
}
}
class AdvancedClass extends BaseClass {
use Logger, Notifier; // 複数のトレイトを使用
public function process() {
echo "Processing data...\n";
}
}
$adv = new AdvancedClass();
$adv->greet(); // 出力: Hello from BaseClass
$adv->log("Logging"); // 出力: [LOG]: Logging
$adv->notify("Alert!"); // 出力: [NOTIFY]: Alert!
$adv->process(); // 出力: Processing data...
extends+親クラスでtraitを使用
trait Logger {
public function log($message) {
echo "[LOG]: " . $message . "\n";
}
}
class ParentClass {
use Logger; // 親クラスにトレイトを組み込む
public function commonMethod() {
echo "Common method in ParentClass\n";
}
}
class SubClass extends ParentClass {
public function customMethod() {
echo "Custom method in SubClass\n";
}
}
$instance = new SubClass();
$instance->commonMethod(); // 出力: Common method in ParentClass
$instance->log("Info"); // 出力: [LOG]: Info
$instance->customMethod(); // 出力: Custom method in SubClass
トレイトのメソッドオーバーライド
trait Logger {
public function log($message) {
echo "[LOG]: " . $message . "\n";
}
}
class ParentClass {
use Logger;
public function log($message) {
echo "[ParentClass LOG]: " . $message . "\n";
}
}
class SubClass extends ParentClass {
public function log($message) {
echo "[SubClass LOG]: " . $message . "\n";
}
}
$instance = new SubClass();
$instance->log("Test message"); // 出力: [SubClass LOG]: Test message
SubClassにメソッドがなく、親クラスとtraitクラスでメソッドが競合した場合は、親クラスのメソッドが優先されるのでご注意!
ではtraitクラスのメソッドを使用したい場合はどうすれば良いか?
insteadof
またはas
を使用するといいでしょう。
insteadof
class ChildClass extends ParentClass {
use TraitExample {
TraitExample::sayHello insteadof ParentClass;
}
}
$child = new ChildClass();
$child->sayHello(); // 出力: Hello from Trait
ParentClassよりもtraitクラスであるTraitExampleのsayHello
メソッドを優先することを明示的に示す事ができます。
as
class ChildClass extends ParentClass {
use TraitExample {
sayHello as sayHelloFromTrait;
}
}
$child = new ChildClass();
$child->sayHello(); // 出力: Hello from ParentClass
$child->sayHelloFromTrait(); // 出力: Hello from Trait
traitクラスであるTraitExampleのsayHello
メソッド名をasを使用して変更しています。これによりメソッド名の競合を避ける事ができます。
読んでいただきありがとうございました!
これからも自分のペースで記事を更新したり、新たな記事を書いていこうと思うので是非見ていただけたらなと思います!