Ciri vs antarmuka


344

Saya telah mencoba untuk belajar di PHP akhir-akhir ini, dan saya menemukan diri saya terpaku pada sifat-sifat. Saya memahami konsep penggunaan kembali kode horisontal dan tidak ingin mewarisi dari kelas abstrak. Apa yang saya tidak mengerti adalah: Apa perbedaan penting antara menggunakan sifat versus antarmuka?

Saya sudah mencoba mencari posting blog yang layak atau artikel yang menjelaskan kapan harus menggunakan satu atau yang lain, tetapi contoh-contoh yang saya temukan sejauh ini tampak sangat mirip sehingga identik.


6
antarmuka tidak memiliki kode apa pun di badan fungsi. mereka sebenarnya tidak memiliki fungsi tubuh.
hakre

2
Terlepas dari jawaban saya yang banyak dipilih, saya ingin menyatakan bahwa saya umumnya anti-sifat / mixin . Periksa transkrip obrolan ini untuk membaca bagaimana sifat sering merusak praktik OOP yang solid .
rdlowrey

2
Saya berpendapat sebaliknya. Setelah bekerja dengan PHP selama bertahun-tahun sebelum dan sejak munculnya sifat-sifat, saya pikir mudah untuk membuktikan nilainya. Cukup baca contoh praktis ini yang memungkinkan 'model gambar' juga berjalan dan berbicara seperti Imagickbenda, mengurangi semua kegembiraan yang dibutuhkan di masa lalu sebelum sifat-sifat.
quickshiftin

Ciri dan antarmuka mirip. Perbedaan utama adalah bahwa Ciri memungkinkan Anda untuk menerapkan metode, antarmuka tidak.
John

Jawaban:


238

Antarmuka mendefinisikan serangkaian metode yang harus diterapkan oleh kelas pelaksana.

Ketika suatu sifat adalah useimplementasi dari metode datang juga - yang tidak terjadi dalam Interface.

Itulah perbedaan terbesar.

Dari Horizontal Reuse untuk PHP RFC :

Ciri adalah mekanisme untuk menggunakan kembali kode dalam bahasa pewarisan tunggal seperti PHP. Trait dimaksudkan untuk mengurangi beberapa batasan pewarisan tunggal dengan memungkinkan pengembang untuk menggunakan kembali set metode secara bebas di beberapa kelas independen yang hidup dalam hierarki kelas yang berbeda.


2
@ JREAM Dalam praktek, tidak ada. Pada kenyataannya, lebih dari itu.
Alec Gorge

79
Kecuali bahwa sifat-sifat sama sekali bukan antarmuka. Antarmuka adalah spesifikasi yang dapat diperiksa. Ciri tidak dapat diperiksa, karena itu hanya implementasi. Mereka adalah kebalikan dari antarmuka.
Kalimat

195
Ciri-ciri pada dasarnya adalah copy dan paste berbantuan bahasa .
Shahid

10
Itu bukan metafora. Itu membantai arti kata. Ini seperti menggambarkan sebuah kotak sebagai permukaan dengan volume.
cleong

6
Untuk memperluas komentar ircmaxell dan Shadi: Anda dapat memeriksa apakah suatu objek mengimplementasikan antarmuka (melalui instanceof), dan Anda dapat memastikan bahwa argumen metode mengimplementasikan antarmuka melalui tip ketik dalam tanda tangan metode. Anda tidak dapat melakukan pemeriksaan yang sesuai untuk sifat.
Brian D'Astous

530

Pengumuman Layanan Publik:

Saya ingin menyatakan untuk catatan bahwa saya percaya ciri-ciri hampir selalu berbau kode dan harus dihindari dalam mendukung komposisi. Menurut saya, pewarisan tunggal sering disalahgunakan sampai menjadi anti-pola dan pewarisan berganda hanya menambah masalah ini. Dalam kebanyakan kasus, Anda akan jauh lebih baik dilayani dengan memilih komposisi daripada warisan (baik tunggal atau banyak). Jika Anda masih tertarik dengan sifat dan hubungannya dengan antarmuka, baca terus ...


Mari kita mulai dengan mengatakan ini:

Pemrograman Berorientasi Objek (OOP) bisa menjadi paradigma yang sulit untuk dipahami. Hanya karena Anda menggunakan kelas tidak berarti kode Anda Berorientasi Objek (OO).

