Beberapa Warisan di PHP

97

Saya sedang mencari cara yang baik dan bersih untuk menghindari fakta bahwa PHP5 masih belum mendukung multiple inheritance. Berikut hierarki kelasnya:

Message
- TextMessage
-------- InvitationTextMessage
- EmailMessage
-------- InvitationEmailMessage

Kedua jenis kelas Undangan * memiliki banyak kesamaan; Saya ingin memiliki kelas orang tua yang sama, Undangan, yang akan mereka warisi dari keduanya. Sayangnya, mereka juga memiliki banyak kesamaan dengan leluhur mereka saat ini ... TextMessage dan EmailMessage. Keinginan klasik untuk mendapatkan banyak warisan di sini.

Apa pendekatan yang paling ringan untuk menyelesaikan masalah ini?

Terima kasih!

Alex Weinstein
sumber
4
Tidak banyak kasus di mana warisan (atau bahkan lebih dari satu warisan) dapat dibenarkan. Lihatlah prinsip SOLID. Lebih suka komposisi daripada warisan.
Ondřej Mirtes
2
@ OndřejMirtes apa maksud Anda - "tidak banyak kasus di mana warisan dapat dibenarkan."?
styler1972
12
Maksud saya - warisan membawa lebih banyak masalah daripada manfaat (lihat prinsip substitusi Liskov). Anda dapat menyelesaikan hampir semua hal dengan komposisi dan menghemat banyak sakit kepala. Warisan juga bersifat statis - itu berarti Anda tidak dapat mengubah apa yang sudah tertulis di kode. Tetapi komposisi dapat digunakan pada waktu proses dan Anda dapat memilih implementasi secara dinamis - misalnya, menggunakan kembali kelas yang sama dengan mekanisme cache yang berbeda.
Ondřej Mirtes
5
PHP 5.4 memiliki "ciri-ciri": stackoverflow.com/a/13966131/492130
f.ardelian
1
Saya akan menyarankan pemula untuk tidak pernah menggunakan warisan . Secara umum, hanya dua situasi di mana pewarisan diperbolehkan adalah: 1) saat membangun perpustakaan, sehingga pengguna menulis lebih sedikit kode dan 2) ketika pemimpin proyek meminta Anda menggunakannya
gurghet

Jawaban:

141

Alex, sebagian besar waktu Anda membutuhkan multiple inheritance adalah sinyal bahwa struktur objek Anda agak salah. Dalam situasi yang Anda uraikan, saya melihat Anda memiliki tanggung jawab kelas yang terlalu luas. Jika Message adalah bagian dari model bisnis aplikasi, Message tidak akan menangani rendering output. Sebagai gantinya, Anda dapat membagi tanggung jawab dan menggunakan MessageDispatcher yang mengirimkan Message yang diteruskan menggunakan backend teks atau html. Saya tidak tahu kode Anda, tetapi biarkan saya mensimulasikannya dengan cara ini:

$m = new Message();
$m->type = 'text/html';
$m->from = 'John Doe <[email protected]>';
$m->to = 'Random Hacker <[email protected]>';
$m->subject = 'Invitation email';
$m->importBody('invitation.html');

$d = new MessageDispatcher();
$d->dispatch($m);

Dengan cara ini Anda dapat menambahkan beberapa spesialisasi ke kelas Pesan:

$htmlIM = new InvitationHTMLMessage(); // html type, subject and body configuration in constructor
$textIM = new InvitationTextMessage(); // text type, subject and body configuration in constructor

$d = new MessageDispatcher();
$d->dispatch($htmlIM);
$d->dispatch($textIM);

Perhatikan bahwa MessageDispatcher akan membuat keputusan apakah akan mengirim sebagai HTML atau teks biasa bergantung pada typeproperti di objek Message yang diteruskan.

// in MessageDispatcher class
public function dispatch(Message $m) {
    if ($m->type == 'text/plain') {
        $this->sendAsText($m);
    } elseif ($m->type == 'text/html') {
        $this->sendAsHTML($m);
    } else {
        throw new Exception("MIME type {$m->type} not supported");
    }
}

Singkatnya, tanggung jawab dibagi menjadi dua kelas. Konfigurasi pesan dilakukan di kelas InvitationHTMLMessage / InvitationTextMessage, dan algoritme pengiriman didelegasikan ke dispatcher. Ini disebut Pola Strategi, Anda dapat membaca lebih lanjut di sini .

