Bisakah kita hidup tanpa konstruktor?

25

Katakanlah dalam beberapa alasan semua objek dibuat dengan cara ini $ obj = CLASS :: getInstance (). Kemudian kami menyuntikkan dependensi menggunakan setter dan melakukan inisialisasi mulai menggunakan $ obj-> initInstance (); Apakah ada masalah atau situasi nyata, yang tidak dapat diselesaikan, jika kita tidak menggunakan konstruktor sama sekali?

Ps alasan untuk membuat objek dengan cara ini, adalah bahwa kita dapat mengganti kelas di dalam getInstance () sesuai dengan beberapa aturan.

Saya bekerja di PHP, jika itu penting

Axel Foley
sumber
bahasa pemrograman apa?
nyamuk
2
PHP (mengapa itu penting?)
Axel Foley
8
Sepertinya apa yang ingin Anda lakukan dapat dicapai dengan menggunakan pola Pabrik.
superM
1
Sama seperti catatan: patern @superM pabrik yang disebutkan adalah salah satu cara untuk mengimplementasikan Inversion of Control (Dependency Injection) adalah cara lain.
Joachim Sauer
Bukankah itu kira-kira apa yang dilakukan javascript dengan objek?
Thijser

Jawaban:

44

Saya akan mengatakan bahwa ini sangat menghambat ruang desain Anda.

Konstruktor adalah tempat yang bagus untuk menginisialisasi dan memvalidasi parameter yang dilewatkan. Jika Anda tidak bisa lagi menggunakannya untuk itu, maka inisialisasi, penanganan keadaan (atau jelas-jelas menyangkal konstruktor benda "rusak") menjadi jauh lebih sulit dan sebagian tidak mungkin.

Sebagai contoh, jika setiap Fooobjek membutuhkan Frobnicatormaka mungkin memeriksa konstruktornya jika Frobnicatortidak nol. Jika Anda mengambil konstruktor, maka semakin sulit untuk memeriksa. Apakah Anda memeriksa setiap titik di mana itu akan digunakan? Dalam suatu init()metode (secara efektif mengeksternalisasi metode konstruktor)? Jangan pernah memeriksanya dan berharap yang terbaik?

Meskipun Anda mungkin masih bisa mengimplementasikan semuanya (setelah semua, Anda masih belum selesai), beberapa hal akan jauh lebih sulit untuk dilakukan.

Secara pribadi saya akan menyarankan melihat Injeksi Ketergantungan / Inversi Kontrol . Teknik-teknik ini juga memungkinkan beralih kelas implementasi konkret, tetapi mereka tidak mencegah penulisan / penggunaan konstruktor.

Joachim Sauer
sumber
Apa yang Anda maksud dengan "atau menyangkal konstruktor objek" rusak ""?
Geek
2
@ Geek: seorang konstruktor dapat memeriksa argumennya dan memutuskan apakah itu akan menghasilkan objek yang berfungsi (misalnya jika objek Anda membutuhkan HttpClientlalu memeriksa apakah parameter itu bukan nol). Dan jika kendala itu tidak terpenuhi, maka bisa melempar pengecualian. Itu tidak benar-benar mungkin dengan pendekatan konstruk dan set nilai.
Joachim Sauer
1
Saya pikir OP hanya menggambarkan eksternalisasi konstruktor di dalam init(), yang sepenuhnya mungkin — meskipun ini hanya membebani lebih banyak beban pemeliharaan.
Peter
26

2 keuntungan untuk konstruktor:

Konstruktor memungkinkan langkah konstruksi objek dilakukan secara atom.

Saya bisa menghindari konstruktor dan menggunakan setter untuk semuanya, tetapi bagaimana dengan properti wajib seperti yang disarankan Joachim Sauer? Dengan konstruktor, suatu objek memiliki logika konstruksinya sendiri untuk memastikan bahwa tidak ada instance kelas yang tidak valid .

Jika membuat instance dari Foo3 properti harus ditetapkan, konstruktor dapat mengambil referensi ke semua 3, memvalidasi mereka dan melemparkan pengecualian jika mereka tidak valid.

Enkapsulasi

Dengan hanya mengandalkan setter, beban ada pada konsumen objek untuk membangunnya dengan benar. Mungkin ada kombinasi properti yang berbeda yang valid.

Misalnya, setiap Fooinstance membutuhkan instance Barsebagai properti baratau instance BarFindersebagai properti barFinder. Itu bisa menggunakan salah satunya. Anda dapat membuat konstruktor untuk setiap set parameter yang valid dan menerapkan konvensi seperti itu.

Logika dan semantik untuk objek hidup di dalam objek itu sendiri. Enkapsulasi yang baik.