Untuk menulis kode OO, Anda perlu memahami bahwa OOP benar-benar tentang kemampuan objek Anda. Anda harus berpikir tentang kelas dalam hal apa yang dapat mereka lakukan daripada apa yang sebenarnya mereka lakukan . Ini sangat kontras dengan pemrograman prosedural tradisional di mana fokusnya adalah membuat sedikit kode "melakukan sesuatu."

Jika kode OOP adalah tentang perencanaan dan desain, antarmuka adalah cetak biru dan objek adalah rumah yang dibangun sepenuhnya. Sementara itu, sifat hanyalah cara untuk membantu membangun rumah yang ditata dengan cetak biru (antarmuka).

Antarmuka

Jadi, mengapa kita harus menggunakan antarmuka? Sederhananya, antarmuka membuat kode kita kurang rapuh. Jika Anda meragukan pernyataan ini, tanyakan siapa saja yang terpaksa mempertahankan kode lawas yang tidak ditulis terhadap antarmuka.

Antarmuka adalah kontrak antara programmer dan / kode nya. Antarmuka mengatakan, "Selama Anda bermain dengan aturan saya, Anda dapat menerapkan saya sesuka Anda dan saya berjanji saya tidak akan melanggar kode Anda yang lain."

Jadi sebagai contoh, pertimbangkan skenario dunia nyata (tidak ada mobil atau widget):

Anda ingin menerapkan sistem caching untuk aplikasi web untuk mengurangi beban server

Anda mulai dengan menulis sebuah kelas untuk menyimpan tanggapan permintaan menggunakan APC:

class ApcCacher
{
  public function fetch($key) {
    return apc_fetch($key);
  }
  public function store($key, $data) {
    return apc_store($key, $data);
  }
  public function delete($key) {
    return apc_delete($key);
  }
}

Kemudian, di objek respons HTTP Anda, Anda memeriksa hit cache sebelum melakukan semua pekerjaan untuk menghasilkan respons aktual:

class Controller
{
  protected $req;
  protected $resp;
  protected $cacher;

  public function __construct(Request $req, Response $resp, ApcCacher $cacher=NULL) {
    $this->req    = $req;
    $this->resp   = $resp;
    $this->cacher = $cacher;

    $this->buildResponse();
  }

  public function buildResponse() {
    if (NULL !== $this->cacher && $response = $this->cacher->fetch($this->req->uri()) {
      $this->resp = $response;
    } else {
      // Build the response manually
    }
  }

  public function getResponse() {
    return $this->resp;
  }
}

Pendekatan ini sangat bagus. Tetapi mungkin beberapa minggu kemudian Anda memutuskan ingin menggunakan sistem cache berbasis file alih-alih APC. Sekarang Anda harus mengubah kode pengontrol Anda karena Anda telah memprogram pengontrol Anda untuk bekerja dengan fungsionalitas ApcCacherkelas daripada antarmuka yang mengekspresikan kemampuan ApcCacherkelas. Katakanlah bukannya di atas Anda telah membuat Controllerkelas bergantung pada CacherInterfacebukan beton ApcCacherseperti:

// Your controller's constructor using the interface as a dependency
public function __construct(Request $req, Response $resp, CacherInterface $cacher=NULL)

Agar sesuai dengan yang Anda tentukan antarmuka Anda seperti:

interface CacherInterface
{
  public function fetch($key);
  public function store($key, $data);
  public function delete($key);
}

Pada gilirannya Anda memiliki kedua Anda ApcCacherdan FileCacherkelas baru Anda menerapkan CacherInterfacedan Anda memprogram Controllerkelas Anda untuk menggunakan kemampuan yang dibutuhkan oleh antarmuka.

Contoh ini (semoga) menunjukkan bagaimana pemrograman ke antarmuka memungkinkan Anda untuk mengubah implementasi internal kelas Anda tanpa khawatir jika perubahan itu akan merusak kode Anda yang lain.

Sifat

Sifat, di sisi lain, hanyalah sebuah metode untuk menggunakan kembali kode. Antarmuka tidak boleh dianggap sebagai alternatif yang eksklusif untuk sifat. Faktanya, menciptakan ciri-ciri yang memenuhi kemampuan yang dibutuhkan oleh sebuah antarmuka adalah kasus penggunaan yang ideal .

Anda hanya boleh menggunakan ciri-ciri ketika beberapa kelas berbagi fungsi yang sama (kemungkinan didikte oleh antarmuka yang sama). Tidak ada gunanya menggunakan sifat untuk menyediakan fungsionalitas untuk satu kelas: yang hanya mengaburkan apa yang dilakukan kelas dan desain yang lebih baik akan memindahkan fungsionalitas sifat ke kelas yang relevan.

Pertimbangkan penerapan sifat berikut:

interface Person
{
    public function greet();
    public function eat($food);
}

trait EatingTrait
{
    public function eat($food)
    {
        $this->putInMouth($food);
    }

