Statik fabrika vs fabrika bir singleton olarak


24

Kodlarımın bazılarında buna benzer statik bir fabrika var:

public class SomeFactory {

    // Static class
    private SomeFactory() {...}

    public static Foo createFoo() {...}

    public static Foo createFooerFoo() {...}
}

Kod incelemesi sırasında bunun bir singleton olması ve enjekte edilmesi önerildi. Yani, bu gibi görünmeli:

public class SomeFactory {

    public SomeFactory() {}

    public Foo createFoo() {...}

    public Foo createFooerFoo() {...}
}

Vurgulanması gereken birkaç şey:

  • Her iki fabrika da vatansız.
  • Yöntemler arasındaki tek fark, kapsamlarıdır (örnek vs statik). Uygulamalar aynıdır.
  • Foo, arayüzü olmayan bir fasulyedir.

Statik olma konusunda sahip olduğum argümanlar şuydu:

  • Sınıf vatansız, bu nedenle somutlaştırılması gerekmiyor
  • Statik bir yöntem çağırabilmek bir fabrikayı canlandırmaktan daha doğal görünüyor

Tekton olarak fabrika argümanları:

  • Her şeyi enjekte etmek iyi
  • Fabrikanın vatansızlığına rağmen, enjeksiyonla test yapmak daha kolaydır (takması kolaydır)
  • Tüketici test edilirken alay edilmeli

Singleton yaklaşımı ile ilgili ciddi sorunlarım var çünkü hiçbir yöntemin statik olmaması gerektiğini gösteriyor. Aynı zamanda StringUtilsaptalca görünen, sarılmış ve enjekte edilmiş gibi yardımcı programların da olduğu anlaşılıyor. Son olarak, doğru görünmeyen bir noktada fabrika ile alay etmeye ihtiyacım olacağı anlamına geliyor. Fabrika ile ne zaman alay etmem gerektiğini düşünemiyorum.

Topluluk ne düşünüyor? Singleton yaklaşımını sevmiyorum, ancak buna karşı oldukça güçlü bir tartışmaya sahip görünmüyorum.


2
Fabrika tarafından kullanılmakta olan bir şey . Bu şeyi izolasyonda test etmek için, fabrikanızın alayını yapmanız gerekir. Fabrikanızla dalga geçmezseniz, bir hata olması gerekmediğinde hatalı ünite testine neden olabilir.
Mike,

Peki, genel olarak statik yararlara mı yoksa sadece statik fabrikalara karşı mı savunuyorsunuz?
bstempi

1
Genel olarak onlara karşı savunuculuk yapıyorum. Örneğin C # 'da DateTimeve Filesınıfların aynı nedenlerle test edilmesi çok zordur. Örneğin yapıcıdaki Createdtarihi ayarlayan bir sınıfınız varsa, DateTime.Now5 dakika arayla oluşturulan bu iki nesneden oluşan bir birim testi oluşturmaya ne dersiniz? Peki ya yıllar sonra? Bunu gerçekten yapamazsınız (çok fazla iş olmadan).
Mike,

2
Singleton fabrikanızın bir privatekurucusu ve bir getInstance()yöntemi yok mu? Üzgünüz, çözülemez nit-toplayıcı!
TMN

1
@Mike - Agreed - Genellikle DateTime.Now değerini döndüren bir DateTimeProvider sınıfı kullandım. Daha sonra, testlerinizde önceden belirlenmiş bir süre veren bir taksiyle değiştirebilirsiniz. Tüm inşaatçılara ulaştırmak ekstra bir iş - en büyük baş ağrısı iş arkadaşlarının% 100 satın almadığı ve DateTime'a yapılan açık referansları kaymadığı zamandır. Tembellikten sonra ... :)
Julia Hayward

Yanıtlar:


15

Neden fabrikanızı yarattığı nesne türünden ayırdınız?

public class Foo {

    // Private constructor
    private Foo(...) {...}

    public static Foo of(...) { return new Foo(...); }
}

Joshua Bloch'un "Etkin Java" adlı kitabının 5. sayfasındaki 1. Maddesi olarak tanımladığı şey budur. Java, ekstra sınıflar ve singletonlar eklemeden yeterince ayrıntılı ve ne olursa olsun. Ayrı bir fabrika sınıfının mantıklı olduğu bazı durumlar vardır, ancak bunlar birbirinden çok uzaktır.

