Başka bir popüler dil, Java / Java EE'deki ile aynı karmaşıklığı yönetirken fabrika şablonunu kullanmak zorunda kalmayı nasıl önlerdi?


23

Fabrika desen (veya en azından kullanım FactoryFactory..) gibi birçok alay konusu olmuştur burada .

RequestProcessorFactoryFactory.RequestProcessorFactory gibi ayrıntılı ve "yaratıcı" adlara sahip olmasının yanı sıra, Java / C ++ dilinde programlamak zorundaysanız ve Abstract_factory_pattern için bir usecase varsa, fabrika modelinde temelde yanlış bir şey var mı?

Başka bir popüler dil (örneğin, Ruby veya Scala ) benzer karmaşıklığı yönetirken kullanmak zorunda kalmayı nasıl önlerdi?

Sormamın nedeni çoğunlukla Java / Java EE ekosistemi bağlamında belirtilen fabrikaların eleştirilerini görüyorum , ancak diğer dillerin / çerçevelerin bunları nasıl çözdüklerini asla açıklamıyorlar.



4
Sorun fabrikaların kullanımı değil - mükemmel bir model. Girişimci java dünyasında özellikle yaygın olan fabrikaların aşırı kullanımı.
CodesInChaos

Çok güzel şaka linki. Bir baharat rafı oluşturmak için araçları almanın işlevsel bir dil yorumunu görmeyi çok isterim. Bu gerçekten eve götürür sanırım.
Patrick M,

Yanıtlar:


28

Sorunuz "Java" ile etiketlendi, Fabrika modelinin neden alay edildiğini sorduğunuza şaşırmamak gerek: Java'nın kendisi, bu modelin hoş bir şekilde paketlenmiş suiistimalleriyle geliyor.

Örneğin, bir dosyadan bir XML belgesi yüklemeyi deneyin ve ona karşı bir XPath sorgusu çalıştırın. Sadece Fabrikaları ve İnşaatçıları kurmak için 10 kod satırı gibi bir şeye ihtiyacınız var:

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);

Acaba bu API'yi tasarlayanlar geliştirici olarak çalışmış mıydı yoksa sadece kitap okuyor mu ve etrafa bir şeyler attı mı? Anladığım kadarıyla sadece ayrıştırıcı yazmak istemediler ve görevi başkalarına bıraktılar ama yine de çirkin bir uygulama için yapılıyor.

Alternatifin ne olduğunu sorduğundan beri, XML dosyasını C # 'a yüklüyor:

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

Yeni yumurtadan çıkmış Java geliştiricilerinin Fabrika deliliğini gördüklerinden ve bunu yapmanın uygun olmadığını düşündüğümden şüpheleniyorum.

Fabrika ve diğer kalıplar, her biri belirli bir iş için uygun olan aletlerdir. Onları işlere uygularsanız, çirkin kodunuz olması zorunlu değildir.


1
C # alternatifinizin XML'den yeni LINQ kullandığını not edin - eski DOM alternatifinin şuna benzer bir şey olurdu: XmlDocument xml = new XmlDocument(); xml.Load("c:\\employees.xml"); XmlNodeList nodes = xml.SelectNodes(expression); (Genel olarak, XML'den LINQ kullanmak, çoğunlukla diğer alanlarda olsa da, kullanımı daha kolaydır.) Ve burada bir KB makalesi MS tarafından önerilen bir yöntemle buldum: support.microsoft.com/kb/308333
Bob

36

Sık sık, insanlar neler olup bittiğini yanlış anlar (ve bu kahkahaların çoğunu içerir).
Her birinin (belki de çoğu) kullandığı kadar kötü olan fabrika düzeni değil.

Ve bu şüphesiz programlama (ve kalıpların) öğretilme biçiminden kaynaklanmaktadır. Okul çocuklarına (genellikle kendilerini “öğrenci” olarak adlandırırlar) “Y deseni kullanarak X yaratmaları” söylenir ve birkaç yinelemeden sonra herhangi bir programlama problemine yaklaşmanın bu olduğunu düşünür.
Böylece okulda olduğu gibi, uygun olup olmadığına bakılmaksızın her şeye ve her şeye karşı olan belirli bir kalıp uygulamaya başlarlar.

