Bagaimana bahasa populer lain menghindari harus menggunakan pola pabrik sambil mengelola kompleksitas yang sama seperti di Java / Java EE?

23

Pola pabrik (atau setidaknya penggunaan FactoryFactory..) adalah bagian dari banyak lelucon, seperti di sini .

Selain memiliki nama verbose dan "kreatif" seperti RequestProcessorFactoryFactory.RequestProcessorFactory , apakah ada yang salah secara mendasar dengan pola pabrik jika Anda harus memprogram di Java / C ++ dan ada usecase untuk Abstract_factory_pattern ?

Bagaimana bahasa populer lain (misalnya, Ruby atau Scala ) menghindari keharusan menggunakannya sambil mengelola kompleksitas yang sama?

Alasan saya bertanya adalah saya melihat sebagian besar kritik dari pabrik yang disebutkan dalam konteks ekosistem Jawa / Jawa EE , tetapi mereka tidak pernah menjelaskan bagaimana bahasa / kerangka kerja lain menyelesaikannya.

senseiwu
sumber
4
Masalahnya bukan penggunaan pabrik - itu pola yang sangat baik. Ini adalah penggunaan pabrik yang terlalu lazim di dunia jawa perusahaan.
CodesInChaos
Link lelucon yang sangat bagus. Saya akan senang melihat interpretasi bahasa fungsional mengambil alat untuk membangun rak bumbu. Itu benar-benar akan mengantarnya pulang, saya pikir.
Patrick M

Jawaban:

28

Pertanyaan Anda ditandai dengan "Java", tidak heran Anda bertanya mengapa pola Pabrik diejek: Java sendiri dilengkapi dengan penyalahgunaan pola yang dikemas dengan baik.

Misalnya, coba memuat dokumen XML dari file dan jalankan permintaan XPath terhadapnya. Anda memerlukan sekitar 10 baris kode hanya untuk mengatur Pabrik dan Pembangun:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder(); 

Document xmlDocument = builder.parse(new FileInputStream("c:\\employees.xml"));
XPath xPath =  XPathFactory.newInstance().newXPath();

xPath.compile(expression).evaluate(xmlDocument);

Saya bertanya-tanya apakah orang-orang yang merancang API ini pernah bekerja sebagai pengembang atau mereka hanya membaca buku dan melempar barang-barang. Saya mengerti mereka hanya tidak ingin menulis parser sendiri dan menyerahkan tugas kepada orang lain tetapi masih membuat implementasi yang jelek.

Karena Anda bertanya apa alternatifnya, memuat file XML di C #:

XDocument xml = XDocument.Load("c:\\employees.xml");
var nodes = xml.XPathSelectElements(expression);

Saya menduga pengembang Java yang baru menetas melihat kegilaan Pabrik dan berpikir tidak apa-apa untuk melakukan itu - jika para genius yang membangun Jawa sendiri telah menggunakan mereka sebanyak itu.

Pabrik dan pola lainnya adalah alat, masing-masing cocok untuk pekerjaan tertentu. Jika Anda menerapkannya pada pekerjaan, mereka tidak cocok karena Anda pasti memiliki kode jelek.

Sten Petrov
sumber
1
Hanya perhatikan bahwa alternatif C # Anda menggunakan LINQ yang lebih baru untuk XML - alternatif DOM yang lebih lama akan terlihat seperti ini: XmlDocument xml = new XmlDocument(); xml.Load("c:\\employees.xml"); XmlNodeList nodes = xml.SelectNodes(expression); (Secara keseluruhan, LINQ ke XML jauh lebih mudah digunakan, meskipun sebagian besar di daerah lain.) Dan inilah artikel KB Saya menemukan, dengan metode yang direkomendasikan MS: support.microsoft.com/kb/308333
Bob
36

