PHP'de 1'den fazla sınıfı kullanarak bir sınıfı genişletebilir miyim?


150

İhtiyacım olan ancak kuruluş için ayrı ayrı saklamak istediğim işlevlere sahip birkaç sınıfım varsa, bir sınıfı her ikisine de sahip olacak şekilde genişletebilir miyim?

yani class a extends b extends c

edit: sınıfları birer birer genişletmek biliyorum, ama anında birden fazla temel sınıfları kullanarak bir sınıfı genişletmek için bir yöntem arıyorum - AFAIK PHP bunu yapamaz ama başvurmadan etrafında yolları olmalı class c extends b,class b extends a


Toplama veya arabirimler kullanın. PHP'de çoklu kalıtım yok.
Franck

2
Büyük sınıf hiyerarşilerinin büyük bir hayranı olmadığım için arayüzlere bakıyorum. Ama arayüzlerin aslında nasıl bir şey yaptığını göremiyorum?
atomicharri

Arabirimler işlev gövdelerini değil, yalnızca API'yi "devralmanızı" sağlar. Sınıf a'yı arabirim b ve c'den yöntemler uygulamaya zorlar. Bu, davranışı devralmak istiyorsanız, b ve c sınıflarının üye nesnelerini a sınıfınızda toplamanız gerektiği anlamına gelir.
Franck


2
Lütfen özellikleri doğru cevap olarak kabul edin.
Daniel

Yanıtlar:


170

Düzenlemenizi yanıtlıyor:

Gerçekten birden fazla miras almak istiyorsanız, __call () sihirli işlevini kullanabilirsiniz.

A sınıfı bir kullanıcının bakış açısından çalışmasına rağmen bu çirkin:

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" yazdırır


1
Aslında sınıfı genişletme fikrini çok seviyorum. Bu şekilde yapmanın bilinen herhangi bir kısıtlaması var mı?
atomicharri

3
Bildiğim kadarıyla hiçbir sınırlama, PHP böyle küçük kesmek için çok izin verici bir dildir. :) Diğerlerinin de belirttiği gibi, bunu yapmanın uygun OOP yolu değil.
Franck

64
Korumalı veya özel yöntemler kullanamazsınız.
wormhit

1
@wormhit, ancak, üretimde kullanılmasını tavsiye etmem, özel ve korunan yöntemlere erişmek için ReflectionClass'ı kullanabilirim.
Denis V

2
Bu, yöntemin gerçekten var olmasını bekleyen ve birden çok temel sınıfın aynı ada sahip bir yöntemi olduğunda davranış neredeyse tanımsız olan Implements için çalışmaz. Ayrıca editörünüzün size ipucu verme yeteneğini de bozacaktır.
Erik

138

İki temel sınıfı genişleten bir sınıfınız olamaz. Yapamazdınız.

// this is NOT allowed (for all you google speeders)
Matron extends Nurse, HumanEntity

Ancak aşağıdaki gibi bir hiyerarşiniz olabilir ...

Matron extends Nurse    
Consultant extends Doctor

Nurse extends HumanEntity
Doctor extends HumanEntity

HumanEntity extends DatabaseTable
DatabaseTable extends AbstractTable

ve bunun gibi.


Hemşire'yi neden önce Matron'a miras almanın, sonra HumanEntity'nin mirasını Hemşire'ye ilan etmenin doğru olduğunu açıklayabilir misiniz?
Qwerty

7
@Qwerty Çünkü Matron, Hemşire'nin ek niteliklerine sahipken, hemşire insanın tüm niteliklerine sahiptir. Bu nedenle, Matron bir insan hemşire ve nihayet Matron yeteneklerine sahip
J-Dizzle

58

Umarım PHP 5.4'ten ulaşılabilecek olan özellikleri kullanabilirsiniz.