Ve ne yazık ki, yazılım tasarımı hakkında kitap yazan üniversite profesörlerini de içeriyor.
Bunun doruk noktası, böyle biri tarafından yaratılmış olması gereken bakımdan hoşnutsuzluk duyduğum bir sistemdi. ).
3 katmanlı bir desene dayanıyordu, her bir katmanın kendisi 3 katmanlı bir sistemdi (ayrıştırmak zorunda kaldı ...). Her bir katman kümesi arasındaki ara yüz üzerinde, her iki tarafta da verileri diğer katmana aktarmak için nesneler üreten bir fabrika ve alınan nesneyi alıcı katmana ait olan birine çevirecek nesneyi üreten bir fabrika vardı.
Her fabrika için soyut bir fabrika vardı (kim bilir, fabrika değişmek zorunda kalabilir ve sonra çağrı kodunun değişmesini istemezsiniz…).
Ve bütün bu karışıklık elbette tamamen belgesizdi.

Sistem 5. normal formda normalize edilmiş bir veritabanına sahipti.

Temelde, gelen ve giden belgeleri kaydetmek ve izlemek için kullanılabilecek bir adres defterinden biraz daha küçük bir sistemde, C ++ ve 50'den fazla veritabanı tablosunda 100 MB'lık bir kod tabanı vardı. 500 form harfin yazdırılması, yazılımı çalıştıran bilgisayara doğrudan bağlı bir yazıcının kullanılması 72 saat kadar sürer.

Bu, insanların neden kalıpları çizdiğini ve özellikle de insanların tek tek belirli bir kalıp üzerine odaklandığını açıklıyor.


40
Diğer bir sebep de, Dörtlü Çetelerin bazılarının, basitçe birinci sınıf fonksiyonları olan bir dilde yapılabilecek şeyler için gerçekten hacklenmiş olmalarıdır. "Strateji", "Gözlemci", "Fabrika", "Komut" ve "Şablon Yöntemi" kalıpları, işlevleri argüman olarak iletmek için kesmek; "Ziyaretçi", toplam tür / varyant / tagged birleşiminde kalıp eşleşmesi gerçekleştirmek için bir kesmek Bazı insanların kelimenin tam anlamıyla birçok dilde önemsiz olduğu bir şey hakkında çok fazla bilgi vermesi gerçeği, insanları C ++, Java ve benzer dillerle alay etmeye iten şeydir.
Doval,

7
Bu fıkra için teşekkür ederim. “Alternatifler neler?” Konusunu da biraz detaylandırabilir misiniz ? Sorunun bir parçası yani biz de öyle değiliz?
Philipp

1
@Doval Bana sadece işlevi iletebildiğiniz zaman, yürütülen komuttan veya strateji denilen değeri nasıl döndürdüğünüzü söyleyebilir misiniz? Ve eğer kapanışlar söylerseniz, o zaman aklınızda bulundurun, daha açık bir sınıf dışında, bir sınıf yaratmakla tamamen aynı olduğunu.
Mutlu

2
@Euphoric Problemi göremiyorum, detaylandırır mısın? Her durumda, tamamen aynı şeyler değiller. Tek bir alana sahip bir nesnenin bir değişkene bir işaretçi / referansla tamamen aynı olduğunu veya bir tamsayının (gerçek) enum ile tamamen aynı olduğunu söyleyebilirsiniz. Uygulamada farklılıklar var: fonksiyonları karşılaştırmanın bir anlamı yok; Henüz isimsiz sınıflar için kısa bir sözdizimi görmedim; ve aynı argüman ve dönüş tipine sahip tüm fonksiyonlar aynı tiptedir (aynı olsalar bile farklı tipte olan farklı isimlerdeki sınıf / arayüzlerin aksine.)
Doval

2
@Euphoric Doğru. Yan etkisi olmadan iş yapmanın bir yolu varsa, bu kötü fonksiyonel stil olarak kabul edilir. Basit bir alternatif, N tür değerlerden birini döndürmek için bir sum type / tagged birliği kullanmak olacaktır. Ne olursa olsun, pratik bir yol olmadığını varsayalım; sınıfla aynı değil. Bu aslında bir arayüzdür (OOP anlamında). Aynı imzaya sahip herhangi bir fonksiyon çiftinin birbirinin yerine geçebileceğini düşünürken, iki sınıfın aynı içeriğe sahip olsalar bile asla birbirinin yerine geçemeyeceğini düşünmeniz zor değildir.
Doval,

17

Fabrikalar, bazı durumlarda şık uygulama tasarımlarına izin veren birçok avantaja sahiptir. Birincisi, daha sonra bir fabrika oluşturarak bir yerde oluşturmak istediğiniz nesnelerin özelliklerini ayarlayabilmeniz ve daha sonra bu fabrikayı teslim edebilmenizdir. Ancak çoğu zaman bunu gerçekten yapmanız gerekmez. Bu durumda bir Fabrika kullanımı, size karşılığında hiçbir şey vermeden, sadece ek bir karmaşıklık ekler. Bu fabrikayı ele alalım, örneğin:

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

