Yapımcılar ve Fabrika Yöntemleri [kapalı]


181

Sınıfları modellerken, başlangıç ​​durumuna getirmenin tercih edilen yolu nedir:

  1. Yapıcılar, veya
  2. Fabrika Yöntemleri

Her ikisini de kullanmak için dikkat edilmesi gereken noktalar nelerdir?

Bazı durumlarda, nesne oluşturulamazsa null döndüren bir fabrika yöntemine sahip olmayı tercih ederim. Bu kodu düzgün yapar. Yapıcıdan bir istisna atmanın aksine, alternatif eylem yapmadan önce döndürülen değerin boş olup olmadığını kontrol edebilirim. (Şahsen istisnaları sevmiyorum)

Diyelim ki, bir sınıf üzerinde bir id değeri bekleyen bir yapıcı var. Yapıcı sınıfı veritabanından doldurmak için bu değeri kullanır. Belirtilen kimliğe sahip bir kaydın bulunmaması durumunda, yapıcı bir RecordNotFoundException oluşturur. Bu durumda tüm bu sınıfların yapımını bir try..catch bloğu içine almam gerekecek.

Bunun aksine, kayıt bulunmazsa null döndürecek olan bu sınıflar üzerinde statik bir fabrika yöntemi olabilir.

Bu durumda, yapıcı veya fabrika yönteminde hangi yaklaşım daha iyidir?

Yanıtlar:


66

Tasarım Desenleri sayfa 108 : Gamma, Helm, Johnson ve Vlissides'in Yeniden Kullanılabilir Nesneye Yönelik Yazılım Öğeleri.

Aşağıdaki durumlarda Fabrika Yöntemi desenini kullanın

  • bir sınıf yaratması gereken nesne sınıfını tahmin edemez
  • bir sınıf alt sınıflarının oluşturduğu nesneleri belirtmesini ister
  • sınıflar sorumluluğu birkaç yardımcı alt sınıftan birine devreder ve hangi yardımcı alt sınıfın temsilci olduğu bilgisini yerelleştirmek istersiniz

21
Statik fabrika yöntemi, GoF tasarım modelinden - Fabrika Yöntem Deseni'nden farklıdır. stackoverflow.com/questions/929021/…
Sree Rama

Lütfen GoF tasarım desenlerinin Fabrika yöntemiyle karşılaştırmayın.
Sree Rama

137
Bu bana hiçbir şey açıklamaz
Sushant

@Sushant, neden böyle?
PaulD

2
Bu cevap soruyu cevaplamıyor, sadece kavramı anlamak / açıklamak için bilgi okumak için bilgi aktarıyor ... Bu daha çok bir yorum.
Crt

202

Kendinize ne olduklarını ve neden onlara sahip olduğumuzu sorun. İkisi de bir nesne örneği oluşturmak için oradalar.

ElementarySchool school = new ElementarySchool();
ElementarySchool school = SchoolFactory.Construct(); // new ElementarySchool() inside