Seperti yang sering terjadi, orang salah mengerti apa yang sedang terjadi (dan itu termasuk banyak yang tertawa).
Bukan pola pabrik itu sendiri yang seburuk yang dilakukan oleh banyak orang (bahkan mungkin sebagian besar) orang.

Dan itu tidak diragukan lagi berasal dari cara pemrograman (dan pola) diajarkan. Anak-anak sekolah (sering menyebut diri mereka sendiri "siswa") diminta untuk "membuat X menggunakan pola Y" dan setelah beberapa iterasi berpikir bahwa itulah cara untuk mendekati masalah pemrograman apa pun.
Jadi mereka mulai menerapkan pola tertentu yang kebetulan mereka sukai di sekolah terhadap apa pun dan semua, apakah itu sesuai atau tidak.

Dan itu termasuk profesor universitas yang menulis buku tentang desain perangkat lunak, sayangnya.
Puncak dari ini adalah sistem saya memiliki ketidaksenangan yang berbeda karena harus memelihara yang telah dibuat oleh orang seperti itu (orang itu bahkan memiliki beberapa buku tentang desain berorientasi objek dengan namanya, dan posisi mengajar di departemen CS dari sebuah universitas besar. ).
Itu didasarkan pada pola 3 tier, dengan masing-masing tier itu sendiri sistem 3 tier (harus decouple ...). Pada antarmuka antara setiap set tingkatan, di kedua sisi, adalah pabrik untuk menghasilkan objek untuk mentransfer data ke tingkat lainnya, dan pabrik untuk menghasilkan objek untuk menerjemahkan objek yang diterima ke salah satu milik lapisan penerima.
Untuk setiap pabrik ada pabrik abstrak (siapa tahu, pabrik mungkin harus berubah dan kemudian Anda tidak ingin kode panggilan harus berubah ...).
Dan kekacauan itu tentu saja benar-benar tidak berdokumen.

Sistem memiliki database dinormalisasi ke bentuk normal ke-5 (I kid you not).

Sebuah sistem yang pada dasarnya sedikit lebih dari sebuah buku alamat yang dapat digunakan untuk mencatat dan melacak dokumen yang masuk dan keluar, ia memiliki basis kode 100MB di C ++ dan lebih dari 50 tabel database. Mencetak 500 surat formulir akan memakan waktu 72 jam menggunakan printer yang terhubung langsung ke komputer yang menjalankan perangkat lunak.

Itulah sebabnya orang-orang mencemooh pola, dan khususnya orang yang berfokus sendirian pada satu pola tertentu.

jwenting
sumber
40
Alasan lainnya adalah bahwa beberapa pola Geng Empat benar-benar merupakan peretasan yang kasar untuk hal-hal yang dapat dilakukan secara sepele dalam bahasa dengan fungsi kelas satu, yang jenisnya membuat mereka pola anti-tak terhindarkan. Pola "Strategi", "Pengamat", "Pabrik", "Perintah" dan "Metode Templat" adalah retasan untuk memberikan fungsi sebagai argumen; "Pengunjung" adalah retasan untuk melakukan pencocokan pola pada jenis gabungan / varian / tagged. Fakta bahwa beberapa orang membuat banyak hal tentang sesuatu yang benar-benar sepele dalam banyak bahasa lain adalah apa yang membuat orang mengejek C ++, Java dan bahasa serupa.
Doval
7
Terima kasih atas anekdot ini. Bisakah Anda sedikit menguraikan "apa saja alternatifnya?" bagian dari pertanyaan sehingga kita tidak menjadi seperti itu juga?
Philipp
1
@Doval Bisakah Anda memberi tahu saya bagaimana Anda mengembalikan nilai dari perintah yang dieksekusi atau disebut strategi ketika Anda hanya dapat melewati fungsi? Dan jika Anda mengatakan penutupan, maka perlu diingat itu sama persis dengan membuat kelas, kecuali lebih eksplisit.
Euforia
2
@ Euforia Saya tidak melihat masalah, bisakah Anda menjelaskan? Bagaimanapun, mereka tidak persis sama. Anda mungkin juga mengatakan bahwa objek dengan bidang tunggal persis sama dengan pointer / referensi ke variabel atau bahwa integer persis sama dengan enum (nyata). Ada perbedaan dalam praktiknya: tidak masuk akal untuk membandingkan fungsi; Saya belum melihat sintaks ringkas untuk kelas anonim; dan semua fungsi dengan argumen dan tipe pengembalian yang sama memiliki tipe yang sama (tidak seperti kelas / antarmuka dengan nama yang berbeda, yang merupakan tipe yang berbeda bahkan ketika mereka identik.)
Doval
2
@ Euforia Benar. Itu akan dianggap gaya fungsional yang buruk jika ada cara untuk menyelesaikan pekerjaan tanpa efek samping. Alternatif sederhana adalah dengan menggunakan tipe penjumlahan / tagged union untuk mengembalikan salah satu dari N jenis nilai. Apapun, mari kita asumsikan tidak ada cara praktis; itu tidak sama dengan kelas. Ini sebenarnya sebuah antarmuka (dalam arti OOP.) Tidak sulit untuk melihat apakah Anda menganggap bahwa setiap pasangan fungsi dengan tanda tangan yang sama akan dapat dipertukarkan, sementara dua kelas tidak pernah dapat dipertukarkan meskipun mereka memiliki konten yang sama persis.
Doval
17