Yapımcıların aksine, Joshua Bloch'un 1. Maddesini anlatmak için statik fabrika yöntemleri şunları yapabilir:

  • Belirli nesne oluşturma türlerini tanımlayabilecek adlara sahip (Bloch, örnek olarak probablePrime (int, int, Random) kullanır)
  • Mevcut bir nesneyi döndür (düşün: uçucu ağırlık)
  • Orijinal sınıfın alt türünü veya uyguladığı arayüzü döndür
  • Ayrıntıları azaltın (tür parametrelerini belirtirken - örneğin Bloch'a bakın)

Dezavantajları:

  • Genel veya korumalı bir kurucu olmayan sınıflar alt sınıflanamaz (ancak korumalı yapıcıları ve / veya Öğe 16: miras alma yerine kompozisyonu tercih edebilirsiniz)
  • Statik fabrika yöntemleri, diğer statik yöntemlerden farklı değildir (() veya valueOf () gibi standart bir ad kullanın)

Gerçekten, Bloch'un kitabını kod eleştirmeninize götürmeli ve ikinizin Bloch'un tarzını kabul edip edemediğini öğrenmelisiniz.


Ben de bunu düşündüm ve sanırım hoşuma gitti. Onları neden ilk önce ayırdığımı hatırlamıyorum.
bstempi

Genel olarak konuşursak, Bloch'un tarzına katılıyoruz. Sonunda, fabrika yöntemlerimizi başlattıkları sınıfa taşıyacağız. Çözümümüzle sizinkiniz arasındaki tek fark, kurucunun halka açık kalacağıdır, böylece insanlar doğrudan sınıfı doğrudan başlatabilirler. Cevabınız için teşekkürler!
bstempi

Ama ... bu korkunç. Muhtemelen IFoo'yu uygulayan ve geçmesini sağlayan bir sınıf istiyorsun. Bu kalıbı kullanmak artık mümkün değil; Örnek almak için IFoo'yu Foo olarak adlandırmanız gerekir. Bir IFoo create () içeren IFooFactory'niz varsa, müşteri kodunun hangi beton Foo'nun IFoo olarak seçildiğinin uygulama detayını bilmesi gerekmez.
Wilbert

@Wilbert public static IFoo of(...) { return new Foo(...); } Sorun nedir? Bloch, bu tasarımın avantajlarından biri olarak endişenizi gideren listeleri (yukarıdaki ikinci madde işareti). Yorumunu anlamamalıyım.
GlenPeterson,

Bir örnek oluşturmak için bu tasarım beklemektedir: Foo.of (...). Ancak, Foo varlığı sadece IFoo bilinmesi gerekir, maruz kalmamalıdır (Foo ama olarak bir IFoo somut impl). Betonarme üzerinde statik bir fabrika yönteminin bulunması bunu kırar ve müşteri kodu ile beton uygulaması arasında bir eşleşmeye yol açar.
Wilbert 21:13

5

Kafamın hemen yukarısında, işte Java'da statik ile ilgili bazı problemler:

  • statik yöntemler OOP "kuralları" ile oynamaz. Bir sınıfı belirli statik yöntemleri (bildiğim kadarıyla) uygulamaya zorlayamazsınız. Dolayısıyla, aynı imzalarla aynı statik yöntem grubunu uygulayan birden fazla sınıfınız olamaz. Yani yaptığın her neyse birine sıkışıp kaldın. Statik bir sınıfı da alt sınıflandıramazsınız. Yani hiçbir polimorfizm, vb.

  • Metotlar nesne olmadığından, statik metotlar geçilemezler ve kullanılamazlar (tonlarca kodlama kodlaması yapmadan), bunlara zor bağlantılı kodlar haricinde - müşteri kodunu yüksek yapan birleştirilmiş. (Adil olmak gerekirse, bu sadece statiklerin hatası değildir).

  • Statik alanları küresellere oldukça benzer


Bahsettiniz:

Singleton yaklaşımı ile ilgili ciddi sorunlarım var çünkü hiçbir yöntemin statik olmaması gerektiğini gösteriyor. Ayrıca, StringUtils gibi uygulamaların aptalca göründüğü gibi sarılıp enjekte edilmesi gerektiğini gösteriyor. Son olarak, doğru görünmeyen bir noktada fabrika ile alay etmeye ihtiyacım olacağı anlamına geliyor. Fabrika ile ne zaman alay etmem gerektiğini düşünemiyorum.

Bu size sormamı meraklandırıyor:

  • Sizce, statik yöntemlerin avantajları nelerdir?
  • Bunun StringUtilsdoğru bir şekilde tasarlanıp uygulandığına inanıyor musunuz?
  • neden asla fabrika ile alay etmek zorunda kalmayacağını düşünüyorsun?

Hey, cevap için teşekkürler! İstediğiniz sırada: (1) Statik yöntemlerin avantajları, sözdizimsel şeker ve gereksiz bir durumun olmamasıdır. Statik içe aktarımı olan bir kodu okumak ve Foo f = createFoo();adlandırılmış bir örneğe sahip olmak yerine bir şey söylemek güzel . Örneğin kendisi hiçbir fayda sağlamaz. (2) Sanırım öyle. Bu yöntemlerin birçoğunun Stringsınıf içinde olmadığı gerçeğinin üstesinden gelmek için tasarlanmıştır . Bir şeyi Stringseçenek olarak olduğu kadar temel bir şeyle değiştirmek , yararlar yolunda gitmektir. (devam ediyor)
bstempi

Kullanımı kolaydır ve herhangi bir durumda ağlamanızı gerektirmez. Doğal hissediyorlar. (3) Çünkü fabrikam içindeki fabrika yöntemlerine çok benziyor Integer. Çok basitler, çok az mantıkları var ve sadece kolaylık sağlamak için oradalar (örneğin, onlara sahip olmak için başka bir OO nedeni yok). Argümanımın ana kısmı, hiçbir devlete güvenmedikleridir - testleri sırasında onları izole etmek için hiçbir neden yoktur. StringUtilsTest sırasında insanlar alay etmiyor - neden bu basit kolaylık fabrikasıyla alay etmeleri gerekiyor?
bstempi

3
Alay etmiyorlar StringUtilsçünkü oradaki yöntemlerin herhangi bir yan etkisi olmadığına dair makul bir güvence var.
Robert Harvey,

@RobertHarvey Sanırım fabrikam hakkında aynı iddiada bulunuyorum - hiçbir şeyi değiştirmiyor. StringUtilsGirişleri değiştirmeden bazı sonuçlar verir gibi fabrikam da hiçbir şeyi değiştirmiyor.
bstempi

@bstempi Orada bir miktar Sokratik yöntem kullanacaktım. Sanırım onu ​​emerim.

0

İnşa ettiğiniz Uygulamanın oldukça karmaşık olması gerektiğini ya da yaşam döngüsünün nispeten erken olduğunu düşünüyorum. Statik durumunuzla ilgili sorunların neresinde sorun yaşamayacağınızı düşünebildiğim diğer senaryo, kodunuzun Fabrikanız hakkında bilinen diğer bölümlerini bir veya iki yerle sınırlamayı başardı.

Uygulamanızın geliştiğini ve şimdi bazı durumlarda bir FooBar(Foo alt sınıfı) üretmeniz gerektiğini hayal edin . Şimdi kodunuzda sizin hakkınızda bilen tüm yerlerden geçmeniz ve veya SomeFactoryüzerine bakan someConditionve arayan bir tür mantık yazmanız gerekiyor . Aslında, örnek koduna bakmak, bundan daha kötü. Sadece farklı Foos oluşturma metodundan sonra bir metot eklemeyi planlıyorsunuz ve tüm müşteri kodunuz hangi Foo'nun yapılacağına karar vermeden önce bir durumu kontrol etmek zorunda. Bu, tüm müşterilerin Fabrikanızın nasıl çalıştığı hakkında kesin bir bilgiye sahip olmaları ve yeni bir Foo gerektiğinde (veya kaldırıldıklarında hepsinin değişmesi gerektiği anlamına gelir).SomeFactorySomeOtherFactory

Şimdi, sadece IFooFactorybunu yapan bir yarattığınızı hayal edin IFoo. Şimdi, farklı bir alt sınıf veya hatta tamamen farklı bir uygulama üretmek çocuk oyuncağıdır - sadece doğru IFooFactory'yi beslersiniz, daha sonra zincir yükselir ve müşteri bunu elde ettiğinde, doğru Foo'yu çağırır gimmeAFoo()ve doğru Fabrikayı alır. .

Bunun üretim kodunda nasıl oynandığını merak ediyor olabilirsiniz. Üzerinde çalışacağım kod tabanından bir örnek vermek gerekirse, bir yıldan fazla süren projelerden sonra seviyeye inmeye başlar, Soru veri nesneleri oluşturmak için kullanılan bir sürü Fabrikam vardır ( Sunum Modelleri gibidirler) ). Bir var QuestionFactoryMaptemelde kullanılacak olan soru fabrika arayanlar için bir karma olduğunu. Bu yüzden farklı sorular soran bir Uygulama oluşturmak için haritaya farklı Fabrikalar yerleştirdim.