    private function putInMouth($food)
    {
        // Digest delicious food
    }
}

class NicePerson implements Person
{
    use EatingTrait;

    public function greet()
    {
        echo 'Good day, good sir!';
    }
}

class MeanPerson implements Person
{
    use EatingTrait;

    public function greet()
    {
        echo 'Your mother was a hamster!';
    }
}

Contoh yang lebih konkret: bayangkan Anda FileCacherdan Anda ApcCacherdari diskusi antarmuka menggunakan metode yang sama untuk menentukan apakah entri cache basi dan harus dihapus (jelas ini tidak terjadi dalam kehidupan nyata, tetapi ikuti saja). Anda bisa menulis sifat dan memungkinkan kedua kelas untuk menggunakannya untuk kebutuhan antarmuka umum.

Satu kata terakhir dari peringatan: berhati-hatilah untuk tidak berlebihan dengan sifat-sifat. Seringkali sifat digunakan sebagai penopang untuk desain yang buruk ketika implementasi kelas yang unik sudah cukup. Anda harus membatasi sifat untuk memenuhi persyaratan antarmuka untuk desain kode terbaik.


69
Saya benar-benar mencari jawaban sederhana cepat yang disediakan di atas, tetapi saya harus mengatakan bahwa Anda memberikan jawaban mendalam yang sangat baik yang akan membantu membuat perbedaan menjadi lebih jelas bagi orang lain, pujian.
datguywhowanders

35
"[C] karakter yang memenuhi kemampuan yang diperlukan oleh antarmuka di kelas yang diberikan adalah kasus penggunaan yang ideal". Tepat: +1
Alec Gorge

5
Apakah adil untuk mengatakan bahwa ciri-ciri dalam PHP mirip dengan mixin dalam bahasa lain?
Eno

5
@igorpan Untuk semua maksud dan tujuan saya akan mengatakan pelaksanaan sifat PHP adalah sama dengan beberapa warisan. Perlu dicatat bahwa jika suatu sifat dalam PHP menentukan sifat statis maka setiap kelas yang menggunakan sifat tersebut akan memiliki salinan sendiri dari sifat statis. Lebih penting lagi ... melihat bagaimana posting ini sekarang sangat tinggi di SERP ketika mencari ciri-ciri saya akan menambahkan pengumuman layanan publik ke bagian atas halaman. Anda harus membacanya.
rdlowrey

3
+1 untuk penjelasan mendalam. Saya berasal dari latar belakang ruby, di mana mixin digunakan BANYAK; hanya untuk menambahkan dua sen saya, aturan praktis yang kami gunakan dapat diterjemahkan dalam php sebagai "jangan menerapkan metode yang bermutasi $ this in traits". Ini mencegah sejumlah besar sesi debug yang gila ... Mixin juga TIDAK boleh membuat asumsi apa pun di kelas yang akan digabungkan (atau Anda harus membuatnya sangat jelas dan mengurangi ketergantungan ke minimum). Dalam hal ini, saya menemukan ide Anda tentang penerapan sifat antarmuka yang bagus.
m_x

67

A traitpada dasarnya adalah implementasi PHP dari a mixin, dan secara efektif seperangkat metode ekstensi yang dapat ditambahkan ke kelas apa pun melalui penambahan trait. Metode kemudian menjadi bagian dari implementasi kelas itu, tetapi tanpa menggunakan warisan .

Dari Manual PHP (penekanan pada tambang):

Ciri adalah mekanisme untuk menggunakan kembali kode dalam bahasa pewarisan tunggal seperti PHP. ... Ini adalah tambahan untuk warisan tradisional dan memungkinkan komposisi perilaku yang horizontal; yaitu, penerapan anggota kelas tanpa memerlukan warisan.

Sebuah contoh:

trait myTrait {
    function foo() { return "Foo!"; }
    function bar() { return "Bar!"; }
}

Dengan sifat di atas didefinisikan, sekarang saya dapat melakukan hal berikut:

class MyClass extends SomeBaseClass {
    use myTrait; // Inclusion of the trait myTrait
}

Pada titik ini, ketika saya membuat instance kelas MyClass , ia memiliki dua metode, yang disebut foo()dan bar()- yang berasal dari myTrait. Dan - perhatikan bahwa traitmetode -defined sudah memiliki tubuh metode - yang Interfacemetode -defined tidak bisa.

Selain itu - PHP, seperti banyak bahasa lainnya, menggunakan a model pewarisan tunggal - yang berarti bahwa suatu kelas dapat diturunkan dari beberapa antarmuka, tetapi tidak dari beberapa kelas. Namun, kelas PHP dapat memiliki beberapa traitinklusi - yang memungkinkan programmer untuk memasukkan bagian yang dapat digunakan kembali - seperti yang mungkin terjadi jika menyertakan beberapa kelas dasar.

Beberapa hal yang perlu diperhatikan:

                      -----------------------------------------------
                      |   Interface   |  Base Class   |    Trait    |
                      ===============================================
> 1 per class         |      Yes      |       No      |     Yes     |
---------------------------------------------------------------------
Define Method Body    |      No       |       Yes     |     Yes     |
---------------------------------------------------------------------
Polymorphism          |      Yes      |       Yes     |     No      |
---------------------------------------------------------------------

Polimorfisme:

Dalam contoh sebelumnya, di mana MyClass memanjang SomeBaseClass , MyClass adalah turunan dari SomeBaseClass. Dengan kata lain, array seperti SomeBaseClass[] basesdapat berisi instance dari MyClass. Demikian pula, jika MyClassdiperluas IBaseInterface, array IBaseInterface[] basesdapat berisi instance dari MyClass. Tidak ada konstruksi polimorfik yang tersedia dengan trait- karena pada traitdasarnya hanya kode yang disalin untuk kenyamanan programmer ke setiap kelas yang menggunakannya.

Hak lebih tinggi:

Seperti yang dijelaskan dalam Manual:

Anggota yang diwarisi dari kelas dasar ditimpa oleh anggota yang dimasukkan oleh suatu Trait. Urutan didahulukan adalah bahwa anggota dari kelas saat ini mengesampingkan metode Trait, yang sebaliknya menimpa metode yang diwarisi.

Jadi - pertimbangkan skenario berikut:

class BaseClass {
    function SomeMethod() { /* Do stuff here */ }
}

interface IBase {
    function SomeMethod();
}

trait myTrait {
    function SomeMethod() { /* Do different stuff here */ }
}

class MyClass extends BaseClass implements IBase {
    use myTrait;

    function SomeMethod() { /* Do a third thing */ }
}

Saat membuat instance MyClass, di atas, terjadi hal berikut:

  1. The Interface IBasememerlukan fungsi parameterless disebutSomeMethod() yang akan diberikan.
  2. Kelas dasar BaseClass menyediakan implementasi metode ini - memenuhi kebutuhan.
  3. Ini trait myTraitmenyediakan fungsi tanpa parameter yang disebut SomeMethod()juga, yang didahulukan dari BaseClassversi-
  4. Ini class MyClassmenyediakan versi sendiri SomeMethod()- yang diutamakan daripada trait-versi.

Kesimpulan

  1. Sebuah Interfacetidak dapat memberikan implementasi standar dari tubuh metode, sementara sebuah traitkaleng.
  2. Sebuah Interfaceadalah polimorfik , mewarisi konstruk - sementara traittidak.
  3. Multiple Interfaces dapat digunakan di kelas yang sama, dan begitu juga multiple traits.

4
"Suatu sifat mirip dengan konsep C # dari kelas abstrak" Tidak, kelas abstrak adalah kelas abstrak; konsep itu ada di PHP dan C #. Saya akan membandingkan sifat dalam PHP dengan kelas statis yang dibuat dari metode ekstensi dalam C # sebagai gantinya, dengan pembatasan berbasis tipe dihapus sebagai sifat dapat digunakan oleh hampir semua jenis, tidak seperti metode ekstensi yang hanya meluas satu jenis.
BoltClock

1
Komentar yang sangat bagus - dan saya setuju dengan Anda. Dalam membaca ulang, itu analogi yang lebih baik. Saya percaya bahwa masih lebih baik untuk menganggapnya sebagai mixin- dan ketika saya telah meninjau kembali pembukaan jawaban saya, saya telah memperbarui untuk mencerminkan ini. Terima kasih telah berkomentar, @BoltClock!
Troy Alford

1
Saya tidak berpikir bahwa ada kaitannya dengan metode ekstensi c #. Metode ekstensi ditambahkan ke tipe kelas tunggal (menghormati hirarki kelas tentu saja) tujuan mereka adalah untuk meningkatkan tipe dengan fungsionalitas tambahan, bukan untuk "berbagi kode" lebih dari beberapa kelas dan membuat berantakan. Itu tidak sebanding! Jika sesuatu perlu digunakan kembali, biasanya itu berarti harus memiliki ruang sendiri, seperti kelas terpisah yang akan terkait dengan kelas yang membutuhkan fungsi umum. Implementasinya dapat bervariasi tergantung pada desain, tetapi kira-kira begitu. Ciri hanyalah cara lain untuk membuat kode yang buruk.
Sofija

Sebuah kelas dapat memiliki beberapa Antarmuka? Saya tidak yakin apakah grafik saya salah, tetapi kelas X mengimplementasikan Y, Z valid.
Yann Chabot

26

kupikir traits berguna untuk membuat kelas yang berisi metode yang dapat digunakan sebagai metode dari beberapa kelas yang berbeda.

Sebagai contoh:

trait ToolKit
{
    public $errors = array();