Pabrik memiliki banyak keunggulan yang memungkinkan desain aplikasi yang elegan dalam beberapa situasi. Salah satunya adalah Anda dapat mengatur properti objek yang nanti ingin Anda buat di satu tempat dengan membuat pabrik, lalu menyerahkan pabrik itu ke mana-mana. Tetapi seringkali Anda sebenarnya tidak perlu melakukan itu. Dalam hal menggunakan Pabrik hanya menambah kompleksitas tambahan tanpa benar-benar memberi Anda imbalan. Mari kita ambil pabrik ini, misalnya:

WidgetFactory redWidgetFactory = new ColoredWidgetFactory(COLOR_RED);
Widget widget = redWidgetFactory.create();

Salah satu alternatif dari pola Pabrik adalah pola Builder yang sangat mirip. Perbedaan utama adalah bahwa properti dari objek yang dibuat oleh Pabrik diatur ketika Pabrik diinisialisasi, sedangkan Builder diinisialisasi dengan keadaan default dan semua properti diatur setelahnya.

WidgetBuilder widgetBuilder = new WidgetBuilder();
widgetBuilder.setColor(COLOR_RED);
Widget widget = widgetBuilder.create();

Tetapi ketika overengineering adalah masalah Anda, mengganti Pabrik dengan Builder kemungkinan tidak banyak perbaikan.

Penggantian paling sederhana untuk kedua pola tentu saja untuk membuat instance-objek dengan konstruktor sederhana dengan newoperator:

Widget widget = new ColoredWidget(COLOR_RED);

Konstruktor, bagaimanapun, memiliki kelemahan penting dalam sebagian besar bahasa berorientasi objek: Mereka harus mengembalikan objek dari kelas yang tepat dan tidak dapat mengembalikan sub-tipe.

Ketika Anda perlu memilih sub-tipe saat runtime tetapi tidak ingin menggunakan untuk membuat kelas Builder atau Factory baru untuk itu, Anda dapat menggunakan metode pabrik sebagai gantinya. Ini adalah metode statis kelas yang mengembalikan contoh baru dari kelas itu atau salah satu sub-kelasnya. Pabrik yang tidak memiliki status internal apa pun seringkali dapat diganti dengan metode pabrik seperti itu:

 Widget widget = Widget.createColoredWidget(COLOR_RED); // returns an object of class RedColoredWidget