Michał Rudnicki
sumber
13
Jawaban yang luar biasa luasnya, terima kasih! Saya belajar sesuatu hari ini!
Alex Weinstein
26
... Saya tahu ini agak lama (saya sedang mencari untuk melihat apakah PHP memiliki MI ... hanya untuk rasa ingin tahu) Saya rasa ini bukan contoh yang baik dari Pola Strategi. Pola Strategi dirancang agar Anda dapat menerapkan "strategi" baru kapan saja. Implementasi yang Anda berikan tidak menampilkan kemampuan seperti itu. Sebaliknya, Message harus memiliki fungsi "send" yang memanggil MessageDispatcher-> dispatch () (Dispatcher baik param atau anggota var), dan kelas baru HTMLDispatcher & TextDispatcher akan mengimplementasikan "dispatch" dengan caranya masing-masing (ini memungkinkan Dispatcher lain untuk melakukannya pekerjaan lain)
Terence Honles
12
Sayangnya PHP tidak bagus untuk menerapkan pola Strategi. Bahasa yang mendukung metode overloading bekerja lebih baik di sini - bayangkan Anda memiliki dua metode dengan nama yang sama: dispatch (HTMLMessage $ m) dan dispatch (TextMessage $) - sekarang dalam bahasa yang diketik dengan kuat, compiler / interpreter akan secara otomatis menggunakan "strategi" yang tepat berdasarkan jenis parameter. Selain itu, menurut saya terbuka untuk implementasi strategi baru adalah inti dari Pola Strategi. Tentu itu hal yang menyenangkan untuk dimiliki, tetapi seringkali bukan persyaratan.
Michał Rudnicki
2
Misalkan Anda memiliki kelas Tracing(ini hanya contoh) di mana Anda ingin memiliki hal-hal umum seperti debug ke dalam file, mengirim SMS untuk masalah kritis, dan sebagainya. Semua kelas Anda adalah anak-anak dari kelas ini. Sekarang misalkan Anda ingin membuat kelas Exceptionyang harus memiliki fungsi tersebut (= anak dari Tracing). Kelas ini harus anak dari Exception. Bagaimana Anda mendesain barang seperti itu tanpa banyak warisan? Ya, Anda mungkin selalu memiliki solusi, tetapi Anda akan selalu mendekati peretasan. Dan peretasan = solusi mahal dalam jangka panjang. Akhir dari cerita.
Olivier Pons
1
Olivier Pons, menurut saya Pelacakan subclass tidak akan menjadi solusi yang tepat untuk kasus penggunaan Anda. Sesuatu yang sederhana seperti memiliki kelas Tracing abstrak dengan metode statis Debug, SendSMS, dll., Yang kemudian dapat dipanggil dari dalam kelas lain dengan Tracing :: SendSMS () dll. Kelas Anda yang lain bukanlah 'tipe' dari Tracing, mereka 'menggunakan' Tracing. Catatan: beberapa orang mungkin lebih memilih metode tunggal daripada metode statis; Saya lebih suka menggunakan metode statis daripada lajang jika memungkinkan.
15

Mungkin Anda bisa mengganti relasi 'is-a' dengan relasi 'has-a'? Undangan mungkin memiliki Pesan, tetapi tidak harus berupa pesan 'is-a'. Fitur Undangan mungkin dikonfirmasi, yang tidak cocok dengan model Pesan.

Telusuri 'komposisi vs. warisan' jika Anda ingin tahu lebih banyak tentang itu.

Matthias Kestenholz
sumber
9

Jika saya bisa mengutip Phil di utas ini ...

PHP, seperti Java, tidak mendukung multiple inheritance.

Datang dalam PHP 5.4 akan menjadi ciri khas yang mencoba memberikan solusi untuk masalah ini.

Sementara itu, sebaiknya pikirkan ulang desain kelas Anda. Anda dapat mengimplementasikan beberapa antarmuka jika Anda menginginkan API yang diperluas ke kelas Anda.

Dan Chris ....

PHP tidak benar-benar mendukung multiple inheritance, tetapi ada beberapa cara (agak berantakan) untuk mengimplementasikannya. Lihat URL ini untuk beberapa contoh:

http://www.jasny.net/articles/how-i-php-multiple-inheritance/

Pikir mereka berdua memiliki tautan yang berguna. Tidak sabar untuk mencoba ciri-ciri atau mungkin beberapa campuran ...

Simon Timur
sumber
1
Sifat adalah cara untuk pergi
Jonathan
6

Kerangka kerja Symfony memiliki plugin mixin untuk ini , Anda mungkin ingin memeriksanya - bahkan hanya untuk ide, jika tidak untuk menggunakannya.

Jawaban "pola desain" adalah mengabstraksi fungsionalitas bersama menjadi komponen terpisah, dan menulis pada waktu proses. Pikirkan tentang cara untuk mengabstraksi fungsionalitas Undangan sebagai kelas yang terkait dengan kelas Message Anda dengan cara lain selain warisan.

joelhardi
sumber
4

Saya menggunakan ciri-ciri dalam PHP 5.4 sebagai cara untuk menyelesaikannya. http://php.net/manual/en/language.oop5.traits.php

Hal ini memungkinkan pewarisan klasik dengan extends, tetapi juga memberikan kemungkinan untuk menempatkan fungsionalitas dan properti umum ke dalam 'ciri'. Seperti yang dikatakan manual:

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