Brandon
sumber
15

Ya, Anda bisa hidup tanpa konstruktor.

Tentu, Anda mungkin berakhir dengan banyak kode pelat ketel yang diduplikasi. Dan jika aplikasi Anda berskala apa pun, kemungkinan Anda akan menghabiskan banyak waktu untuk mencari sumber masalah ketika kode boilerplate tidak digunakan secara konsisten di seluruh aplikasi.

Tapi tidak, Anda tidak benar-benar 'membutuhkan' konstruktor Anda sendiri. Tentu saja, Anda juga tidak benar-benar 'membutuhkan' kelas dan objek.

Sekarang jika tujuan Anda adalah menggunakan semacam pola pabrik untuk pembuatan objek, itu tidak eksklusif untuk menggunakan konstruktor saat menginisialisasi objek Anda.

GrandmasterB
sumber
Benar. Seseorang bahkan dapat hidup tanpa kelas dan objek, untuk memulainya.
JensG
5
Semua memuji assembler yang perkasa!
Davor Ždralo
9

Keuntungan menggunakan konstruktor adalah mereka membuatnya lebih mudah untuk memastikan bahwa Anda tidak akan pernah memiliki objek yang tidak valid.

Konstruktor memberi Anda kesempatan untuk mengatur semua variabel anggota objek ke status yang valid. Ketika Anda kemudian memastikan bahwa tidak ada metode mutator dapat mengubah objek ke keadaan tidak valid, Anda tidak akan pernah memiliki objek yang tidak valid, yang akan menyelamatkan Anda dari banyak bug.

Tetapi ketika objek baru dibuat dalam keadaan tidak valid dan Anda harus memanggil beberapa setter untuk memasukkannya ke dalam status yang valid di mana ia dapat digunakan, Anda berisiko bahwa seorang konsumen kelas lupa untuk memanggil setter ini atau menyebutnya dengan salah dan Anda berakhir dengan objek yang tidak valid.

Solusinya bisa dengan membuat objek hanya melalui metode pabrik yang memeriksa setiap objek yang dibuatnya untuk validitas sebelum mengembalikannya ke pemanggil.

Philipp
sumber
3

$ obj = CLASS :: getInstance (). Kemudian kami menyuntikkan dependensi menggunakan setter dan melakukan inisialisasi mulai menggunakan $ obj-> initInstance ();

Saya pikir Anda membuat ini lebih sulit daripada yang seharusnya. Kami dapat menyuntikkan dependensi dengan baik melalui konstruktor - dan jika Anda memiliki banyak dependensi, cukup gunakan struktur seperti kamus sehingga Anda dapat menentukan yang mana yang ingin Anda gunakan:

$obj = new CLASS(array(
    'Frobnicator' => (),
    'Foonicator' => (),
));

Dan di dalam konstruktor, Anda dapat memastikan konsistensi seperti:

if (!array_key_exists('Frobnicator', $args)) {
    throw new Exception('Frobnicator required');
}
if (!array_key_exists('Foonicator', $args)) {
    $args['Foonicator'] = new DefaultFoonicator();
}

$args kemudian dapat digunakan untuk mengatur anggota pribadi sesuai kebutuhan.

Ketika dilakukan sepenuhnya dalam konstruktor seperti itu, tidak akan pernah ada keadaan peralihan di mana $objada tetapi tidak diinisialisasi, karena akan ada dalam sistem yang dijelaskan dalam pertanyaan. Lebih baik menghindari keadaan perantara seperti itu, karena Anda tidak dapat menjamin objek selalu akan digunakan dengan benar.

Izkata
sumber
2

Saya sebenarnya memikirkan hal-hal serupa.

Pertanyaan yang saya ajukan adalah "Apa yang dilakukan konstruktor, dan apakah mungkin melakukannya secara berbeda?" Saya mencapai kesimpulan itu:

  • Ini memastikan beberapa properti diinisialisasi. Dengan menerimanya sebagai parameter dan mengaturnya. Tapi ini bisa dengan mudah ditegakkan oleh kompiler. Dengan hanya memberi anotasi pada bidang atau properti sebagai "wajib", kompiler akan memeriksa selama pembuatan instance jika semuanya diatur dengan benar. Panggilan untuk membuat instance mungkin akan sama, tidak akan ada metode konstruktor.

  • Memastikan properti valid. Ini bisa dengan mudah dicapai dengan syarat tegas. Sekali lagi Anda hanya akan membubuhi keterangan properti dengan kondisi yang benar. Beberapa bahasa sudah melakukan ini.

  • Beberapa logika konstruksi yang lebih kompleks. Pola modern tidak merekomendasikan untuk melakukan itu dalam konstruktor, tetapi usulkan menggunakan metode atau kelas pabrik khusus. Jadi penggunaan konstruktor dalam hal ini minimal.