Sorular, Talimatlarda veya Uygulamalarda sunulabilir (her ikisi de uygulanır IContentBlock), böylece her biri InstructionsFactoryveya ExerciseFactoryQuestionFactoryMap'in bir kopyasını alır. Daha sonra, çeşitli Talimatlar ve Egzersiz fabrikaları, ContentBlockBuilderbir daha ne zaman kullanılacağı bir hash tutan tekrar verilir. Ve sonra XML yüklendiğinde, ContentBlockBuilder Egzersiz veya Talimatlar Fabrikasını karma dışına çağırır ve istediğini sorar createContent()ve daha sonra Fabrika, soruların oluşturulmasını neye dayanarak kendi iç QuestionFactoryMap’ten çıkardığı konusuna delegasyonu yapar. her XML düğümünde, hash ile karşılaştırılır. Ne yazık ki , bu şey BaseQuestionbirIQuestionama bu, tasarım konusunda dikkatli olsanız bile, sizi rahatsız edecek hatalar yapabileceğinizi gösteriyor.

Sizin bağırsak bu statik size zarar edeceğini bildiren ve kesinlikle orada olduğunu bol orada İçgüdülerine desteklemek için literatürde. Yalnızca Ünite Testleriniz için kullandığınız Bağımlılık Enjeksiyonuna sahip olduğunuzda asla kaybedemezsiniz (iyi bir kod oluşturursanız, kendisinden daha fazlasını kullanırken kendinizi bulamayacağınıza inanmayacağınıza inanıyorum), ancak statik yeteneklerinizi tamamen tahrip edebilir Kodunuzu hızlı ve kolay bir şekilde değiştirmek için. Peki neden oraya gitmek?