Fabrika modeline bir alternatif, benzer yapı modeline benzer. Temel fark, bir Fabrika tarafından yaratılan nesnelerin özelliklerinin Fabrika başlatıldığında, bir Oluşturucu varsayılan durumla başlatıldığında ve tüm özelliklerin daha sonra ayarlanmasıdır.

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

Ancak aşırı mühendislik yapmak sizin probleminiz olduğunda, bir Fabrika'yı bir Oluşturucu ile değiştirmek muhtemelen bir gelişme değildir.

Her iki model için de en basit alternatif, newoperatörle basit bir kurucu ile nesne örnekleri oluşturmaktır.

Widget widget = new ColoredWidget(COLOR_RED);

Bununla birlikte, inşaatçılar çoğu nesne yönelimli dilde çok önemli bir dezavantaja sahiptir: Bu sınıfın bir nesnesini döndürmeleri gerekir ve bir alt tür döndüremezler.

Çalışma zamanında alt türü seçmeniz gerektiğinde, ancak bunun için tamamen yeni bir Oluşturucu veya Fabrika sınıfı oluşturmaya başvurmak istemiyorsanız, bunun yerine fabrika yöntemini kullanabilirsiniz. Bu, o sınıfın veya alt sınıflarından birinin yeni örneklerini döndüren bir sınıfın statik bir yöntemidir. Herhangi bir iç durumu sağlamayan bir Fabrika genellikle böyle bir fabrika yöntemiyle değiştirilebilir:

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

Java 8'deki yeni bir özellik , tıpkı bir vatansız fabrika ile yaptığınız gibi, yöntemleri geçmenize izin veren yöntem referanslarıdır . Bir yöntem referansını kabul eden herhangi bir şey aynı zamanda, aynı işlevsel arayüzü uygulayan herhangi bir nesneyi de kabul eder; bu, aynı zamanda dahili devlete sahip tam teşekküllü bir Fabrika olabilir, böylece bunu yapmak için bir neden gördüğünüzde fabrikaları kolayca tanıtabilirsiniz.


2
Bir fabrika genellikle oluşturulduktan sonra da yapılandırılabilir. Bir inşaatçı için en büyük fark, bir inşaatçının tipik olarak tek, karmaşık bir örnek oluşturmak için kullanıldığı, fabrikaların da benzer birçok örnek oluşturmak için kullanıldığı şeklindedir.
Cephalopod

1
teşekkürler (+1), ancak cevap diğer PL'lerin bunu nasıl çözeceğini açıklayamıyor, yine de Java 8 yöntemi referanslarını belirttiğiniz için teşekkür ederiz.
senseiwu

1
Nesne yönelimli bir çerçevede, gerçek nesne yapısını söz konusu tiple sınırlandırmanın mantıklı olacağını düşündüm ve buna foo = new Bar(23);eşdeğer olmalıydım foo = Bar._createInstance(23);. Bir nesne, özel bir getRealType()yöntem kullanarak kendi türünü güvenilir bir şekilde sorgulayabilmeli , ancak nesneler dış çağrılar tarafından döndürülecek bir üst tip belirleyebilmelidir getType(). Dış kod, çağrının new String("A")gerçekte String[örneğin bir örneğinin aksine SingleCharacterString] bir örneğini döndürüp döndürmemesiyle ilgilenmemelidir .
Supercat

1
Bağlam temelli bir alt sınıf seçmem gerektiğinde, statik yöntem fabrikalarının çoğunu kullanıyorum, ancak arayanın farkları opak olmalı. Örneğin, hepsi içeren draw(OutputStream out)ancak biraz farklı html içeren bir dizi resim sınıfı var , statik yöntem fabrikası durum için doğru sınıfı oluşturur ve sonra arayan sadece draw yöntemini kullanabilir.
Michael Shops,

1
@supercat objektif-c'ye bakmakla ilgilenebilirsiniz. Orada nesnelerin yapımı genellikle basit yöntem çağrılarından oluşur [[SomeClass alloc] init]. Tamamen farklı bir sınıf örneği veya başka bir nesneyi (önbelleklenmiş bir değer gibi)
döndürmek mümkündür

3

Bir veya diğer tür fabrikalar, uygun koşullar altında hemen hemen herhangi bir nesne yönelimli dilde bulunur. Bazen sadece bir dize gibi basit bir parametreye dayanarak ne tür bir nesne oluşturacağınızı seçmeniz için bir yöntem gerekir.

Bazı insanlar bunu çok ileri götürür ve bir fabrika dışında hiçbir zaman bir inşaatçı çağırmak zorunda kalmamak için kodlarını oluşturmaya çalışır. Fabrika fabrikalarınız olduğunda işler saçma olmaya başlar.

