Apakah Pola Pabrik melanggar Prinsip Terbuka / Tertutup?

14

Mengapa ShapeFactory ini menggunakan pernyataan kondisional untuk menentukan objek apa yang akan dipakai. Jangan kita harus memodifikasi ShapeFactory jika kita ingin menambahkan kelas lain di masa depan? Mengapa ini tidak melanggar prinsip tertutup terbuka?

Desain Pola Pabrik

Desain ShapeFactory

Armon Safai
sumber
2
“Pola pabrik” mana yang Anda maksudkan dengan tepat? Secara umum, pabrik adalah objek atau metode apa pun yang berfungsi untuk membuat instance objek. Lalu ada variasi spesifik dari gagasan umum ini seperti Pola Pabrik Abstrak, di mana setiap instance pabrik mewakili palet pilihan tertentu - biasanya dikelola melalui subklasifikasi alih-alih persyaratan.
amon
3
Terima kasih atas info itu, ini sangat jelas. Itu jelas merupakan contoh dari pola pabrik, tetapi bukan dari Pola Pabrik Abstrak yang umumnya dikaitkan dengan pola pabrik. Kode dalam artikel itu cukup dipertanyakan, dan saya tidak berharap melihat hal seperti itu dalam kode nyata.
amon
@ArmonSafai: Anda banyak menautkan posting blog ini, tetapi Anda tidak benar-benar menjelaskan alasannya. Apakah kita semua entah bagaimana tidak mengetahui polanya? Kami juga memiliki Google, sama seperti Anda.
Robert Harvey
1
@RobertHarvey Saya menautkan postingan blog ini untuk menunjukkan bagaimana pola pabrik di halaman itu menggunakan kondisional
Armon Safai

Jawaban:

20

Kebijaksanaan berorientasi objek konvensional adalah untuk menghindari ifpernyataan dan menggantinya dengan pengiriman dinamis metode override dalam subkelas kelas abstrak. Sejauh ini baik.

Tetapi inti dari pola pabrik adalah membebaskan Anda dari keharusan mengetahui tentang masing-masing subkelas dan hanya bekerja dengan superclass abstrak . Idenya adalah bahwa pabrik tahu lebih baik daripada Anda yang kelas khusus untuk instantiate, dan Anda akan lebih baik bekerja hanya dengan metode yang dipublikasikan oleh kelas super. Ini sering benar dan merupakan pola yang berharga.

Oleh karena itu, tidak mungkin menulis kelas pabrik dapat mengabaikan ifpernyataan. Ini akan menggeser beban memilih kelas tertentu ke penelepon, yang persis apa yang seharusnya dihindari pola. Tidak semua prinsip bersifat absolut (pada kenyataannya, tidak ada prinsip yang absolut), dan jika Anda menggunakan pola ini Anda akan menganggap bahwa manfaat darinya lebih besar daripada manfaat tidak menggunakan if.

Kilian Foth
sumber
2
Sangat mungkin untuk membuat pola pabrik tanpa banyak if. Lihat jawaban @ BЈовић untuk contoh sederhana tentang cara mencapai ini. Diturunkan.
David Arno
11
@ Davidvidno Secara alami, ada berbagai cara untuk memilih kelas yang konkret. Service Locator adalah satu, Kontainer IoC yang dapat dikonfigurasi adalah yang lain. Itu hanya detail implementasi; mereka tidak mengurangi pesan utama Killian, yaitu bahwa Pabrik membebaskan penelepon dari harus memutuskan kelas konkret mana yang akan dipakai. Jangan terperosok dalam detailnya.
Robert Harvey
1
Pernyataan hebat yang sama sekali tidak menjawab pertanyaan.
Martin Maat
1
@ R.Schmitz Saya pikir Anda salah dalam asumsi Anda. Saya pikir banyak orang melewatkan pertanyaan ini dari OP: "Tidakkah kita harus memodifikasi ShapeFactory jika kita ingin menambahkan kelas lain di masa depan?" JELAS, OP bingung berpikir bahwa pola ini melanggar OCP karena untuk menambah fungsionalitas baru, Anda harus memodifikasi kode yang ada. Jawaban yang benar untuk pertanyaan ini dapat ditemukan dalam jawaban saya. Jawaban singkatnya: Anda membiarkan kode itu sendiri dan menerapkan pola Pabrik Abstrak untuk MEMPERPANJANG (tidak mengubah) fungsi yang ada. Karena jawaban Kilian ini TIDAK menjawab pertanyaan itu.
hfontanez
5