Özellikler, PHP gibi tek miras dillerinde kodun yeniden kullanılması için bir mekanizmadır. Bir Özelliğin, bir geliştiricinin farklı sınıf hiyerarşilerinde yaşayan birkaç bağımsız sınıfta yöntem kümelerini serbestçe yeniden kullanmasını sağlayarak tek mirasın bazı sınırlamalarını azaltması amaçlanır. Özelliklerin ve sınıfların kombinasyonunun semantiği, karmaşıklığı azaltan ve çoklu kalıtım ve Mixins ile ilişkili tipik problemleri önleyen bir şekilde tanımlanır.

Daha iyi kompozisyon ve yeniden kullanımı destekleme potansiyellerinden dolayı tanınmaktadırlar, bu nedenle Perl 6, Squeak, Scala, Slate ve Fortress gibi dillerin daha yeni sürümlerine entegrasyonları vardır. Özellikler Java ve C # 'a da taşınmıştır.

Daha fazla bilgi: https://wiki.php.net/rfc/traits


Onları kötüye kullanmadığınızdan emin olun. Temelde, nesnelere uyguladığınız statik işlevlerdir. Onları kötüye kullanarak bir karmaşa yaratabilirsiniz.
Nikola Petkanski

2
Bunun seçilen cevap olmadığına inanamıyorum.
Daniel

18

Sınıflar sadece yöntemlerin koleksiyonları değildir. Bir sınıfın hem durumu (alanları) hem de durumu değiştiren davranışı (yöntemleri) içeren soyut bir kavramı temsil etmesi beklenir. Kötü OO tasarımı gibi bazı istenen davranış seslerini almak için kalıtım kullanmak ve birçok dilin birden fazla kalıtıma izin vermemesinin nedeni tam olarak: "spagetti kalıtımını" önlemek için, yani her birinin ihtiyacınız olan bir yöntemi olduğu için 3 sınıfı genişletmek ve 100 metodu ve 20 alanı miras alan ancak sadece 5 tanesini kullanan bir sınıf.


1
OP'nin talebinin "kötü OO tasarımı" oluşturduğuna dair iddianızı ele alacağım ; birden fazla kaynaktan bir sınıfa yöntem eklemenin mimari olarak iyi bir fikir olduğu konumunu desteklemek için Mixins'in ortaya çıkışına bakın . Bununla birlikte, PHP'nin optimum bir "tasarım" elde etmek için en uygun dil özelliklerini sağlamadığını, ancak yaklaşık olarak kötü bir fikir olduğunu tahmin etmek için mevcut özellikleri kullanmak anlamına gelmediğini size vereceğim; @ Franck'ın cevabına bakın.
MikeSchinkel

15

Yakında mix-in eklemek için planlar var, sanırım.

Ama o zamana kadar, kabul edilen cevapla devam et. "Genişletilebilir" bir sınıf yapmak için bunu biraz soyutlayabilirsiniz:

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();

Bu modelde "OtherClass" 'ın $ foo hakkında bilgi sahibi olduğunu unutmayın. OtherClass'ın bu ilişkiyi kurmak için "setExtendee" adında bir genel işlevi olması gerekir. Daha sonra, yöntemleri $ foo'dan çağrılırsa, dahili olarak $ foo'ya erişebilir. Bununla birlikte, gerçek bir genişletilmiş sınıf gibi herhangi bir özel / korumalı yönteme / değişkene erişemez.


12

Temel sınıflar olarak özellikleri kullanın. Sonra bunları bir üst sınıfta kullanın. Uzat .

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();

Şimdi iş kadını mantıklı iş ve insan hem miras bakın;


Php 5.3 ile takılıp kaldım Hiçbir özellik desteği yok: D
Nishchal Gautam

Neden özelliği BusinessWomanyerine kullanmıyorsunuz BusinessPerson? O zaman gerçek çoklu mirasa sahip olabilirsiniz.
SOFe

@ SOFe, çünkü BusinessMan bunu genişletebilir. Yani kim hiç iş yapıyor busines kişi genişletmek ve otomatik olarak özellikleri alır
Alice

Bunu yapma şekliniz, BusinessPerson'un insan adı verilen soyut bir sınıfı genişlettiği tek mirasta mümkün olandan farksızdır. Demek istediğim, örneğin gerçekten çoklu mirasa ihtiyacı yok.
SOFe