Bir an için Integersınıftaki fabrika yöntemlerini düşünün - bir String'den dönüştürme gibi basit şeyler. İşte Foobu. Benim Foouzatılmamalı (bunu belirtmemem benim suçum), bu yüzden polimorfik sorunlarınız yok. Polimorfik fabrikalara sahip olmanın değerini anladım ve geçmişte onları kullandım ... Burada uygun olduklarını sanmıyorum. IntegerStatik fabrikalar üzerinden yapılan basit işlemleri yapmak için bir fabrikayı başlatmanız gerekip gerekmediğini hayal edebiliyor musunuz?
bstempi

Ayrıca, uygulama hakkında varsayımlar oldukça kapalı. Uygulama oldukça katılıyor. FooAncak bu , çoğu sınıftan daha basittir. Bu sadece basit bir POJO.
bstempi

Foo'nun uzatılması gerekiyorsa, bu hak Fabrika’nın bir örneğinin olması için bir neden vardır - fabrikanın if (this) then {makeThisFoo()} else {makeThatFoo()}kodunu çağırmak yerine size doğru Alt Sınıfı vermesi için doğru örneği sağlarsınız . Kronik olarak kötü mimarisi olan insanlar hakkında fark ettiğim bir şey, kronik acı çeken insanlar gibileri. Aslında acı çekmemenin nasıl bir his olduğunu bilmiyorlar, bu yüzden içinde bulundukları acı hiç de fena görünmüyor.
Amy Blankenship

Ayrıca, statik yöntemlerle ilgili problemler hakkındaki bu bağlantıya göz atmak isteyebilirsiniz (hatta Matematik!)
Amy Blankenship

Soruyu güncelledim. Siz ve bir kişi daha uzatmaktan bahsettiniz Foo. Haklı olarak - Sonunu asla ilan etmedim. Foouzatılması amaçlanmayan bir fasulyektir.
bstempi
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.