Contohnya mungkin menggunakan pernyataan kondisional karena itu adalah yang paling sederhana. Implementasi yang lebih kompleks mungkin menggunakan peta atau konfigurasi atau (jika Anda ingin benar-benar mewah) semacam registri di mana kelas dapat mendaftar sendiri. Namun tidak ada yang salah dengan menggunakan kondisi jika jumlah kelas kecil dan perubahan jarang terjadi.

Memperluas persyaratan untuk menambah dukungan untuk subkelas baru di masa depan memang akan menjadi pelanggaran prinsip terbuka / tertutup. Solusi "benar" adalah membuat pabrik baru dengan antarmuka yang sama. Yang mengatakan, kepatuhan terhadap prinsip O / C harus selalu ditimbang terhadap prinsip-prinsip desain lainnya seperti KISS dan YAGNI.

Yang mengatakan, kode yang ditampilkan jelas contoh kode yang dirancang untuk menunjukkan konsep pabrik dan tidak ada yang lain. Misalnya itu adalah gaya yang benar-benar buruk untuk mengembalikan nol seperti contohnya, tetapi penanganan kesalahan yang lebih rumit hanya akan mengaburkan intinya. Kode contoh bukan kode kualitas produksi, kode apa pun yang tidak Anda harapkan.

JacquesB
sumber
Bisakah Anda jelaskan bagaimana peta / konfigurasi / registri akan bekerja?
Armon Safai
@ArmonSafai: Berikut adalah contoh: jkfill.com/2010/12/29/self-registering-factories-in-c-sharp
JacquesB
Pabrik yang mendaftar sendiri, AFAIK, tidak mungkin dalam C ++ di dalam perpustakaan statis karena variabel global yang tidak digunakan (yaitu odr-use) dibuang oleh toolchain.
void.pointer
@ArmonSafai membaca ini untuk lebih memahami goo.gl/RYuNSM
AZ_
2

Pola itu sendiri tidak melanggar Prinsip Terbuka / Tertutup (OCP). Namun, kami melanggar OCP ketika kami menggunakan pola yang salah.

Jawaban sederhana untuk pertanyaan ini adalah sebagai berikut:

  1. Buat fungsionalitas dasar Anda menggunakan Pola Metode Pabrik .
  2. PERINGKAT fungsionalitas Anda dengan menggunakan Pola Pabrik Abstrak

Dalam contoh yang diberikan, fungsionalitas dasar Anda mendukung tiga bentuk: Lingkaran, Kotak, dan Kotak. Misalkan Anda perlu mendukung Triangle, Pentagon, dan Hexagon di masa depan. Untuk melakukan ini TANPA melanggar OCP, Anda harus membuat pabrik tambahan untuk mendukung bentuk baru Anda (sebut saja AdvancedShapeFactory) dan kemudian gunakan AbstractFactory untuk memutuskan pabrik apa yang perlu Anda buat untuk membuat bentuk apa pun yang Anda butuhkan.

hfontanez
sumber
Saya lebih suka solusi pabrik yang mendaftar sendiri (dengan tidak adanya wadah IoC yang dapat dikonfigurasi yang terbaik), karena jika tidak, apa yang kami dapatkan dari proposal Anda pada dasarnya adalah pabrik pabrik , dan saat itulah segalanya menjadi terlalu rumit.
Nom1fan
1

Jika Anda berbicara tentang pola Abstrak Pabrik, pengambilan keputusan sering bukan di Pabrik itu sendiri tetapi dalam kode aplikasi. Kode itulah yang memilih pabrik beton apa yang akan dipakai dan memberikan kode klien yang akan menggunakan objek yang diproduksi oleh Pabrik. Lihat akhir dari contoh Java di sini: https://en.wikipedia.org/wiki/Abstract_factory_pattern