BusinessPerson gerekli olana kadar inanıyorum, BusinessWoman diğer özellikleri doğrudan miras alabilirdi. Ama burada denediğim şey, her iki özelliği de bir arabulucu sınıfta tutmak ve daha sonra gerektiği yerde kullanmak. Yani yine başka bazı yerlerde gerekirse her iki özelliği dahil unutabilir (BusinessMan açıklandığı gibi). Her şey kod stilleri ile ilgilidir. Doğrudan sınıfınıza dahil etmek isterseniz. bununla devam edebilirsiniz - bu konuda kesinlikle haklısınız.
Alice

9
<?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;
?>

4
Yanıtınız, kodunuzun bir açıklamasını ve sorunun nasıl çözüldüğünü açıklayan bir açıklama içermelidir.
AbcAeffchen

1
Neredeyse açıklayıcıdır, ancak kod boyunca yorum yapmak harika olurdu.
Heroselohim

şimdi Ext1 ve Ext2'nin her ikisi de Ext1 cw olarak adlandırılacak aynı ada sahip bir işleve sahipse, parametrelere hiç bağlı olmayacaktır.
Alice

8

@Franck tarafından şu anda kabul edilen cevap işe yarayacak, ancak aslında çoklu miras değil, kapsam dışında tanımlanan sınıfın alt örneği, ayrıca __call()kısayol var - $this->childInstance->method(args)"genişletilmiş" sınıfta ExternalClass sınıf yöntemine ihtiyacınız olan her yerde kullanmayı düşünün .

Kesin cevap

Hayır , extendsanahtar kelime kılavuzunun dediği gibi, gerçekten değil, sırasıyla yapamazsınız :

Genişletilmiş sınıf her zaman tek bir temel sınıfa bağlıdır, yani çoklu kalıtım desteklenmez.

Gerçek cevap

Ancak @adam'ın doğru bir şekilde önerdiği gibi, bu, birden çok hiyerarşik kalıtım kullanmanızı yasaklamaz.

Bir sınıfı diğeriyle diğerini diğeriyle genişletebilirsiniz ...

Bu konuda oldukça basit bir örnek:

class firstInheritance{}
class secondInheritance extends firstInheritance{}
class someFinalClass extends secondInheritance{}
//...and so on...

Önemli Not

Fark etmiş olabileceğiniz gibi , yalnızca sürece dahil olan tüm sınıflar üzerinde kontrolünüz varsa, hiyerarşi ile çoklu (2+) dürüstlük yapabilirsiniz - yani, bu çözümü örneğin yerleşik sınıflarla veya sınıflarla basitçe düzenleyemez - bunu yapmak istiyorsanız, @Franck çözümü - alt örnekleri ile kalırsınız.

... Ve son olarak bazı çıktılarla örnek:

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();

Hangi çıktılar

I am a of A 
I am b of B 
I am c of C 

6

Projelerde kalıtımı caydıran (kitaplıkların / çerçevelerin aksine) ve uygulamalara karşı hayır arayüzleri programlamayı teşvik eden birkaç makale okudum.
Ayrıca bileşime göre OO'yu savunurlar: a ve b sınıfındaki işlevlere ihtiyacınız varsa, c'nin bu türden üyelere / alanlara sahip olmasını sağlayın:

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();
    }
}

Bu, Franck ve Sam'in gösterdiği aynı şey haline gelir. Tabii ki bileşimi açıkça kullanmayı seçerseniz, bağımlılık enjeksiyonu kullanmalısınız .
MikeSchinkel

3

Çoklu kalıtım arayüz seviyesinde çalışıyor gibi görünüyor. Php 5.6.1 üzerinde bir test yaptım.

İşte çalışan bir kod:

<?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

Bunun mümkün olduğunu düşünmedim, ancak SwiftMailer kaynak kodunda, aşağıdaki tanıma sahip Swift_Transport_IoBuffer sınıfında tökezledim:

interface Swift_Transport_IoBuffer extends Swift_InputByteStream, Swift_OutputByteStream