    public function error($msg)
    {
        $this->errors[] = $msg;
        return false;
    }
}

Anda dapat memiliki dan menggunakan metode "kesalahan" ini di kelas apa pun yang menggunakan sifat ini.

class Something
{
    use Toolkit;

    public function do_something($zipcode)
    {
        if (preg_match('/^[0-9]{5}$/', $zipcode) !== 1)
            return $this->error('Invalid zipcode.');

        // do something here
    }
}

Sementara dengan interfaces Anda hanya dapat mendeklarasikan metode tanda tangan, tetapi tidak kode fungsinya. Juga, untuk menggunakan antarmuka, Anda harus mengikuti hierarki, menggunakan implements. Ini tidak terjadi dengan sifat-sifat.

Ini benar-benar berbeda!


Saya pikir ini adalah contoh buruk dari suatu sifat. to_integerakan lebih mungkin dimasukkan dalam IntegerCastantarmuka karena tidak ada cara yang secara fundamental mirip dengan (secara cerdas) melemparkan kelas ke integer.
Matius

5
Lupakan "to_integer" - itu hanya ilustrasi. Sebuah contoh. A "Halo, Dunia". "Example.com".
J. Bruni

2
Apa manfaat yang dimiliki oleh alat bantu ini yang tidak dapat dimiliki oleh kelas utilitas mandiri? Alih-alih use ToolkitAnda bisa $this->toolkit = new Toolkit();atau saya kehilangan beberapa manfaat dari sifat itu sendiri?
Anthony

@Anthony di suatu tempat di Somethingwadah Anda Anda lakukanif(!$something->do_something('foo')) var_dump($something->errors);
TheRealChx101

20

Bagi pemula jawaban di atas mungkin sulit, Ini adalah cara termudah untuk memahaminya:

Sifat

trait SayWorld {
    public function sayHello() {
        echo 'World!';
    }
}

jadi jika Anda ingin memiliki sayHellofungsi di kelas lain tanpa membuat ulang seluruh fungsi Anda dapat menggunakan ciri-ciri,

class MyClass{
  use SayWorld;

}

$o = new MyClass();
$o->sayHello();

Benar keren!

Tidak hanya fungsi, Anda dapat menggunakan apa pun dalam sifat (fungsi, variabel, konst ..). Anda juga dapat menggunakan beberapa sifat:use SayWorld,AnotherTraits;

Antarmuka

  interface SayWorld {
     public function sayHello();
  }