Pengambilan keputusan tidak selalu berarti ifpernyataan. Itu bisa membaca jenis Pabrik beton dari file konfigurasi, berasal dari struktur peta, dll.

guillaume31
sumber
Jika saya, si penelepon, sedang membuat keputusan tentang kelas konkret mana yang akan dipakai, lalu mengapa saya repot-repot dengan Pabrik Abstrak?
Robert Harvey
Silakan tentukan "si penelepon". Seperti yang saya jelaskan dalam jawaban saya, ada kode aplikasi global dan kemudian ada kode yang perlu menelurkan objek menggunakan Pabrik. Sedangkan yang kedua memang perlu menyadari kelas konkret untuk instantiate, beberapa kode kontekstual lainnya memiliki tahu tentang hal itu dan baru itu ...
guillaume31
0

Jika Anda berpikir tentang Open-Close di tingkat kelas dengan pabrik ini, Anda membuat kelas lain di sistem Anda Open-Close, misalnya jika Anda memiliki kelas lain yang mengambil satu Shape dan menghitung area (contoh umum) kelas ini adalah OpenClose karena dapat menghitung luas untuk tipe bentuk baru tanpa modifikasi. Kemudian Anda memiliki kelas lain yang menggambar bentuk, kelas lain yang mengambil bentuk N dan mengembalikan yang lebih besar dan Anda dapat berpikir secara umum bahwa kelas-kelas lain di sistem Anda yang berhubungan dengan bentuk adalah Open-Close (setidaknya tentang bentuk). Melihat desain pabrik memungkinkan sisa sistem menjadi Open-Close dan tentunya pabrik itu sendiri TIDAK Open-Close.

Tentu saja Anda dapat membuat pabrik ini buka-tutup juga, melalui semacam pemuatan dinamis dan seluruh sistem Anda bisa Terbuka-Tutup (misalnya, Anda dapat menambahkan bentuk baru dengan menjatuhkan beberapa toples di classpath). Anda perlu mengevaluasi apakah kompleksitas tambahan ini layak tergantung pada sistem yang Anda bangun, tidak semua sistem perlu fitur yang dapat dicolokkan dan tidak semua sistem harus sepenuhnya Terbuka-Tutup.

AlfredoCasado
sumber
0

Prinsip tertutup-terbuka, sebagai prinsip substitusi Liskov, berlaku untuk pohon kelas, untuk hierarki warisan. Dalam contoh Anda, kelas pabrik tidak di pohon keluarga dari kelas yang instantiate sehingga tidak bisa melanggar aturan ini. Akan ada pelanggaran jika GetShape Anda (atau lebih tepatnya, CreateShape) diimplementasikan di kelas basis bentuk.

Martin Maat
sumber
-2

Itu semua tergantung bagaimana Anda menerapkannya. Anda bisa menggunakan std::mapuntuk menahan fungsi pointer ke fungsi membuat objek. Maka prinsip buka / tutup tidak dilanggar. Atau beralih / kasing.

Lagi pula, jika Anda tidak menyukai pola pabrik, Anda selalu dapat menggunakan injeksi ketergantungan.

BЈовић
sumber
6
Bagaimana saklar / kasing lebih baik dari kondisional? Menggunakan peta / dikt / tabel untuk mewakili kode karena data baik, jika Anda benar-benar membutuhkan registri dari implementasi yang berbeda - misalnya dalam beberapa implementasi wadah DI. Tetapi memiliki callback berbeda dari jenis yang sama tidak diperlukan untuk sebagian besar pabrik! Saya tidak mengerti mengapa Anda menyarankan itu. Juga, banyak kontainer DI diterapkan dalam hal objek pabrik, jadi menyarankan untuk menggunakan DI bukan pabrik tampaknya agak melingkar.
amon
1
@amon Saya bermaksud menggunakan tipe DI lainnya - bukan pabrik.
BЈовић
1
Bagaimana pabrik Anda akan memutuskan pointer mana yang akan digunakan? Akhirnya Anda harus membuat keputusan.
whatsisname
@ArmonSafai ???
BЈовић