Farkı yok. Şimdi çeşitli okul türlerimiz olduğunu ve ElementarySchool'u kullanmaktan Lise'ye (bir ElementarySchool'dan türetilen veya ElementarySchool ile aynı arayüzü uygulayan) geçmek istediğimizi düşünün. Kod değişikliği şöyle olur:

HighSchool school = new HighSchool();
HighSchool school = SchoolFactory.Construct(); // new HighSchool() inside

Bir arayüz durumunda:

ISchool school = new HighSchool();
ISchool school = SchoolFactory.Construct(); // new HighSchool() inside

Şimdi bu kodu birden fazla yerde varsa, fabrika yöntemini kullanmanın oldukça ucuz olabileceğini görebilirsiniz, çünkü fabrika yöntemini değiştirdikten sonra (ikinci örneği arayüzlerle kullanırsak).

Ve bu ana fark ve avantaj. Karmaşık bir sınıf hiyerarşileriyle uğraşmaya başladığınızda ve böyle bir hiyerarşiden dinamik olarak bir sınıf örneği oluşturmak istediğinizde aşağıdaki kodu alırsınız. Fabrika yöntemleri daha sonra yönteme hangi somut örneğin somutlaştırılacağını söyleyen bir parametre alabilir. Diyelim ki bir MyStudent sınıfınız var ve öğrencinizin o okulun bir üyesi olması için ilgili ISchool nesnesini başlatmanız gerekiyor.

ISchool school = SchoolFactory.ConstructForStudent(myStudent);

Artık uygulamanızda, farklı IStudent nesneleri için hangi ISchool nesnesinin somutlaştırılacağını belirleyen iş mantığını içeren bir yer var.

Yani - basit sınıflar için (değer nesneleri vb.) Yapıcı gayet iyi (uygulamanızı incelemek istemezsiniz) ancak karmaşık sınıf hiyerarşileri için fabrika yöntemi tercih edilen bir yoldur.

Bu şekilde dört tasarım çetesinden "Program bir uygulamaya değil, bir arayüze" ilk tasarım ilkesini takip edersiniz .


2
Basit bir sınıf olduğunu düşündüğünüzde bile, birinin basit sınıfınızı genişletme şansı vardır, bu nedenle fabrika yöntemi hala daha iyidir. Örneğin, ElementarySchool ile başlayabilirsiniz, ancak daha sonra birisi (kendiniz dahil) PrivateElementarySchool ve PublicElementarySchool ile genişletebilir.
jack

10
kabul edilen cevap bu olmalıdır
am05mhz

2
@David, iyi yanıt, ancak her arabirim uygulamasının inşaat için farklı parametreler gerektirebileceği bir örneği genişletebilirsiniz. İşte aptal bir örnek: IFood sandwich = new Sandwich(Cheese chz, Meat meat);ve IFood soup = new Soup(Broth broth, Vegetable veg);fabrika ve inşaatçı burada nasıl yardımcı olabilir?
Brian

1
Ben sadece fabrika kullanımı amacı ile ilgili diğer üç açıklamayı okudum ve sonunda benim için "tık" olan bu. Teşekkür ederim!
Daniel Peirano

Bu neden cevap kabul edilmiyor?
Tomas

74

Etkili Java 2'yi (erişiminiz varsa) okumanız gerekir Öğe 1: Yapıcılar yerine statik fabrika yöntemlerini göz önünde bulundurun .

Statik fabrika yöntemleri avantajları:

  1. İsimleri var.
  2. Her çağrıldıklarında yeni bir nesne oluşturmaları gerekmez.
  3. Dönüş türlerinin herhangi bir alt türündeki bir nesneyi döndürebilirler.
  4. Parametreli tip örnekleri yaratmanın ayrıntı düzeyini azaltırlar.

Statik fabrika yöntemleri dezavantajları:

  1. Yalnızca statik fabrika yöntemleri sağlanırken, genel veya korumalı kurucuları olmayan sınıflar alt sınıflara ayrılamaz.
  2. Diğer statik yöntemlerden kolayca ayırt edilemezler

4
Bu bana Java'da ciddi bir hata, daha sonra genel bir OOD sorunu gibi geliyor. Yapıcıları bile olmayan çok sayıda OO dili var , ancak alt sınıflandırma gayet iyi çalışıyor.
Jörg W Mittag

1
@cherouvim Neden kod çoğunlukla Yapıcılar kullanılarak yazılırsa( factory methods are better than Constructors. ( Item-1 ) ) Effective java
Asif Mushtaq

Güzel nokta. Yine de Java'ya özgüdür. Fabrika yöntemlerini diğer statik yöntemlerden ayırt edilebilir kılan bir dil özelliği için bir durum yapılabilir.
OCDev

30

Varsayılan olarak yapıcılar tercih edilmelidir, çünkü anlaşılması ve yazılması daha kolaydır. Ancak, bir nesnenin yapım özelliklerini özellikle istemci kodu tarafından anlaşıldığı gibi anlamsal anlamından ayırmanız gerekiyorsa, fabrikaları kullanmak daha iyi olur.

Yapıcılar ve fabrikalar arasındaki fark, örneğin bir değişkene ve bir değişkene bir işaretçiye benzerdir. Bir dezavantaj olan başka bir dolaylama seviyesi daha vardır; ama başka bir esneklik seviyesi daha var, bu bir avantaj. Bu nedenle, bir seçim yaparken, fayda maliyetine karşı bu maliyeti yapmanız önerilir.


17
Yani, (TDD stili) işi yapmanın en basit yolu olarak inşaatçılar ile başlarsın. Ve sonra kod kokusu almaya başladıktan sonra fabrikalara refactor (hangi yapıcıyı arayacağınızı belirleyen tekrarlanan koşullu mantık gibi)?
AndyM

1
Çok önemli bir nokta. Fabrikaları ve kurucuları karşılaştıran bir kullanıcı çalışması, fabrikaların API kullanılabilirliğine zarar verdiğini gösteren son derece önemli sonuçlar bulmuştur: "kullanıcılar, bir nesneyi bir fabrikada kurucuya göre oluşturmak için çok daha fazla zamana (p = 0.005) ihtiyaç duymaktadır" [API Tasarımında Fabrika Deseni Bir Kullanılabilirlik Değerlendirmesi ].
mdeff

12

Bir fabrikayı yalnızca nesne oluşturma ile ekstra kontrole ihtiyaç duyduğunuzda, yapıcılarla yapılamayacak şekilde kullanın.

Örneğin fabrikaların önbellekleme olasılığı vardır.

Fabrikaları kullanmanın başka bir yolu, inşa etmek istediğiniz türü bilmediğiniz bir senaryodur. Genellikle, her eklentinin bir taban sınıfından türemesi veya bir tür arabirim uygulaması gereken eklenti fabrika senaryolarında bu tür bir kullanım görürsünüz. Fabrika, taban sınıftan türetilen veya arabirimi uygulayan sınıf örnekleri oluşturur.


11

"Etkili Java" dan bir alıntı, 2. baskı, Madde 1: Yapıcılar yerine statik fabrika yöntemlerini düşünün, s. 5:

" Statik bir fabrika yönteminin, Tasarım Desenlerindeki Fabrika Yöntemi ile aynı olmadığını unutmayın [Gamma95, s. 107]. Bu öğede açıklanan statik fabrika yönteminin Tasarım Desenlerinde doğrudan eşdeğeri yoktur."


10

Ek olarak "etkili java" (başka cevapta belirtildiği gibi), başka bir klasik kitap da öneriyor:

Aşırı yüklenmiş kurucular için statik fabrika yöntemlerini (bağımsız değişkenleri tanımlayan adlarla) tercih edin.

Örneğin. yazma

Complex complex = new Complex(23.0);

ama onun yerine yaz

Complex complex = Complex.fromRealNumber(23.0);

Kitap Complex(float), kullanıcıyı statik fabrika yöntemini çağırmaya zorlamak için yapıcıyı özel yapmayı önerecek kadar ileri gider .


2
Kitabın bu bölümünü okumak beni buraya getirdi
Purple Haze

1
@Bayrem: ben de, son zamanlarda yeniden okuyordum ve cevaplara eklemem gerektiğini düşündüm.
blue_note

1
İlgili bir, bazı yararlı olabilecek adlandırma kuralları tarafından hazırlanan java.time çerçevesinde isimlendirilmesine ilişkin from…, to…, parse…, with…, vb. Java.time sınıflarının değişmez olacak şekilde oluşturulduğunu unutmayın , ancak bu adlandırma kurallarından bazılarının değişken sınıflarla da takip edilmesi yararlı olabilir.
Basil Bourque

7

CAD / CAM uygulamasından somut bir örnek.

Bir yapıcı kullanılarak bir kesme yolu yapılır. Kesim yolunu tanımlayan bir dizi çizgi ve yaydır. Çizgiler ve yaylar farklı olabilir ve farklı koordinatlara sahip olsa da, bir liste yapıcıya iletilerek kolayca ele alınabilir.

Bir fabrika kullanılarak bir şekil yapılır. Çünkü bir şekil sınıfı varken, her şekil, ne tür bir şekle bağlı olarak farklı şekilde ayarlanır. Kullanıcı bir seçim yapana kadar hangi şekli başlatacağımızı bilmiyoruz.


5

Diyelim ki, bir sınıf üzerinde bir id değeri bekleyen bir yapıcı var. Yapıcı sınıfı veritabanından doldurmak için bu değeri kullanır.

Bu süreç kesinlikle bir kurucu dışında olmalıdır.

  1. Yapıcı veritabanına erişmemelidir.

  2. Bir kurucunun görevi ve nedeni, veri üyelerini başlatmak ve kurucuya iletilen değerleri kullanarak sınıf değişmezi oluşturmaktır.

  3. Her şey için daha iyi bir yaklaşım, statik fabrika yöntemini veya daha karmaşık durumlarda ayrı bir fabrika veya üretici sınıfını kullanmaktır.

Microsoft'tan bazı oluşturucu kılavuz satırları :

Yapıcıda çok az iş yapın. Yapıcılar, yapıcı parametrelerini yakalamak dışında çok fazla iş yapmamalıdır. Diğer işlemlerin maliyeti gerekene kadar ertelenmelidir.

Ve

İstenen işlemin anlambilimi doğrudan yeni bir örneğin yapısıyla eşleşmiyorsa, yapıcı yerine statik bir fabrika yöntemi kullanmayı düşünün.


2

Bazen bir nesne oluştururken bazı değerleri / koşulları kontrol etmeniz / hesaplamanız gerekir. Ve bir İstisna fırlatabilirse - constructro çok kötü bir yoldur. Yani böyle bir şey yapmanız gerekiyor:

var value = new Instance(1, 2).init()
public function init() {
    try {
        doSome()
    }
    catch (e) {
        soAnotherSome()
    }
}

Tüm ek hesaplamaların init () olduğu yerlerde. Ancak sadece geliştirici olarak bu init () hakkında bilgi sahibi olursunuz. Ve tabii ki, aylar sonra sadece unutuyorsun. Ancak bir fabrikanız varsa - bu init () öğesini doğrudan çağrıdan gizleyerek tek bir yöntemle yapmanız yeterlidir - bu yüzden sorun yok. Bu yaklaşımla, yaratılış ve hafıza sızıntısı ile ilgili hiçbir sorun yoktur.

Birisi size önbelleklemeden bahsetti. Bu iyi. Ama aynı zamanda Fabrika yolu ile kullanmak güzel Flyweight desen hakkında hatırlamak zorunda.

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.