Scala'yı öğrendiğimde beni vuran ve Ruby'yi bilmediğim, ama aynı şekilde olduğuna inanıyorum, dilin programcıların her zaman "sıhhi tesisat" işini dış yapılandırma dosyalarına zorlamaya çalışmadığı kadar açık bir ifade olduğuna inanıyorum. . Sınıflarınızı farklı konfigürasyonlarda bir araya getirmek için fabrika fabrikaları kurmaya istekli olmaktansa, Scala'da özellikleri karışık olan nesneleri kullanırsınız. Java'da genellikle çok fazla işlenen işler için dilde basit DSL'ler oluşturmak nispeten kolaydır.

Ayrıca, diğer yorum ve cevapların da belirttiği gibi, kapanışlar ve birinci sınıf fonksiyonlar birçok paterne olan ihtiyacı ortadan kaldırmaktadır. Bu nedenle, anti-kalıpların birçoğunun C ++ 11 olarak kaybolmaya başlayacağına ve Java 8'in yaygınlaştığını düşünüyorum.


sayesinde (1). Cevabınız Scala'nın bundan nasıl kaçınacağına dair bazı şüphelerimi açıklıyor. Dürüst olmak gerekirse, bir keresinde (eğer) Scala, kurumsal düzeyde Java'nın en az yarısı kadar popüler hale geldiğini, çerçeve ordusunun, kalıpların, ücretli uygulama sunucularının vs. ortaya çıkacağını düşünmüyor musunuz?
senseiwu

Bence eğer Scala, Java'yı sollayacaksa, insanların mimarlık yazılımının "Scala yolunu" tercih etmesi nedeniyle olacak. Beni daha çok çarpıcı kılan şey, Java'nın, insanları Java 5’in jenerikleri ve Java 8’in lambdaları ve akışları gibi Scala’ya geçmeye iten özelliklerin daha fazlasını benimsemeye devam etmesidir. özellikler mevcut değildi. Dilleri ne kadar iyi olursa olsun, FactoryFactory'e bağlı olanlar her zaman olacaktır. Açıkçası bu mimarileri seven bazı insanlar, ya da o kadar yaygın olmazlardı.
Karl Bielefeldt

2

Genel olarak, Java programlarının korkunç derecede fazla mühendislik yaptığı [kaynak belirtilmeli] bu eğilim vardır. Birçok fabrikaya sahip olmak, aşırı mühendisliğin en yaygın belirtilerinden biridir; Bu yüzden insanlar onlarla dalga geçiyor.

Özellikle, Java’nın fabrikalarla ilgili sorunu, Java’da a) yapıcıların işlevsiz olması ve b) işlevlerin birinci sınıf vatandaş olmamasıdır.

Böyle bir şey yazabileceğinizi hayal edin ( JRE'ninFunction iyi bilinen bir arayüzü olsun )

// 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);

Bakın, fabrika arayüzü veya sınıf yok. Birçok dinamik dil birinci sınıf fonksiyonlara sahiptir. Ne yazık ki, bu Java 7 veya daha önceki sürümlerde mümkün değildir (fabrikaların aynısını uygulamak okuyucunun alıştırması olarak bırakılmıştır).

Bu nedenle, alternatif çok fazla "farklı bir kalıp kullan" değil, "daha iyi bir nesne modeline sahip bir dil kullan" ya da "basit problemleri fazla mühendislik yapma" dır.


2
bu, sorulan soruyu cevaplamaya bile kalkışmaz: "alternatifler nelerdir?"
gnat

1
İkinci paragrafı okuduğunuzda "diğer diller bunu nasıl yapar" bölümüne gireceksiniz. (Not: diğer cevap da alternatifler göstermiyor)
Cephalopod

4
@gnat Dolaylı olarak, alternatifin birinci sınıf işlevleri kullanmak olduğunu söylemiyor mu? İstediğiniz şey nesneler yaratabilecek bir kara kutu ise, seçenekleriniz bir fabrika ya da işlevdir ve bir fabrikanın sadece kılık değiştirmiş bir işlevidir.
Doval,

3
"Birçok dinamik dilde" biraz yanıltıcıdır, çünkü gerçekte ihtiyaç duyulan şey birinci sınıf işlevlere sahip bir dildir. "Dinamik" buna diktir.
Andres F.

Doğru, ancak genellikle dinamik dillerde bulunan bir özelliktir. Cevabımın başında birinci sınıf bir fonksiyona duyulan ihtiyaçtan bahsettim.
Cephalopod
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.