  class MyClass implements SayWorld { 
     public function sayHello() {
        echo 'World!';
     }
}

jadi inilah perbedaan antarmuka dari ciri-ciri: Anda harus membuat kembali semua antarmuka di kelas yang diimplementasikan. antarmuka tidak memiliki implementasi. dan antarmuka hanya dapat memiliki fungsi dan konst, tidak dapat memiliki variabel.

Saya harap ini membantu!


5

Metafora yang sering digunakan untuk menggambarkan Traits adalah Traits adalah antarmuka dengan implementasi.

Ini adalah cara berpikir yang baik dalam kebanyakan situasi, tetapi ada beberapa perbedaan kecil di antara keduanya.

Sebagai permulaan, the instanceof operator tidak akan bekerja dengan sifat-sifat (yaitu, suatu sifat bukan objek nyata) sehingga Anda tidak dapat kami untuk melihat apakah suatu kelas memiliki sifat tertentu (atau untuk melihat apakah dua kelas yang tidak terkait berbagi suatu sifat ). Itulah yang mereka maksudkan dengan menjadi konstruksi untuk penggunaan kembali kode horizontal.

Ada yang fungsi sekarang dalam PHP yang akan membiarkan Anda mendapatkan daftar semua ciri-ciri seorang penggunaan kelas, tapi sifat-warisan berarti Anda harus melakukan pemeriksaan rekursif untuk andal memeriksa apakah kelas di beberapa titik memiliki sifat tertentu (ada contoh kode pada halaman doco PHP). Tapi ya, itu tentu tidak sesederhana dan sebersih instanceofnya, dan IMHO itu fitur yang akan membuat PHP lebih baik.

Juga, kelas abstrak masih kelas, sehingga mereka tidak memecahkan masalah terkait penggunaan ulang beberapa warisan. Ingat Anda hanya dapat memperluas satu kelas (nyata atau abstrak) tetapi mengimplementasikan beberapa antarmuka.

Saya telah menemukan ciri-ciri dan antarmuka yang benar-benar baik untuk digunakan bersama untuk membuat pseudo multiple inheritance. Misalnya:

class SlidingDoor extends Door implements IKeyed  
{  
    use KeyedTrait;  
    [...] // Generally not a lot else goes here since it's all in the trait  
}

Melakukan ini berarti Anda dapat menggunakan instanceof untuk menentukan apakah objek Pintu tertentu diketik atau tidak, Anda tahu Anda akan mendapatkan seperangkat metode yang konsisten dll, dan semua kode berada di satu tempat di semua kelas yang menggunakan KeyedTrait.


Bagian terakhir dari jawaban itu tentu saja adalah apa yang dikatakan @rdlowrey lebih terinci dalam tiga paragraf terakhir di bawah "Ciri-ciri" dalam posnya; Saya hanya merasa potongan kode kerangka yang sangat sederhana akan membantu mengilustrasikannya.
Jon Kloske

Saya pikir cara OO terbaik untuk menggunakan sifat adalah menggunakan antarmuka di mana Anda bisa. Dan jika ada kasus ketika beberapa subclass menerapkan jenis kode yang sama untuk antarmuka itu dan Anda tidak dapat memindahkan kode itu ke superclass (abstrak) mereka -> menerapkannya dengan ciri
pemain-satu

4

Ciri - ciri hanya untuk penggunaan kembali kode .

Antarmuka hanya menyediakan tanda tangan dari fungsi yang harus didefinisikan di kelas di mana ia dapat digunakan tergantung pada kebijaksanaan pemrogram . Dengan demikian memberi kami prototipe untuk sekelompok kelas .

Untuk referensi- http://www.php.net/manual/en/language.oop5.traits.php


3

Anda dapat menganggap suatu sifat sebagai "salin-tempel" kode secara otomatis.

Menggunakan sifat-sifat berbahaya karena tidak ada artinya untuk mengetahui apa yang dilakukannya sebelum eksekusi.

Namun, sifat-sifat lebih fleksibel karena kurangnya keterbatasan seperti pewarisan.

Ciri dapat berguna untuk menyuntikkan metode yang memeriksa sesuatu ke dalam kelas, misalnya, keberadaan metode atau atribut lain. Artikel bagus tentang itu (tapi dalam bahasa Prancis, maaf) .

Untuk orang-orang yang membaca bahasa Prancis yang bisa mendapatkannya, GNU / Linux Magazine HS 54 memiliki artikel tentang hal ini.


Masih tidak mengerti bagaimana ciri-ciri berbeda dari antarmuka dengan implementasi default
denis631

@ denis631 Anda dapat melihat Ciri sebagai potongan kode, dan antarmuka sebagai kontrak tanda tangan. Jika itu bisa membantu, Anda bisa melihatnya sebagai bit informal dari kelas yang bisa berisi apa saja. Beri tahu saya jika itu membantu.
Benj

Saya melihat bahwa ciri-ciri PHP dapat dilihat sebagai makro yang kemudian diperluas pada waktu kompilasi / aliasing potongan kode itu dengan kunci itu. Akan tetapi, sifat-sifat karat tampak berbeda (atau saya salah). Tetapi karena mereka berdua memiliki sifat kata di dalamnya saya akan berasumsi bahwa mereka sama, artinya konsep yang sama. Tautan ciri karat: doc.rust-lang.org/rust-by-example/trait.html
denis631

2

Jika Anda tahu bahasa Inggris dan tahu apa traitartinya, itu persis seperti namanya. Ini adalah paket metode dan properti tanpa kelas yang Anda lampirkan ke kelas yang ada dengan mengetikuse .

Pada dasarnya, Anda bisa membandingkannya dengan satu variabel. Fungsi penutupan dapat usevariabel-variabel ini dari luar lingkup dan dengan cara itu mereka memiliki nilai di dalamnya. Mereka kuat dan dapat digunakan dalam segala hal. Hal yang sama terjadi pada sifat-sifat jika mereka digunakan.


2

Jawaban lain berhasil menjelaskan perbedaan antara antarmuka dan sifat. Saya akan fokus pada contoh dunia nyata yang berguna, khususnya yang menunjukkan bahwa sifat dapat menggunakan variabel instan - memungkinkan Anda menambahkan perilaku ke kelas dengan kode boilerplate minimal.

Sekali lagi, seperti yang disebutkan oleh orang lain, sifat-sifat berpasangan dengan baik dengan antarmuka, memungkinkan antarmuka untuk menentukan kontrak perilaku, dan sifat untuk memenuhi implementasi.

Menambahkan kemampuan mempublikasikan / berlangganan acara ke kelas dapat menjadi skenario umum di beberapa basis kode. Ada 3 solusi umum:

  1. Tentukan kelas dasar dengan pub peristiwa / subkode, dan kemudian kelas yang ingin menawarkan acara dapat memperluasnya untuk mendapatkan kemampuan.
  2. Tentukan kelas dengan pub peristiwa / subkode, dan kemudian kelas lain yang ingin menawarkan acara dapat menggunakannya melalui komposisi, menentukan metode mereka sendiri untuk membungkus objek yang dikomposisikan, proksi panggilan metode untuk itu.
  3. Tentukan sifat dengan event pub / sub kode, dan kemudian kelas lain yang ingin menawarkan acara dapat usesifat tersebut, alias impor, untuk mendapatkan kemampuan.

Seberapa baik masing-masing bekerja?

# 1 Tidak bekerja dengan baik. Itu akan, sampai hari Anda menyadari Anda tidak dapat memperpanjang kelas dasar karena Anda sudah memperpanjang sesuatu yang lain. Saya tidak akan menunjukkan contoh ini karena harus jelas betapa terbatasnya untuk menggunakan warisan seperti ini.

# 2 & # 3 keduanya bekerja dengan baik. Saya akan menunjukkan contoh yang menyoroti beberapa perbedaan.

Pertama, beberapa kode yang akan sama antara kedua contoh:

Antarmuka

interface Observable {
    function addEventListener($eventName, callable $listener);
    function removeEventListener($eventName, callable $listener);
    function removeAllEventListeners($eventName);
}

Dan beberapa kode untuk menunjukkan penggunaan:

$auction = new Auction();

// Add a listener, so we know when we get a bid.
$auction->addEventListener('bid', function($bidderName, $bidAmount){
    echo "Got a bid of $bidAmount from $bidderName\n";
});

// Mock some bids.
foreach (['Moe', 'Curly', 'Larry'] as $name) {
    $auction->addBid($name, rand());
}

Ok, sekarang mari kita tunjukkan bagaimana implementasi dari Auction kelas akan berbeda ketika menggunakan ciri-ciri.

Pertama, beginilah penampilan # 2 (menggunakan komposisi):

class EventEmitter {
    private $eventListenersByName = [];

    function addEventListener($eventName, callable $listener) {
        $this->eventListenersByName[$eventName][] = $listener;
    }

    function removeEventListener($eventName, callable $listener) {
        $this->eventListenersByName[$eventName] = array_filter($this->eventListenersByName[$eventName], function($existingListener) use ($listener) {
            return $existingListener === $listener;
        });
    }

    function removeAllEventListeners($eventName) {
        $this->eventListenersByName[$eventName] = [];
    }

    function triggerEvent($eventName, array $eventArgs) {
        foreach ($this->eventListenersByName[$eventName] as $listener) {
            call_user_func_array($listener, $eventArgs);
        }
    }
}

class Auction implements Observable {
    private $eventEmitter;

    public function __construct() {
        $this->eventEmitter = new EventEmitter();
    }

    function addBid($bidderName, $bidAmount) {
        $this->eventEmitter->triggerEvent('bid', [$bidderName, $bidAmount]);
    }

    function addEventListener($eventName, callable $listener) {
        $this->eventEmitter->addEventListener($eventName, $listener);
    }

    function removeEventListener($eventName, callable $listener) {
        $this->eventEmitter->removeEventListener($eventName, $listener);
    }

