必要な機能を備えた複数のクラスがあり、組織のために別々に保存したい場合、クラスを拡張して両方を使用できますか?
すなわちclass a extends b extends c
編集:クラスを一度に1つずつ拡張する方法は知っていますが、複数のベースクラスを使用してクラスを即座に拡張するメソッドを探しています-PHPでこれを行うことはできませんが、 class c extends b
、class b extends a
に頼らずにそれを回避する方法
編集への回答:
本当に多重継承を偽造したい場合は、マジック関数__call()を使用できます。
クラスAのユーザーの観点からは動作しますが、これは見苦しいです:
class B {
public function method_from_b($s) {
echo $s;
}
}
class C {
public function method_from_c($s) {
echo $s;
}
}
class A extends B
{
private $c;
public function __construct()
{
$this->c = new C;
}
// fake "extends C" using magic function
public function __call($method, $args)
{
$this->c->$method($args[0]);
}
}
$a = new A;
$a->method_from_b("abc");
$a->method_from_c("def");
「abcdef」を出力します
2つの基本クラスを拡張するクラスを持つことはできません。できなかった。
// this is NOT allowed (for all you google speeders)
Matron extends Nurse, HumanEntity
ただし、次のような階層を作成できます。
Matron extends Nurse
Consultant extends Doctor
Nurse extends HumanEntity
Doctor extends HumanEntity
HumanEntity extends DatabaseTable
DatabaseTable extends AbstractTable
等々。
特性を使用することもできますが、これはPHP 5.4から入手できることを願っています。
Traitsは、PHPなどの単一継承言語でコードを再利用するためのメカニズムです。 Traitは、開発者が異なるクラス階層に存在するいくつかの独立したクラスでメソッドのセットを自由に再利用できるようにすることで、単一継承の制限を軽減することを目的としています。 Traitsとクラスの組み合わせのセマンティクスは、複雑さを軽減し、多重継承とMixinに関連する典型的な問題を回避する方法で定義されます。
彼らは、より良い合成と再利用をサポートする可能性があると認められているため、Perl 6、Squeak、Scala、Slate、Fortressなどの新しいバージョンの言語に統合されています。特性は、JavaおよびC#にも移植されています。
クラスは単なるメソッドのコレクションではありません。クラスは、状態を変更する状態(フィールド)と動作(メソッド)の両方を持つ抽象概念を表すことになっています。必要な動作を得るためだけに継承を使用すると、OOの設計が悪いように聞こえます。多くの言語が多重継承を許可しない理由はまさに「スパゲティ継承」を防ぐためです。 100のメソッドと20のフィールドを継承するクラスになりますが、そのうち5つしか使用しません。
まもなくミックスインを追加する計画があると思います。
しかし、それまでは、受け入れられた答えを使用してください。これを少し抽象化して、「拡張可能な」クラスを作成できます。
class Extendable{
private $extender=array();
public function addExtender(Extender $obj){
$this->extenders[] = $obj;
$obj->setExtendee($this);
}
public function __call($name, $params){
foreach($this->extenders as $extender){
//do reflection to see if extender has this method with this argument count
if (method_exists($extender, $name)){
return call_user_func_array(array($extender, $name), $params);
}
}
}
}
$foo = new Extendable();
$foo->addExtender(new OtherClass());
$foo->other_class_method();
このモデルでは、「OtherClass」が$ fooを「知る」ことに注意してください。 OtherClassには、この関係を設定するための「setExtendee」というパブリック関数が必要です。次に、メソッドが$ fooから呼び出された場合、内部で$ fooにアクセスできます。ただし、実際の拡張クラスのようにプライベート/保護されたメソッド/変数にアクセスすることはできません。
基本クラスとして特性を使用します。次に、それらを親クラスで使用します。それを拡張します。
trait business{
function sell(){
}
function buy(){
}
function collectMoney(){
}
}
trait human{
function think(){
}
function speak(){
}
}
class BusinessPerson{
use business;
use human;
// If you have more traits bring more
}
class BusinessWoman extends BusinessPerson{
function getPregnant(){
}
}
$bw = new BusinessWoman();
$bw ->speak();
$bw->getPregnant();
現在、ビジネスの女性がビジネスと人間の両方を論理的に継承しているのを見てください。
<?php
// what if we want to extend more than one class?
abstract class ExtensionBridge
{
// array containing all the extended classes
private $_exts = array();
public $_this;
function __construct() {$_this = $this;}
public function addExt($object)
{
$this->_exts[]=$object;
}
public function __get($varname)
{
foreach($this->_exts as $ext)
{
if(property_exists($ext,$varname))
return $ext->$varname;
}
}
public function __call($method,$args)
{
foreach($this->_exts as $ext)
{
if(method_exists($ext,$method))
return call_user_method_array($method,$ext,$args);
}
throw new Exception("This Method {$method} doesn't exists");
}
}
class Ext1
{
private $name="";
private $id="";
public function setID($id){$this->id = $id;}
public function setName($name){$this->name = $name;}
public function getID(){return $this->id;}
public function getName(){return $this->name;}
}
class Ext2
{
private $address="";
private $country="";
public function setAddress($address){$this->address = $address;}
public function setCountry($country){$this->country = $country;}
public function getAddress(){return $this->address;}
public function getCountry(){return $this->country;}
}
class Extender extends ExtensionBridge
{
function __construct()
{
parent::addExt(new Ext1());
parent::addExt(new Ext2());
}
public function __toString()
{
return $this->getName().', from: '.$this->getCountry();
}
}
$o = new Extender();
$o->setName("Mahdi");
$o->setCountry("Al-Ahwaz");
echo $o;
?>
(ライブラリ/フレームワークとは対照的に)プロジェクトの継承を阻止し、実装に反対するプログラムに対するインターフェイスを奨励するいくつかの記事を読みました。
また、構成によってOOを提唱しています。クラスaおよびbの関数が必要な場合、cにこのタイプのメンバー/フィールドを持たせます。
class C
{
private $a, $b;
public function __construct($x, $y)
{
$this->a = new A(42, $x);
$this->b = new B($y);
}
protected function DoSomething()
{
$this->a->Act();
$this->b->Do();
}
}
多重継承は、インターフェースレベルで機能するようです。 php 5.6.1でテストを行いました。
動作するコードは次のとおりです。
<?php
interface Animal
{
public function sayHello();
}
interface HairyThing
{
public function plush();
}
interface Dog extends Animal, HairyThing
{
public function bark();
}
class Puppy implements Dog
{
public function bark()
{
echo "ouaf";
}
public function sayHello()
{
echo "hello";
}
public function plush()
{
echo "plush";
}
}
echo PHP_VERSION; // 5.6.1
$o = new Puppy();
$o->bark();
$o->plush();
$o->sayHello(); // displays: 5.6.16ouafplushhello
私はそれが可能だとは思いませんでしたが、私はSwiftMailerのソースコードで、次の定義を持つSwift_Transport_IoBufferクラスを見つけました。
interface Swift_Transport_IoBuffer extends Swift_InputByteStream, Swift_OutputByteStream
まだ遊んでいませんでしたが、共有するのは面白いかもしれないと思いました。
「多重継承」の問題を次のように解決しました。
class Session {
public $username;
}
class MyServiceResponsetype {
protected $only_avaliable_in_response;
}
class SessionResponse extends MyServiceResponsetype {
/** has shared $only_avaliable_in_response */
public $session;
public function __construct(Session $session) {
$this->session = $session;
}
}
このようにして、MyServiceResponsetypeを拡張するSessionResponse内でセッションを操作し、それ自体でSessionを処理することができます。
PHP 5.4の時点で発表されたPHPのTraitsを使用して、それを行うことができます。
ここにあなたのための簡単なチュートリアルがあります http://culttt.com/2014/06/25/php-traits/
あなたが何を達成しようとしているかを正確に知らないので、この場合、継承ではなく構成を使用するようにアプリケーションを再設計する可能性を検討することをお勧めします。
PHPは複数のクラスの継承をまだサポートしていませんが、複数のインターフェイスの継承をサポートしています。
いくつかの例については、 http://www.hudzilla.org/php/6_17_0.php を参照してください。
関数がパブリックかどうかを確認する場合は、次のトピックを参照してください: https://stackoverflow.com/a/4160928/2226755
そして、多くの引数またはそうでない引数に対してcall_user_func_array(...)メソッドを使用します。
このような :
class B {
public function method_from_b($s) {
echo $s;
}
}
class C {
public function method_from_c($l, $l1, $l2) {
echo $l.$l1.$l2;
}
}
class A extends B {
private $c;
public function __construct() {
$this->c = new C;
}
public function __call($method, $args) {
if (method_exists($this->c, $method)) {
$reflection = new ReflectionMethod($this->c, $method);
if (!$reflection->isPublic()) {
throw new RuntimeException("Call to not public method ".get_class($this)."::$method()");
}
return call_user_func_array(array($this->c, $method), $args);
} else {
throw new RuntimeException("Call to undefined method ".get_class($this)."::$method()");
}
}
}
$a = new A;
$a->method_from_b("abc");
$a->method_from_c("d", "e", "f");
PHPは多重継承を許可していませんが、複数のインターフェースを実装することでできます。実装が「重い」場合は、個別のクラスの各インターフェイスに骨格実装を指定します。次に、すべてのインターフェイスクラスをこれらの骨格実装に委任するを介してオブジェクトの包含を使用できます。
常に良いアイデアは、関数を使用して親クラスを作成することです...つまり、このすべての機能を親に追加します。
そして、これを使用するすべてのクラスを階層的に「移動」します。必要な-特定の関数を書き換えます。
プログラミング言語としてのPHPの問題の1つは、単一の継承しか持てないという事実です。つまり、クラスは他の1つのクラスからのみ継承できます。
ただし、多くの場合、複数のクラスから継承することは有益です。たとえば、コードの重複を防ぐために、いくつかの異なるクラスからメソッドを継承することが望ましい場合があります。
この問題は、多くの場合意味をなさない相続の長い家族歴を有するクラスにつながる可能性があります。
PHP 5.4では、Traitsとして知られる言語の新しい機能が追加されました。 TraitはMixinのようなもので、Traitクラスを既存のクラスにミックスできます。これは、コードの重複を減らし、多重継承の問題を回避しながら利益を得ることができることを意味します。