Fitur baru di Java 8 adalah referensi metode yang memungkinkan Anda untuk meneruskan metode, sama seperti yang Anda lakukan dengan pabrik stateless. Dengan mudah, apa pun yang menerima referensi metode juga menerima objek apa pun yang mengimplementasikan antarmuka fungsional yang sama, yang juga bisa menjadi Pabrik lengkap dengan keadaan internal, sehingga Anda dapat dengan mudah memperkenalkan pabrik nanti, ketika Anda melihat alasan untuk melakukannya.

Philipp
sumber
2
Sebuah pabrik seringkali juga dapat dikonfigurasi setelah pembuatan. Perbedaan utama, afaik, untuk pembangun adalah bahwa pembangun biasanya digunakan untuk membuat contoh tunggal yang kompleks, sedangkan pabrik digunakan untuk membuat banyak contoh serupa.
Cephalopod
1
terima kasih (+1), tetapi jawabannya gagal menjelaskan bagaimana PL lain akan menyelesaikannya, namun terima kasih telah menunjukkan referensi metode Java 8.
senseiwu
1
Saya sering berpikir bahwa akan masuk akal dalam kerangka berorientasi objek untuk membatasi konstruksi objek yang sebenarnya untuk jenis yang dimaksud, dan harus foo = new Bar(23);setara dengan foo = Bar._createInstance(23);. Suatu objek harus dapat secara andal menanyai tipenya sendiri menggunakan getRealType()metode pribadi , tetapi objek harus mampu menunjuk suatu supertype untuk dikembalikan oleh panggilan luar ke getType(). Kode luar seharusnya tidak peduli apakah panggilan untuk new String("A")benar - benar mengembalikan instance String[sebagai lawan dari misalnya instance SingleCharacterString].
supercat
1
Saya menggunakan banyak pabrik metode statis ketika saya harus memilih subclass berdasarkan konteks tetapi perbedaannya harus buram ke pemanggil. Sebagai contoh saya memiliki serangkaian kelas gambar yang semuanya berisi draw(OutputStream out)tetapi menghasilkan html yang sedikit berbeda, pabrik metode statis menciptakan kelas yang tepat untuk situasi dan kemudian penelepon hanya dapat menggunakan metode menggambar.
Michael Shopsin
1
@supercat Anda mungkin tertarik untuk melihat objektif-c. Ada konstruksi objek terdiri dari panggilan metode sederhana, biasanya [[SomeClass alloc] init]. Dimungkinkan untuk mengembalikan instance kelas yang sama sekali berbeda atau objek lain (seperti nilai cache)
axelarge
3

Berbagai jenis pabrik ditemukan dalam hampir semua bahasa berorientasi objek dalam keadaan yang sesuai. Terkadang Anda hanya butuh cara untuk memilih objek apa yang akan dibuat berdasarkan parameter sederhana seperti string.

Beberapa orang mengambilnya terlalu jauh dan mencoba merancang kode mereka untuk tidak perlu memanggil konstruktor kecuali di dalam pabrik. Hal-hal mulai menjadi konyol ketika Anda memiliki pabrik pabrik.

Apa yang mengejutkan saya ketika saya belajar Scala, dan saya tidak tahu Ruby, tetapi percaya bahwa itu adalah cara yang sama, adalah bahwa bahasa tersebut cukup ekspresif sehingga programmer tidak selalu mencoba untuk mendorong pekerjaan "plumbing" ke file konfigurasi eksternal . Alih-alih tergoda untuk membuat pabrik pabrik untuk menyatukan kelas-kelas Anda dalam konfigurasi yang berbeda, di Scala Anda menggunakan objek yang bercampur. Ini juga relatif mudah untuk membuat DSL sederhana dalam bahasa untuk tugas-tugas yang sering direkayasa secara berlebihan di Jawa.

Juga, seperti yang ditunjukkan oleh komentar dan jawaban lainnya, fungsi penutupan dan kelas satu meniadakan kebutuhan akan banyak pola. Untuk alasan itu, saya percaya banyak dari anti-pola akan mulai menghilang ketika C ++ 11 dan Java 8 mendapatkan adopsi luas.