Jadi untuk menjawab pertanyaan Anda: Ya, saya percaya itu mungkin. Tetapi itu akan membutuhkan beberapa perubahan besar dalam desain bahasa.

Dan saya hanya memperhatikan jawaban saya cukup PL.

Euforia
sumber
2

Ya, Anda dapat melakukan hampir semua hal tanpa menggunakan konstruktor tetapi ini jelas menyia-nyiakan manfaat dari bahasa Pemrograman Berorientasi Objek.

Dalam bahasa modern (saya akan berbicara di sini tentang C # program mana yang saya di) dapat Anda membatasi bagian kode yang dapat dijalankan hanya dalam konstruktor. Berkat itu Anda dapat menghindari kesalahan yang canggung. Salah satunya adalah pengubah baca saja :

public class A {
    readonly string rostring;

    public A(string arg) {
        rostring = arg;
    }

    public static A CreateInstance(string arg) {
        var result = new A();
        A.rostring = arg;  // < because of this the code won't compile!
        return result;
    }
}

Seperti yang direkomendasikan oleh Joachim Sauer sebelumnya, alih-alih menggunakan Factoryderai desain, bacalah tentang Dependency Injection. Saya akan merekomendasikan membaca Dependency Injection dalam .NET oleh Mark Seemann .

SOReader
sumber
1

Instantiate objek dengan tipe tergantung pada persyaratan itu sangat mungkin. Itu bisa menjadi objek itu sendiri, menggunakan variabel global sistem untuk mengembalikan tipe tertentu.

Namun, memiliki kelas dalam kode yang dapat "Semua" adalah konsep tipe dinamis . Saya pribadi percaya bahwa pendekatan ini menciptakan ketidakkonsistenan dalam kode Anda, membuat kompleks pengujian *, dan "masa depan menjadi tidak pasti" sehubungan dengan aliran karya yang diusulkan.

* Saya merujuk pada fakta bahwa tes harus mempertimbangkan terlebih dahulu jenisnya, kedua hasil yang ingin Anda capai. Kemudian Anda membuat tes bersarang besar.

marcocs
sumber
1

Untuk menyeimbangkan beberapa jawaban lain, dengan mengklaim:

Konstruktor memberi Anda kesempatan untuk mengatur semua variabel anggota objek ke status yang valid ... Anda tidak akan pernah memiliki objek yang tidak valid, yang akan menyelamatkan Anda dari banyak bug.

dan

Dengan konstruktor, suatu objek memiliki logika konstruksinya sendiri untuk memastikan bahwa tidak ada instance kelas yang tidak valid.

Pernyataan seperti itu terkadang menyiratkan asumsi bahwa:

Jika suatu kelas memiliki konstruktor yang, pada saat itu keluar, telah menempatkan objek ke dalam keadaan yang valid, dan tidak ada metode kelas yang memutasikan keadaan itu untuk membuatnya tidak valid, maka tidak mungkin kode di luar kelas untuk mendeteksi objek dari kelas itu dalam keadaan tidak valid.

Tetapi ini tidak sepenuhnya benar. Sebagian besar bahasa tidak memiliki aturan terhadap konstruktor yang meneruskan this( selfatau apa pun bahasa menyebutnya) ke kode eksternal. Konstruktor seperti itu sepenuhnya mematuhi aturan yang disebutkan di atas, namun berisiko mengekspos objek setengah jadi ke kode eksternal. Ini poin kecil tapi mudah diabaikan.

Daniel Earwicker
sumber
0

Ini agak anekdotal, tetapi saya biasanya memesan konstruktor untuk keadaan penting agar objek menjadi lengkap dan dapat digunakan. Jika tidak ada penyetel-injeksi, begitu konstruktor dijalankan, objek saya harus dapat melakukan tugas-tugas yang diperlukan.

Apa pun yang dapat ditunda, saya tinggalkan konstruktor (menyiapkan nilai output, dll). Dengan pendekatan ini, saya merasa tidak menggunakan konstruktor untuk apa pun kecuali injeksi ketergantungan masuk akal.

Ini juga memiliki manfaat tambahan dari proses desain mental Anda untuk tidak melakukan apa-apa sebelum waktunya. Anda tidak akan menginisialisasi atau melakukan logika yang mungkin tidak pernah berakhir digunakan karena yang terbaik yang Anda lakukan adalah pengaturan dasar untuk pekerjaan di depan.

Akhir
sumber