概要
今日はめずらしく真面目なプログラミングの記事です。
interface についての説明はこちらをどうぞ。
interface とは本来抽象メソッドしか持つことができません。しかし、とある具象メソッド内で使用されるパラメータの実装を強制したい場合どうすればよいか、わたしなりに考えてみましたので今日はそんな話をしたいとおもいます。
状況
まずは下記のサンプルをご覧ください。
class Common
{
public function hoge()
{
return 'called common method';
}
}
use Common;
class A
{
public function foo()
{
$common = new Common();
$common->hoge();
}
}
use Common;
class B
{
public function bar()
{
$common = new Common();
$common->hoge();
}
}
まあ特に変わったところのないクラスです。
このとき、Common クラスの hoge() メソッド の内部で使用するパラメータを動的に変更したい という場合はどうするのが良いでしょうか。
手取り早いのは引数を追加して外から渡す方法でしょう。
メソッドの引数でもいいですし、コンストラクタの引数でもいいとおもいます。
class Common
{
public function hoge(string $str)
{
return "{$str}: called common method";
}
}
これでも解決するのですが、
- 引数を準備する際に単純な値のリテラルではすまない
- パラメータの引数が多くなる
といった場合にちょっと困ってきます。
このとき、interface で必要なパラメータの準備を強制させることができれば、新しいクラスへの導入をよりスムーズに行えるメリットもあるのでできればそのようにしたいのですが、interface だと抽象メソッドしか書けないのでどうしたものかというところです。
というわけで本題
trait を組み合わせることで、限りなく近いものを実現できると考えました。
具体的にはこのようなイメージです。
- trait → 具象メソッド
- interface → 抽象メソッド
サンプル
先ほどの例を書き換えてみます。
まず共通処理を trait にします
このとき、抽象メソッドを trait 内に組み込んでしまう のがポイントです。
trait Common
{
public function hoge()
{
$param = $this->getParam();
return "{$param}:called common method";
}
}
実装を強制したい部分をinterfaceにします
interface Params
{
/**
* @return string
*/
public function getParam(): string;
}
使用クラスに導入します
作った trait と interface を、導入したいクラスに適用させます
class A implements Params
{
use Common;
public function foo()
{
$this->hoge();
}
public function getParam(): string
{
return 'class A param';
}
}
まとめ
このように、trait と interface をセットで使えば、「この trait と interface を読み込んで、あとは抽象メソッドを実装すれば、メソッド****がお好きなクラスで使えますよ」みたいなことが可能です。
もっとスマートな方法があるのかもしれませんが、事例のひとつとしてよかったら参考にしてみてください。
※追記
スマートな方法をコメントで教えていただけました。
@nishimura さんありがとうございました!