Henüz onunla oynamadım ama paylaşmanın ilginç olabileceğini düşündüm.


1
ilginç ve ilgisiz.
Yevgeniy Afanasyev

1

Ben sadece "çoklu kalıtım" sorunumu çözdüm:

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;
    }

}

Bu şekilde MyServiceResponsetype'ı hala Session tek başına ele alabilecek şekilde genişleten bir SessionResponse içindeki oturumu değiştirme gücüne sahibim.


1

Bir işlevin herkese açık olup olmadığını kontrol etmek istiyorsanız şu konuya bakın: https://stackoverflow.com/a/4160928/2226755

Ve birçok bağımsız değişken için ya da bağımsız değişken için call_user_func_array (...) yöntemini kullanın.

Bunun gibi :

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");


0

PHP henüz birden fazla sınıf mirasını desteklemez, ancak birden çok arabirim mirasını destekler.

Bazı örnekler için bkz. Http://www.hudzilla.org/php/6_17_0.php .


Hala? Yapacaklarından şüpheliyim. Modern OO çoklu mirastan vazgeçirir, dağınık olabilir.
PhiLho

0

PHP birden fazla kalıtıma izin vermez, ancak birden fazla arabirim uygulamakla yapabilirsiniz. Uygulama "ağır" ise, ayrı bir sınıftaki her arabirim için iskeletsel uygulama sağlayın . Daha sonra, tüm arayüz sınıfını nesne tutma yoluyla bu iskelet uygulamalarına devredebilirsiniz .


0

Tam olarak ne elde etmeye çalıştığınızı bilmeden, bu durumda miras yerine kompozisyon kullanmak için uygulamanızı yeniden tasarlama olasılığını araştırmanızı öneririm.


0

Her zaman iyi bir fikir, işlevlerle ebeveyn sınıfını yapmaktır ... yani tüm işlevselliği ebeveyne eklemek.

Ve bunu hiyerarşik olarak kullanan tüm sınıfları "taşı". Ihtiyacım - belirli işlevleri yeniden yazma.


0

Bir programlama dili olarak PHP'nin sorunlarından biri, sadece tek bir mirasa sahip olabilmenizdir. Bu, bir sınıfın yalnızca bir sınıftan miras alabileceği anlamına gelir.

Bununla birlikte, çoğu zaman birden fazla sınıftan miras almak faydalı olacaktır. Örneğin, kod çoğaltmasını önlemek için yöntemlerin birkaç farklı sınıftan miras alınması istenebilir.

Bu sorun, uzun bir aile mirası olan ve çoğu zaman mantıklı olmayan sınıfa yol açabilir.

PHP 5.4'te, dil olarak bilinen yeni bir özellik eklendi. Bir Özellik, Trait sınıflarını mevcut bir sınıfa karıştırmanıza izin verdiği için bir Mixin gibidir. Bu, kod çoğaltmayı azaltabileceğiniz ve çoklu kalıtım sorunlarından kaçınırken avantajlardan yararlanabileceğiniz anlamına gelir.

özellikleri


-1

Bu gerçek bir cevap değil, mükerrer sınıfımız var

... ama işe yarıyor :)

class A {
    //do some things
}

class B {
    //do some things
}

class copy_B, tüm B sınıfını kopyalar

class copy_B extends A {

    //do some things (copy class B)
}

class A_B extends copy_B{}

şimdi

class C_A extends A{}
class C_B extends B{}
class C_A_b extends A_B{}  //extends A & B

-3

A sınıfı B'yi genişletir {}

B sınıfı C'yi genişletir {}

Sonra A hem B hem de C'yi uzattı


2
@rostamiani çünkü bu yöntem de sınıfı değiştiriyor B. Ne zaman en çok istediği iki ilgisiz sınıfları sahip olmaktır Bve C(muhtemelen farklı kütüphanelerden) ve eş zamanlı olarak miras olsun A, böyle Ahem her şeyi içerir Bve Cancak Bbir şey içermiyor Ctersi ve yardımcısı.
SOFe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.