Yapıcı işlevi ve Fabrika işlevleri


150

Birisi bir yapıcı işlevi ile Javascript'te bir fabrika işlevi arasındaki farkı netleştirebilir.

Biri diğeri yerine ne zaman kullanılır?

Yanıtlar:


149

Temel fark, bir yapıcı işlevinin newanahtar kelimeyle kullanılmasıdır (bu, JavaScript'in otomatik olarak yeni bir nesne oluşturmasına this, işlev içinde o nesneye ayarlanmasına ve nesneyi döndürmesine neden olur):

var objFromConstructor = new ConstructorFunction();

Fabrika fonksiyonuna "normal" fonksiyon denir:

var objFromFactory = factoryFunction();

Ancak bir "fabrika" olarak kabul edilebilmesi için, bir nesnenin yeni bir örneğini döndürmesi gerekir: eğer bir boolean veya başka bir şey döndürdüyse buna "fabrika" işlevi demezdiniz. Bu, otomatik olarak olduğu gibi olmaz new, ancak bazı durumlarda daha fazla esneklik sağlar.

Gerçekten basit bir örnekte, yukarıda atıfta bulunulan işlevler şöyle görünebilir:

function ConstructorFunction() {
   this.someProp1 = "1";
   this.someProp2 = "2";
}
ConstructorFunction.prototype.someMethod = function() { /* whatever */ };

function factoryFunction() {
   var obj = {
      someProp1 : "1",
      someProp2 : "2",
      someMethod: function() { /* whatever */ }
   };
   // other code to manipulate obj in some way here
   return obj;
}

Tabii ki fabrika fonksiyonlarını bu basit örnekten çok daha karmaşık hale getirebilirsiniz.

Fabrika fonksiyonlarının bir avantajı, döndürülecek nesnenin bir parametreye bağlı olarak birkaç farklı tipte olabilmesidir.


14
"(DÜZENLE: ve bu bir sorun olabilir, çünkü yeni olmadan fonksiyon hala çalışır, ancak beklendiği gibi olmaz)." Bu, yalnızca "new" ile bir fabrika işlevini çağırmaya çalışırsanız veya örneğe atamak için "this" anahtar sözcüğünü kullanmaya çalışırsanız bir sorundur. Aksi takdirde, yeni, keyfi bir nesne oluşturup geri döndürürsünüz. Sorun değil, işleri yapmanın daha farklı, daha esnek bir yolu, daha az kaynatma plakasıyla ve API'ya örnekleme ayrıntılarını sızdırmadan.
Eric Elliott

6
Her iki durum için de örneklerin (yapıcı işlevi ve fabrika işlevi) tutarlı olması gerektiğini belirtmek istedim. Fabrika işlevine örnek, fabrika someMethodtarafından döndürülen nesneler için geçerli değildir ve burası biraz sisli hale gelir. Fabrika işlevinin içinde, biri yaparsa var obj = { ... , someMethod: function() {}, ... }, geri gönderilen her nesnenin farklı bir kopyasını someMethodistemeyeceğimiz farklı bir kopyaya götürür . Kullanarak yani nereye newve prototypefabrika işlevi yardımcı olacağını içeride.
Bharat Khatri

3
Daha önce de belirttiğiniz gibi, bazı insanlar fabrika işlevlerini kullanmaya çalışırlar çünkü sadece newyapıcı işleviyle kullanmayı unuttukları hataları bırakmak istemezler ; Birinin nasıl yapıcıları fabrika fonksiyonları örneği ile değiştirmenin gerekli olabileceğini düşündüm ve buradaki örneklerde tutarlılığın gerekli olduğunu düşündüm. Her neyse, cevap yeterince bilgilendirici. Bu sadece yükseltmek istediğim bir noktaydı, herhangi bir şekilde cevabın kalitesini düşürdüğümden değil.
Bharat Khatri

4
Benim için Fabrika fonksiyonlarının en büyük avantajı, bazı uygulamalarda yararlı olabilecek daha iyi kapsülleme ve veri gizleme elde etmenizdir . Her örnek özelliği ve yöntemleri genel ve kullanıcılar tarafından kolayca değiştirilebilir yapma konusunda bir sorun varsa, o zaman bazı insanlar gibi "yeni" anahtar kelime sevmiyorum sürece Yapıcı işlevi daha uygun olduğunu düşünüyorum.
devius

1
@Federico - fabrika yöntemlerinin yalnızca düz bir nesne döndürmesi gerekmez. newDahili olarak kullanabilir veya Object.create()belirli bir prototipi olan bir nesne oluşturmak için kullanabilirler .
nnnnnn

110

Yapıcı kullanmanın faydaları

  • Çoğu kitap size yapıcıları ve new

  • this yeni nesneyi ifade eder

  • Bazı insanlar var myFoo = new Foo();okumayı sever .

Dezavantajları

  • Gerçekleştirme ayrıntıları, arayan API'ye ( newgereksinim yoluyla ) sızdırılır , böylece tüm arayanlar yapıcı uygulamasına sıkıca bağlanır. Fabrikanın ek esnekliğine ihtiyacınız varsa, tüm arayanları yeniden düzenlemeniz gerekir (kuşkusuz kural yerine istisnai durum).

  • Unutmak newyaygın bir hatadır, kurucunun doğru bir şekilde çağrıldığından emin olmak için bir kazan plakası eklemeyi kuvvetle düşünmelisiniz ( if (!(this instanceof Foo)) { return new Foo() }). EDIT: ES6 (ES2015) beri newbir classyapıcı ile unutamazsınız , ya da yapıcı bir hata atar.

  • instanceofÇeki yaparsanız new, gerekli olup olmadığı konusunda belirsizlik bırakır . Bence öyle olmamalı. newGereksinimi etkili bir şekilde kısa devre yaptınız , yani 1 numaralı dezavantajı silebilirsiniz. Ancak daha sonra , ek kazan plakası, büyük harf ve daha az esnek bağlam ile isim dışında bir fabrika fonksiyonuna sahipsinizthis .

Yapıcılar Açık / Kapalı Prensibini ihlal ediyor

Ama asıl endişem, açık / kapalı prensibini ihlal etmesi. Bir kurucuyu dışa aktarmaya başlıyorsunuz, kullanıcılar kurucuyu kullanmaya başlıyor, daha sonra bunun yerine bir fabrikanın esnekliğine ihtiyacınız olduğunu fark ettiğiniz yolda (örneğin, uygulamayı nesne havuzlarını kullanacak şekilde değiştirmek veya yürütme bağlamlarında örneklemek için veya prototypal OO kullanarak daha fazla kalıtım esnekliğine sahip).

Yine de sıkışıp kaldın. Yapıcıyı çağıran tüm kodu bozmadan değişikliği yapamazsınız new. Örneğin, performans kazanımları için nesne havuzları kullanmaya geçemezsiniz.

Ayrıca, yapıcıları kullanmak size instanceofyürütme bağlamlarında çalışmayan ve yapıcı prototipiniz değiştirilirse çalışmayan bir aldatıcılık verir . Ayrıca, kurucunuzdan dönmeye başlar thisve sonra kurucunuzda fabrika benzeri davranışı etkinleştirmek için yapmanız gereken rasgele bir nesneyi dışa aktarmaya geçerseniz de başarısız olur .

Fabrika kullanmanın faydaları

  • Daha az kod - kaynatma plakası gerekmez.

  • Herhangi bir rastgele nesneyi döndürebilir ve herhangi bir rastgele prototip kullanabilirsiniz - böylece aynı API'yi uygulayan çeşitli nesne türleri oluşturma konusunda size daha fazla esneklik sağlar. Örneğin, hem HTML5 hem de flash oynatıcı örnekleri oluşturabilen bir medya oynatıcı veya DOM olayları veya web soketi olayları yayabilecek bir olay kütüphanesi. Fabrikalar aynı zamanda yürütme bağlamında nesneleri başlatabilir, nesne havuzlarından yararlanabilir ve daha esnek prototip kalıtım modellerine izin verebilir.

  • Asla bir fabrikadan bir kurucuya dönüşmenize gerek kalmaz, bu yüzden yeniden düzenleme asla sorun olmaz.

  • Kullanmayla ilgili bir belirsizlik yok new. Yapma. ( thisKötü davranacaktır, bir sonraki noktaya bakınız).

  • thisnormalde olduğu gibi davranır - böylece üst nesneye erişmek için kullanabilirsiniz (örneğin, içeride player.create(), tıpkı diğer herhangi bir yöntem çağrısında olduğu gibi thisanlamına gelir) ve beklendiği gibi yeniden playeratayabilirsiniz . Prototipleri üst nesnede depolarsanız, işlevselliği dinamik olarak değiştirmenin ve nesne somutlaştırmanız için çok esnek bir polimorfizme olanak sağlamanın harika bir yolu olabilir.callapplythis

  • Büyük harf kullanımı olup olmamasına dair belirsizlik yok. Yapma. Tüy bırakmayan araçlar şikayet edecek ve sonra kullanmaya çalışacaksınız newve daha sonra yukarıda açıklanan avantajı geri alacaksınız.

  • Bazı insanlar okumayı var myFoo = foo();veya var myFoo = foo.create();okumayı sever .

Dezavantajları

  • newbeklendiği gibi davranmaz (yukarıya bakınız). Çözüm: kullanma.

  • thisYeni bir nesne ifade etmez (yapıcı nokta gösterimde veya köşeli ayraç gösterimde, örneğin foo.bar () çağrıldığında, bunun yerine, - thisatıfta fooyararlarını görmek - sadece her JavaScript yöntemi gibi -).


2
Hangi anlamda inşaatçıların arayanları uygulamalarına sıkı sıkıya bağladığını kastediyorsunuz? Yapıcı argümanları söz konusu olduğunda, bunların kullanılması ve içindeki uygun kurucuyu çağırması için bunların fabrika işlevine bile geçirilmesi gerekecektir.
Bharat Khatri

4
Açık / Kapalı ihlali ile ilgili olarak: bu sadece bağımlılık enjeksiyonuyla ilgili değil mi? A'nın B'ye ihtiyacı varsa, A'nın yeni B () veya A'nın BFactory.create () çağrısı olup olmadığı, her ikisinin de birleşimi sağlar. Öte yandan, kompozisyon kökünde A'nın B örneğini verirseniz, A'nın B'nin nasıl başlatıldığı hakkında hiçbir şey bilmesine gerek yoktur. Hem inşaatçıların hem de fabrikaların kullanımları olduğunu hissediyorum; yapıcılar basit örnekleme, daha karmaşık örnekleme fabrikaları içindir. Ancak her iki durumda da, bağımlılıklarınızı enjekte etmek akıllıca olacaktır.
Stefan Billiet

1
DI, durumu enjekte etmek için iyidir: yapılandırma, etki alanı nesneleri vb. Diğer her şey için aşırı dolu.
Eric Elliott

1
Yaygara, ihtiyaç newduymanın açık / kapalı prensibini ihlal etmesidir. Bu yorumların izin verdiğinden çok daha büyük bir tartışma için medium.com/javascript-scene/… adresine bakın .
Eric Elliott

3
Herhangi bir işlev JavaScript'te yeni bir nesne döndürebildiğinden ve bunların çoğu newanahtar kelime olmadan bunu yapabildiğinden , newanahtar kelimenin aslında ek okunabilirlik sağladığına inanmıyorum . IMO, arayanların daha fazla yazmasını sağlamak için çemberlerden atlamak aptalca görünüyor.
Eric Elliott

39

Bir kurucu, çağırdığınız sınıfın bir örneğini döndürür. Fabrika işlevi her şeyi döndürebilir. Rasgele değerler döndürmeniz gerektiğinde veya bir sınıfın büyük bir kurulum işlemi olduğunda fabrika işlevini kullanırsınız.


6

Yapıcı işlevi örneği

function User(name) {
  this.name = name;
  this.isAdmin = false;
}

let user = new User("Jack");
  • newprototiplenmiş bir nesne oluşturur ve yaratılan nesne ile değeri olarak User.prototypeçağırır .Userthis

  • new işlenen için bir argüman ifadesini isteğe bağlı olarak ele alır:

         let user = new User;

    hiçbir argüman olmadan newçağrı neden olur User.

  • newyapıcı bunun yerine döndürülen bir nesne değeri döndürmediği sürece oluşturduğu nesneyi döndürür . Bu, çoğunlukla göz ardı edilebilecek bir kenar durumudur.

Lehte ve aleyhte olanlar

Yapıcı işlevleri tarafından oluşturulan nesneler, özellikleri yapıcı özelliğinden devralır ve yapıcı işlevindeki işleci prototypekullanarak true değerini döndürür instanceOf.

Yapıcıyı prototypezaten kullandıktan sonra yapıcı özelliğinin değerini dinamik olarak değiştirirseniz, yukarıdaki davranışlar başarısız olabilir . Bunu yapmak nadirdir ve yapıcı classanahtar kelime kullanılarak oluşturulmuşsa değiştirilemez .

Yapıcı işlevleri extendsanahtar sözcük kullanılarak genişletilebilir .

Yapıcı işlevleri nullhata değeri olarak geri dönemez . Nesne veri türü olmadığından tarafından yoksayılır new.

Fabrika işlevi örneği

function User(name, age) {
  return {
    name,
    age,
  }
};

let user = User("Tom", 23);

Burada fabrika fonksiyonu olmadan çağrılır new. İşlev, bağımsız değişkenleri ve döndürdüğü nesne türünde doğrudan veya dolaylı kullanımdan tamamen sorumludur. Bu örnekte, argümanlardan ayarlanmış bazı özelliklere sahip basit bir [Nesne nesnesi] döndürür.

Lehte ve aleyhte olanlar

Kolayca arayandan nesne oluşturma uygulama karmaşıklığını gizler. Bu özellikle bir tarayıcıdaki yerel kod işlevleri için kullanışlıdır.

Fabrika işlevinin her zaman aynı türdeki nesneleri döndürmesi gerekmez ve hatta nullbir hata göstergesi olarak geri dönebilir .

Basit durumlarda, fabrika fonksiyonları yapı ve anlam bakımından basit olabilir.

İade nesneleri genellikle fabrika işlevin devralan yok prototypemülkiyet ve karşılığında falsegelen instanceOf factoryFunction.

Fabrika işlevi, extendsanahtar sözcük kullanılarak güvenli bir şekilde genişletilemez, çünkü genişletilmiş nesneler fabrika işlevi tarafından kullanılan yapıcıdan prototypeziyade fabrika işlevleri özelliğinden miras alınır prototype.


1
Bu aynı soruya bu soruya cevap olarak gönderilen geç bir cevaptır ,
traktor53

sadece "null" değil, "new" de yapıcı işlevi tarafından döndürülen herhangi bir ilkel veri tipini göz ardı edecektir.
Vishal

2

Fabrikalar "her zaman" daha iyidir. Nesneye yönelik dilleri kullanırken

  1. sözleşmeye karar verme (yöntemler ve ne yapacakları)
  2. Bu yöntemleri ortaya çıkaran arayüzler oluşturun (javascript'te arayüzlere sahip değilsiniz, böylece uygulamayı kontrol etmenin bir yolunu bulmanız gerekir)
  3. Gereken her arabirimin bir uygulamasını döndüren bir fabrika oluşturun.

Uygulamalar (yeni ile oluşturulan gerçek nesneler) fabrika kullanıcısına / tüketicisine maruz kalmaz. Bu, fabrika geliştiricisinin sözleşmeyi ihlal etmediği sürece yeni uygulamaları genişletebileceği ve oluşturabileceği anlamına gelir ... ve fabrika tüketicisinin kodlarını değiştirmek zorunda kalmadan yeni API'den faydalanmasını sağlar ... eğer yeni kullandılar ve "yeni" bir uygulama gelirse, "yeni" uygulamayı kullanmak için "yeni" kullanan her satırı değiştirmek zorunda kalırlar ... fabrika ile kodları değişmez ...

Fabrikalar - her şeyden daha iyi - bahar çerçevesi tamamen bu fikir üzerine inşa edilmiştir.


Fabrika, her satırı değiştirmek zorunda kalma problemini nasıl çözüyor?
Kod Adı Jack

0

Fabrikalar bir soyutlama katmanıdır ve tüm soyutlamalar gibi karmaşıklığa da sahiptirler. Fabrika tabanlı bir API ile karşılaştığınızda, belirli bir API için fabrikanın ne olduğunu bulmak API tüketicisi için zor olabilir. Yapıcılar ile keşfedilebilirlik önemsizdir.

Ctorlar ve fabrikalar arasında karar verirken, karmaşıklığın fayda tarafından haklı olup olmadığına karar vermeniz gerekir.

Javascript kurucularının bundan başka bir şey döndürerek veya tanımsız olarak rastgele fabrikalar olabileceğini belirtmek gerekir. Böylece js'de her iki dünyanın en iyisini elde edebilirsiniz - keşfedilebilir API ve nesne havuzlama / önbellekleme.


5
JavaScript'te, JS'deki herhangi bir işlev yeni bir nesne döndürebileceğinden, kurucu kullanma maliyeti fabrika kullanma maliyetinden daha yüksektir. Yapıcılar şu şekilde karmaşıklık ekler: Gereksinim new, Değişme davranışı this, Dönüş değerini değiştirme, Bir prototip ref bağlanması, Etkinleştirme instanceof(bu amaç için kullanılır ve kullanılmamalıdır). Görünüşte, bunların hepsi "özellikler" dir. Uygulamada, kod kalitenize zarar verirler.
Eric Elliott

0

Farklılıklar için Eric Elliott çok iyi açıkladı,

Ancak ikinci soru için:

Biri diğeri yerine ne zaman kullanılır?

Nesneye yönelik arka plandan geliyorsanız, Oluşturucu işlevi size daha doğal görünür. bu şekilde newanahtar kelime kullanmayı unutmamalısınız .

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.