    function removeAllEventListeners($eventName) {
        $this->eventEmitter->removeAllEventListeners($eventName);
    }
}

Beginilah penampilan # 3 (ciri-ciri):

trait EventEmitterTrait {
    private $eventListenersByName = [];

    function addEventListener($eventName, callable $listener) {
        $this->eventListenersByName[$eventName][] = $listener;
    }

    function removeEventListener($eventName, callable $listener) {
        $this->eventListenersByName[$eventName] = array_filter($this->eventListenersByName[$eventName], function($existingListener) use ($listener) {
            return $existingListener === $listener;
        });
    }

    function removeAllEventListeners($eventName) {
        $this->eventListenersByName[$eventName] = [];
    }

    protected function triggerEvent($eventName, array $eventArgs) {
        foreach ($this->eventListenersByName[$eventName] as $listener) {
            call_user_func_array($listener, $eventArgs);
        }
    }
}

class Auction implements Observable {
    use EventEmitterTrait;

    function addBid($bidderName, $bidAmount) {
        $this->triggerEvent('bid', [$bidderName, $bidAmount]);
    }
}

Perhatikan bahwa kode di dalam EventEmitterTraitpersis sama dengan apa yang ada di dalam EventEmitterkelas kecuali sifat menyatakan triggerEvent()metode yang dilindungi. Jadi, satu-satunya perbedaan yang perlu Anda perhatikan adalah implementasi Auctionkelas .

Dan perbedaannya besar. Saat menggunakan komposisi, kami mendapatkan solusi hebat, memungkinkan kami untuk menggunakan kembali EventEmitterkelas kami sebanyak yang kami suka. Namun, kelemahan utama adalah kita memiliki banyak kode boilerplate yang perlu kita tulis dan pelihara karena untuk setiap metode yang didefinisikan dalam Observableantarmuka, kita perlu mengimplementasikannya dan menulis kode boilerplate yang membosankan yang hanya meneruskan argumen ke metode yang sesuai di kami menyusun EventEmitterobjek. Menggunakan sifat dalam contoh ini memungkinkan kita menghindari itu , membantu kita mengurangi kode boilerplate dan meningkatkan rawatan .

Namun, mungkin ada saat-saat di mana Anda tidak ingin Auctionkelas Anda mengimplementasikan Observableantarmuka penuh - mungkin Anda hanya ingin mengekspos 1 atau 2 metode, atau bahkan mungkin tidak ada sama sekali sehingga Anda dapat menentukan tanda tangan metode Anda sendiri. Dalam kasus seperti itu, Anda mungkin masih lebih suka metode komposisi.

Tapi, sifat ini sangat menarik di sebagian besar skenario, terutama jika antarmuka memiliki banyak metode, yang menyebabkan Anda menulis banyak boilerplate.

* Anda sebenarnya bisa melakukan keduanya - tentukan EventEmitterkelas jika Anda ingin menggunakannya secara komposisional, dan tentukan EventEmitterTraitsifatnya juga, menggunakan EventEmitterimplementasi kelas di dalam sifat tersebut :)


1

Sifatnya sama dengan kelas yang dapat kita gunakan untuk beberapa tujuan pewarisan dan juga penggunaan kembali kode.

Kita dapat menggunakan sifat di dalam kelas dan juga kita dapat menggunakan beberapa sifat di kelas yang sama dengan 'menggunakan kata kunci'.

Antarmuka menggunakan untuk penggunaan kembali kode sama dengan suatu sifat

antarmuka memperluas beberapa antarmuka sehingga kita dapat memecahkan beberapa masalah warisan tetapi ketika kita mengimplementasikan antarmuka maka kita harus membuat semua metode di dalam kelas. Untuk info lebih lanjut klik tautan di bawah ini:

http://php.net/manual/en/language.oop5.traits.php http://php.net/manual/en/language.oop5.interfaces.php



0

Perbedaan utama adalah bahwa, dengan antarmuka, Anda harus menentukan implementasi aktual dari setiap metode dalam setiap kelas yang mengimplementasikan antarmuka tersebut, sehingga Anda dapat memiliki banyak kelas yang mengimplementasikan antarmuka yang sama tetapi dengan perilaku yang berbeda, sementara sifat-sifat hanyalah potongan-potongan kode yang disuntikkan ke dalam kelas; Perbedaan penting lainnya adalah bahwa metode sifat hanya dapat berupa metode kelas atau metode statis, tidak seperti metode antarmuka yang juga dapat (dan biasanya) menjadi metode instan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.