MatthewPearson
sumber
3

Sepertinya pola dekorator mungkin cocok, tetapi sulit untuk membedakannya tanpa detail lebih lanjut.

danio
sumber
Jawaban terbaik IMO.
100rb
3

Ini adalah pertanyaan sekaligus solusi ....

Bagaimana dengan panggilan magis (),_get (), __set () metode? Saya belum menguji solusi ini, tetapi bagaimana jika Anda membuat kelas multiInherit. Variabel yang dilindungi dalam kelas anak bisa berisi larik kelas yang akan diturunkan. Konstruktor di kelas multi-antarmuka dapat membuat instance dari setiap kelas yang diwariskan dan menautkannya ke properti pribadi, misalnya _ext. Metode __call () bisa menggunakan fungsi method_exists () pada setiap kelas dalam larik _ext untuk menemukan metode yang benar untuk dipanggil. __get () dan __set dapat digunakan untuk menemukan properti internal, atau jika ahli Anda dengan referensi, Anda dapat membuat properti kelas anak dan kelas yang diwariskan menjadi referensi ke data yang sama. Beberapa pewarisan objek Anda akan transparan untuk kode menggunakan objek tersebut. Juga, objek internal bisa mengakses objek yang diwarisi secara langsung jika diperlukan selama array _ext diindeks dengan nama kelas. Saya telah membayangkan membuat kelas super ini dan belum menerapkannya karena saya merasa jika berhasil daripada itu dapat menyebabkan mengembangkan beberapa kebiasaan pemrograman yang buruk.

Ralph Ritoch
sumber
Saya pikir ini layak. Ini akan menggabungkan fungsionalitas dari beberapa kelas, tetapi tidak akan benar-benar mewarisinya (dalam arti instanceof)
user102008
Dan ini pasti akan gagal untuk mengizinkan penggantian segera setelah di kelas dalam melakukan panggilan ke diri sendiri :: <whatever>
Phil Lello
1

Saya punya beberapa pertanyaan untuk ditanyakan untuk mengklarifikasi apa yang Anda lakukan:

1) Apakah objek pesan Anda adil berisi pesan misalnya badan, penerima, waktu jadwal? 2) Apa yang ingin Anda lakukan dengan objek Undangan Anda? Apakah itu perlu diperlakukan secara khusus dibandingkan dengan EmailMessage? 3) Jika demikian APA yang istimewa tentang itu? 4) Jika demikian, mengapa jenis pesan perlu ditangani secara berbeda untuk undangan? 5) Bagaimana jika Anda ingin mengirim pesan selamat datang atau pesan OK? Apakah itu benda baru juga?

Kedengarannya seperti Anda mencoba menggabungkan terlalu banyak fungsionalitas ke dalam satu set objek yang seharusnya hanya berkaitan dengan penyimpanan konten pesan - dan bukan bagaimana seharusnya menanganinya. Bagi saya, Anda tahu, tidak ada perbedaan antara undangan atau pesan standar. Jika undangan membutuhkan penanganan khusus, itu berarti logika aplikasi dan bukan tipe pesan.

Misalnya: sistem yang saya buat memiliki objek pesan dasar bersama yang diperluas menjadi SMS, Email, dan jenis pesan lainnya. Namun: ini tidak diperpanjang lebih lanjut - pesan undangan hanyalah teks yang telah ditentukan sebelumnya untuk dikirim melalui pesan jenis Email. Aplikasi Undangan tertentu akan berkaitan dengan validasi dan persyaratan lain untuk undangan. Lagi pula, yang ingin Anda lakukan adalah mengirim pesan X ke penerima Y yang seharusnya menjadi sistem tersendiri.


sumber
0

Masalah yang sama seperti Java. Coba gunakan antarmuka dengan fungsi abstrak untuk memecahkan masalah itu

DeeCee
sumber
0

PHP mendukung antarmuka. Ini bisa menjadi taruhan yang bagus, tergantung pada kasus penggunaan Anda.

Cheekysoft
sumber
5
Antarmuka tidak memungkinkan implementasi fungsi konkret, jadi tidak membantu di sini.
Alex Weinstein
1
Antarmuka mendukung Multiple Inheritance, tidak seperti kelas.
Craig Lewis
-1

Bagaimana dengan kelas Undangan tepat di bawah kelas Pesan?

jadi hirarki berjalan:

Pesan
--- Undangan
------ TextMessage
------ EmailMessage

Dan di kelas Invitation, tambahkan fungsionalitas yang ada di InvitationTextMessage dan InvitationEmailMessage.

Saya tahu bahwa Undangan sebenarnya bukan jenis Pesan, ini lebih merupakan fungsionalitas Pesan. Jadi saya tidak yakin apakah ini desain OO yang bagus atau tidak.

inti
sumber