必要な機能を持つクラスをいくつか持っているが、整理のために別々に保存したい場合、クラスを拡張して両方を持たせることはできますか?
すなわち class a extends b extends c
編集:私は一度にクラスを拡張する方法を知っていますが、複数の基本クラスを使用してクラスを即座に拡張する方法を探しています-私はこれをPHPで行うことはできませんが、に頼らずにそれを回避する方法があるはずですclass c extends b
、class b extends a
必要な機能を持つクラスをいくつか持っているが、整理のために別々に保存したい場合、クラスを拡張して両方を持たせることはできますか?
すなわち class a extends b extends c
編集:私は一度にクラスを拡張する方法を知っていますが、複数の基本クラスを使用してクラスを即座に拡張する方法を探しています-私はこれを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から利用できるようになる特性を使用できます。
トレイトは、PHPなどの単一継承言語でコードを再利用するためのメカニズムです。特性は、開発者が異なるクラス階層にあるいくつかの独立したクラスでメソッドのセットを自由に再利用できるようにすることで、単一継承のいくつかの制限を減らすことを目的としています。トレイトとクラスの組み合わせのセマンティクスは、複雑さを軽減し、多重継承とミックスインに関連する一般的な問題を回避する方法で定義されています。
これらは、より良い構成と再利用をサポートする可能性が認められているため、Perl 6、Squeak、Scala、Slate、Fortressなどの新しいバージョンの言語に統合されています。トレイトもJavaとC#に移植されています。
クラスは、単なるメソッドのコレクションではありません。クラスは、状態(フィールド)と状態を変更する動作(メソッド)の両方で、抽象的な概念を表すことになっています。継承を使用して目的の動作を取得するだけでオブジェクト指向の設計が悪いように聞こえ、正確に多くの言語が多重継承を許可しない理由:「スパゲッティ継承」を防ぐために、つまり、それぞれに必要なメソッドがあるため3つのクラスを拡張し、 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();
ビジネスの女性が論理的に受け継がれたビジネスと人間の両方を今見てください。
BusinessWoman
代わりにトレイトを使用しないのはなぜBusinessPerson
ですか?その後、実際の多重継承を行うことができます。
<?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;
?>
@Franckによる現在受け入れられている回答は機能しますが、実際には多重継承ではなく、スコープ外で定義されたクラスの子インスタンスです。__call()
省略形 もあります。$this->childInstance->method(args)
ます。「拡張」クラスでExternalClassクラスメソッドが必要な場所することをください。
キーワードマニュアルに次のextends
ように記載されています。
拡張クラスは常に単一の基本クラスに依存しています。つまり、多重継承はサポートされていません。
ただし、@ adamが正しく提案したように、これは複数の階層継承を使用することを禁止しません。
クラスを拡張したり、別のクラスを拡張したりできます。
これに関する非常に単純な例は次のようになります。
class firstInheritance{}
class secondInheritance extends firstInheritance{}
class someFinalClass extends secondInheritance{}
//...and so on...
お気づきかもしれませんが、プロセスに含まれるすべてのクラスを制御できる場合にのみ、階層ごとに複数(2+)の積分を実行できます。実行できます。つまり、このソリューションは、たとえば組み込みクラスや、単に編集することはできません。編集したい場合は、@ Franckソリューションの子インスタンスが残ります。
...そして最後にいくつかの出力の例:
class A{
function a_hi(){
echo "I am a of A".PHP_EOL."<br>".PHP_EOL;
}
}
class B extends A{
function b_hi(){
echo "I am b of B".PHP_EOL."<br>".PHP_EOL;
}
}
class C extends B{
function c_hi(){
echo "I am c of C".PHP_EOL."<br>".PHP_EOL;
}
}
$myTestInstance = new C();
$myTestInstance->a_hi();
$myTestInstance->b_hi();
$myTestInstance->c_hi();
どの出力
I am a of A
I am b of B
I am c of C
(ライブラリー/フレームワークではなく)プロジェクトでの継承を阻止し、実装ではなく、インターフェイスのプログラミングを奨励するいくつかの記事を読みました。
また、構成によってオブジェクト指向を推奨しています。クラス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を処理できるようにします。
関数が公開されているかどうかを確認する場合は、次のトピックを参照してください。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 5.4で発表されたPHPのトレイトを使用してそれを行うことができます
これはあなたのための簡単なチュートリアルです、http://culttt.com/2014/06/25/php-traits/
PHPはまだ複数のクラスの継承をサポートしていませんが、複数のインターフェイスの継承をサポートしています。
いくつかの例については、http://www.hudzilla.org/php/6_17_0.phpを参照してください。
常に良いアイデアは、関数を使用して親クラスを作成することです。つまり、このすべての機能を親に追加します。
そして、これを使用するすべてのクラスを階層的に「移動」します。私は必要です-特定の関数を書き換えてください。
プログラミング言語としてのPHPの問題の1つは、継承を1つしか持てないことです。つまり、クラスは他の1つのクラスからのみ継承できます。
ただし、多くの場合、複数のクラスから継承することは有益です。たとえば、コードの重複を防ぐために、いくつかの異なるクラスからメソッドを継承することが望ましい場合があります。
この問題は、継承の家族歴が長く、しばしば意味をなさないクラスにつながる可能性があります。
PHP 5.4では、Traitsと呼ばれる言語の新機能が追加されました。特性は、特性クラスを既存のクラスに混在させることができるという点で、ミックスインのようなものです。つまり、多重継承の問題を回避しながら、コードの重複を減らしてメリットを得ることができます。
これは本当の答えではありません。クラスが重複しています
...しかし、それは動作します:)
class A {
//do some things
}
class B {
//do some things
}
クラスcopy_BはすべてのクラスBをコピーします
class copy_B extends A {
//do some things (copy class B)
}
class A_B extends copy_B{}
今
class C_A extends A{}
class C_B extends B{}
class C_A_b extends A_B{} //extends A & B