Karl Bielefeldt
sumber
terima kasih (+1). Jawaban Anda menjelaskan beberapa keraguan saya mengenai bagaimana Scala akan menghindarinya. Sejujurnya, tidakkah Anda berpikir bahwa sekali (jika) Scala menjadi setidaknya setengah sepopuler Java di tingkat perusahaan, pasukan kerangka kerja, pola, server aplikasi berbayar, dll. Akan muncul?
senseiwu
Saya pikir jika Scala menyalip Jawa, itu karena orang lebih suka "cara Scala" dalam merancang perangkat lunak. Yang menurut saya lebih besar kemungkinannya adalah Java terus mengadopsi lebih banyak fitur yang mendorong orang untuk beralih ke Scala, seperti generik Java 5 dan lambdas dan stream Java 8, yang diharapkan pada akhirnya akan mengakibatkan programmer meninggalkan pola-anti yang muncul ketika mereka fitur tidak tersedia. Akan selalu ada penganut FactoryFactory, tidak peduli seberapa baik bahasa yang didapat. Jelas beberapa orang menyukai arsitektur itu, atau mereka tidak akan begitu umum.
Karl Bielefeldt
2

Secara umum, ada kecenderungan bahwa program-program Java sangat direkayasa berlebihan. Memiliki banyak pabrik adalah salah satu gejala over-engineering yang paling umum; itu sebabnya orang mengolok-olok mereka.

Secara khusus, masalah Jawa dengan pabrik adalah bahwa di Jawa a) konstruktor bukan fungsi dan b) fungsi bukan warga negara kelas satu.

Bayangkan Anda bisa menulis sesuatu seperti ini (biarlah Functionantarmuka JRE yang terkenal )

// Framework code
public Node buildTree(Function<BranchNode, Node, Node> branchNodeFactory) {
    Node current = nextNode();
    while (hasNextNode()) {
        current = branchNodeFactory.call(current, nextNode());
    }
    return current
}

// MyDataNode.java
public class MyBranchNode implements BranchNode {

    public MyBranchNode(Node left, Node right) { ... }
}

// Client code
Node root = buildTree(MyBranchNode::new);

Lihat, tidak ada antarmuka pabrik atau kelas. Banyak bahasa dinamis memiliki fungsi kelas satu. Sayangnya, ini tidak mungkin dengan Java 7 atau sebelumnya (menerapkan hal yang sama dengan pabrik dibiarkan sebagai latihan untuk pembaca).

Jadi, alternatifnya bukanlah "menggunakan pola yang berbeda", tetapi lebih "menggunakan bahasa dengan model objek yang lebih baik" atau "jangan merekayasa masalah sederhana".

Cephalopod
sumber
2
ini bahkan tidak berusaha menjawab pertanyaan yang diajukan: "apa alternatifnya?"
nyamuk
1
Baca melewati paragraf kedua dan Anda akan sampai ke bagian "bagaimana bahasa lain melakukannya". (PS: jawaban lain juga tidak menunjukkan alternatif)
Cephalopod
4
@gnat Bukankah dia secara tidak langsung mengatakan bahwa alternatifnya adalah menggunakan fungsi kelas satu? Jika yang Anda inginkan adalah kotak hitam yang dapat membuat objek, opsi Anda adalah pabrik atau fungsi, dan pabrik hanya fungsi yang menyamar.
Doval
3
"Dalam banyak bahasa dinamis" agak menyesatkan, karena yang sebenarnya dibutuhkan adalah bahasa dengan fungsi kelas satu. "Dinamis" adalah ortogonal untuk ini.
Andres F.
Benar, tetapi itu adalah properti yang sering ditemukan dalam bahasa dinamis. Saya menyebutkan perlunya fungsi kelas satu di awal jawaban saya.
Cephalopod