Yapıcılar olmadan yaşayabilir miyiz?


25

Diyelim ki bazı nedenlerle tüm nesneler bu şekilde yaratıldı $ obj = CLASS :: getInstance (). Daha sonra ayarlayıcıları kullanarak bağımlılıkları enjekte ediyoruz ve $ obj-> initInstance () kullanarak başlatma işlemini başlatıyoruz; Yapıcıları hiç kullanmayacaksak, çözülemeyen gerçek sorunlar veya durumlar var mı?

Bu şekilde nesne yaratmanın nedeni Ps, getInstance () içindeki sınıfı bazı kurallara göre değiştirebilmemizdir.

PHP'de çalışıyorum, eğer öyleyse


hangi programlama dili
tatarcık

2
PHP (neden önemli?)
Axel Foley

8
Fabrika modelini kullanarak yapmak istediğiniz şeyin başarılabileceği anlaşılıyor.
Superm

1
Tıpkı bir not olarak: Bahsedilen fabrika denetleyicisi @superM uygulamasının bir yolu ise, İnversiyon Kontrolü (Bağımlılık Enjeksiyonu) başka bir yöntemdir.
Joachim Sauer,

Javascript’in nesnelerle yaptığı kabaca bu değil mi?
Thijser

Yanıtlar:


44

Bunun tasarım alanınızı ciddi şekilde engellediğini söyleyebilirim.

Yapıcılar, iletilen parametrelerin başlatılması ve doğrulanması için harika bir yerdir. Bunları artık bunun için kullanamazsanız, daha sonra başlatma, durum işleme (veya "kırılmış" nesnelerin inkar kurucusunu reddetme) çok daha zor ve kısmen imkansız hale gelir.

Örneğin, eğer her Foonesne bir şeye ihtiyaç duyarsa , null Frobnicatordeğilse, kurucusunu kontrol edebilir Frobnicator. Yapıcıyı alırsanız, kontrol etmesi zorlaşır. Kullanılacağı her noktayı kontrol ediyor musunuz? Bir init()yöntemde (yapıcı yöntemini etkin bir şekilde dışsallaştırma)? Asla kontrol etme ve en iyisini umalım mı?

Eğer iken olabilir muhtemelen hala her şeyi uygulamak (bütün sonra hala tam turing ediyoruz), bazı şeyler çok daha zor yapmak olacaktır.

Şahsen Bağımlılık Enjeksiyonu / Kontrolün Tersine çevrilmesi konusunu öneriyorum . Bu teknikler aynı zamanda somut uygulama sınıflarının değiştirilmesine izin verir, ancak kurucuların yazmasını / kullanılmasını engellemez.


Ne demek "veya" kırılmış "nesnelerin" inkar eden inşasını reddetmekle?
Geek

2
@Geek: bir kurucu argümanını inceleyebilir ve bunun çalışan bir nesne ile sonuçlanıp sonuçlanmayacağına karar verebilir (örneğin, eğer nesneniz bir HttpClientzamana ihtiyaç duyuyorsa , o parametrenin boş olup olmadığını kontrol eder). Ve eğer bu kısıtlamalar yerine getirilmezse, o zaman bir istisna atabilir. Yapı ve küme değerleri yaklaşımıyla bu gerçekten mümkün değil.
Joachim Sauer

1
Bence OP sadece yapının dışsallaştırmasını tarif ediyordu init(), bu tamamen mümkün - bu sadece bakım yükünü arttırıyor.
Peter

26

Yapıcılara 2 avantaj:

Yapıcılar, nesnenin yapım adımlarının atomik olarak yapılmasına izin verir.

Bir kurucudan kaçınabilir ve her şey için belirleyiciler kullanabilirim, peki Joachim Sauer'ın önerdiği gibi zorunlu özellikler? Bir yapıcı ile, bir nesne , bu sınıfın geçersiz örnekleri olmadığından emin olmak için kendi yapım mantığına sahiptir .

FooAyarlanması gereken 3 özellik örneği olması durumunda, yapıcı 3 öğenin tamamına referans alabilir, bunları doğrulayabilir ve geçersizse istisna atabilir.

kapsülleme

Yalnızca ayarlayıcılara dayanarak, yük uygun şekilde oluşturmak için bir nesnenin tüketicisine aittir. Geçerli olan özelliklerin farklı kombinasyonları olabilir.

Örneğin, her bir Fooörnek bir örneğini ya ihtiyacı Barolarak özellik barveya bir örneği BarFinderolarak özelliği barFinder. İkisinden birini kullanabilir. Her geçerli parametre seti için bir oluşturucu oluşturabilir ve kuralları bu şekilde zorlayabilirsiniz.

Nesnelerin mantığı ve anlambilimi, nesnenin içinde yaşar. Bu iyi bir kapsülleme.


15

Evet, yapıcı olmadan yaşayabilirsiniz.

Tabii ki, birçok çoğaltılmış kazan plakası kodu ile bitebilirsiniz. Uygulamanız herhangi bir ölçekte ise, büyük bir kod kodu uygulamada tutarlı bir şekilde kullanılmadığında, sorunun kaynağını bulmaya çalışırken muhtemelen çok zaman harcayacaksınız.

Ama hayır, kesinlikle kendi yapıcılarınıza ihtiyaç duymazsınız. Tabii ki, kesinlikle sınıflara ve nesnelere de 'ihtiyaç duymazsınız'.

Şimdi, amacınız nesne oluşturma için bir çeşit fabrika deseni kullanmaksa, bu nesnelerinizi başlatırken yapıcıları kullanmak için özel değildir.


Kesinlikle. Kişi, başlangıçta, sınıflar ve nesneler olmadan yaşayabilir.
JensG

5
Herkes güçlü montajcıyı selamla!
Davor Ždralo

9

Yapıcıları kullanmanın avantajı, hiçbir zaman geçersiz bir nesneye sahip olmadığınızdan emin olmanızı kolaylaştırmasıdır.

Bir yapıcı size nesnenin tüm üye değişkenlerini geçerli bir duruma getirme fırsatı verir. Daha sonra, hiçbir mutator yönteminin nesneyi geçersiz bir duruma değiştiremediğinden emin olduğunuzda, sizi asla bir çok hatadan kurtaracak hiçbir zaman geçersiz bir nesneye sahip olmazsınız.

Ancak, geçersiz bir durumda yeni bir nesne oluşturulduğunda ve kullanılabileceği geçerli bir duruma koymak için bazı ayarlayıcıları çağırmanız gerekir; sınıftaki bir tüketicinin bu ayarlayıcıları çağırması veya yanlış çağırması riskini alırsınız ve geçersiz bir nesneyle bitirdiniz.

Çözüm, nesneleri yalnızca arayan kişiye döndürmeden önce geçerlilik için oluşturduğu her nesneyi kontrol eden bir fabrika yöntemiyle oluşturmak olabilir.


3

$ obj = SINIF :: getInstance (). Daha sonra ayarlayıcıları kullanarak bağımlılıkları enjekte ediyoruz ve $ obj-> initInstance () kullanarak başlatma işlemini başlatıyoruz;

Sanırım bunu olması gerekenden daha zorlaştırıyorsun. Sadece yapıcıya bağımlılıklar enjekte edebiliriz - ve eğer birçoğunuz varsa, sadece sözlük benzeri bir yapı kullanın, böylece ne kullanmak istediğinizi belirtin:

$obj = new CLASS(array(
    'Frobnicator' => (),
    'Foonicator' => (),
));

Ve kurucu içerisinde aşağıdaki gibi tutarlılık sağlayabilirsiniz:

if (!array_key_exists('Frobnicator', $args)) {
    throw new Exception('Frobnicator required');
}
if (!array_key_exists('Foonicator', $args)) {
    $args['Foonicator'] = new DefaultFoonicator();
}

$args daha sonra gerektiğinde özel üyeler ayarlamak için kullanılabilir.

Tamamen böyle bir kurucu $objiçerisinde yapıldığında, söz konusu sistemde olduğu gibi, var olan ancak başlatılmayan bir ara durum asla olmayacaktır . Bu tür ara durumlardan kaçınmak daha iyidir, çünkü nesnenin daima doğru kullanılacağını garanti edemezsiniz.


2

Aslında benzer şeyler hakkında düşünüyordum.

Sorduğum soru, "Yapıcı ne yapar ve bunu farklı şekilde yapmak mümkün mü?" Bu sonuçlara vardım:

  • Bazı özelliklerin başlatılmasını sağlar. Bunları parametre olarak kabul edip ayarlayarak. Ancak bu derleyici tarafından kolayca uygulanabilir. Alanları veya özellikleri yalnızca "zorunlu" olarak ekleyerek derleyici, her şeyin uygun şekilde ayarlanıp ayarlanmadığını kontrol eder. Örneği oluşturma çağrısı muhtemelen aynı olacaktı, herhangi bir kurucu yöntem olmazdı.

  • Özelliklerin geçerli olmasını sağlar. Bu kolayca assert koşulu ile elde edilebilir. Yine, özellikleri sadece doğru koşullarla açıklamalıydınız. Bazı diller zaten bunu yapıyor.

  • Bazı daha karmaşık yapı mantığı. Modern kalıplar bunu yapıcıda yapmayı önermez, ancak özel fabrika yöntemlerini veya sınıflarını kullanmayı önerir. Dolayısıyla bu durumda yapıcıların kullanımı asgari düzeydedir.

Sorunuzu cevaplamak için: Evet, bunun mümkün olduğuna inanıyorum. Ancak, dil tasarımında bazı büyük değişiklikler gerektirir.

Ve şunu fark ettim ki cevabım oldukça OT.


2

Evet, yapıcı kullanmadan hemen hemen her şeyi yapabilirsiniz, ancak bu açıkça Nesne Yönelimli Programlama dillerinin yararlarını israf ediyor.

Modern dillerde (burada programladığım C # hakkında konuşacağım) yalnızca yapıcıda çalıştırılabilecek kod bölümlerini sınırlayabilirsiniz . Bunun sayesinde sakar hatalardan kaçınabilirsiniz. Böyle bir şeyden biri salt okunur değiştiricidir:

public class A {
    readonly string rostring;

    public A(string arg) {
        rostring = arg;
    }

    public static A CreateInstance(string arg) {
        var result = new A();
        A.rostring = arg;  // < because of this the code won't compile!
        return result;
    }
}

Joachim Sauer tarafından daha önce tavsiye edildiği gibi Factorytasarım bildiri kullanmak yerine hakkında bilgi edinin Dependency Injection. .NET'te Bağımlılık Enjeksiyonu'nun Mark Seemann tarafından okunmasını tavsiye ederim .


1

İhtiyaçlara bağlı olarak türüyle bir nesneyi örneklemek kesinlikle mümkündür. Belirli bir türü döndürmek için sistemin global değişkenlerini kullanan nesnenin kendisi olabilir.

Ancak, kodda "All" olabilecek bir sınıf olması dinamik tip kavramıdır . Şahsen, bu yaklaşımın kodunuzda tutarsızlık yarattığına, test komplekslerini * yaptığına ve önerilen çalışmanın akışına göre "geleceğin kararsız hale geldiğine" inanıyorum.

* Testlerin önce türü, ikincisi elde etmek istediğiniz sonucu göz önünde bulundurması gerektiği gerçeğinden bahsediyorum. Sonra büyük iç içe geçmiş test yaratıyorsunuz.


1

Diğer yanıtların bazılarını dengelemek için:

Bir yapıcı size nesnenin tüm üye değişkenlerini geçerli bir duruma getirme fırsatı verir ... asla geçersiz bir nesneye sahip olmazsınız, bu da sizi birçok hatadan kurtarır.

ve

Bir yapıcı ile, bir nesne, bu sınıfın geçersiz örnekleri olmadığından emin olmak için kendi yapım mantığına sahiptir.

Bu tür açıklamalar bazen şu varsayımı ima eder:

Bir sınıf, ortaya çıktığı zaman nesneyi geçerli bir duruma sokan ve sınıfın hiçbir yönteminin geçersiz kılmak için bu durumu değiştiremediği bir yapıcıya sahipse, sınıfın dışındaki bir nesneyi algılaması imkansızdır. bu sınıfın geçersiz bir durumda.

Ancak bu tam olarak doğru değil. Çoğu dil , dış koda geçiren this(veya selfdilden ne olursa olsun) bir kurucuya karşı bir kurala sahip değildir . Böyle bir kurucu, yukarıda belirtilen kurala tamamen uyuyor ve yine de yarı yapılandırılmış nesnelerin dış koda maruz kalması riskini taşıyor. Küçük bir nokta ama kolayca gözden kaçırılabilir.


0

Bu biraz anekdottur, ancak genellikle nesnenin eksiksiz ve kullanılabilir olması için gerekli durum için yapıcıları saklı tutarım. Herhangi bir setter enjeksiyonunu engelleyen, kurucu bir kez çalıştırıldığında, nesnemin gereken işlemleri yapabilmesi gerekir.

Ertelenebilecek bir şey olursa, kurucudan ayrılıyorum (çıktı değerlerini hazırlamak, vb.). Bu yaklaşımla yapıcıları hiçbir şey için kullanmadığımı hissediyorum ama bağımlılık enjeksiyonunun anlamı var.

Bu ayrıca zihinsel tasarım sürecinizi zamansız hiçbir şey yapmamak için kablolama avantajına da sahiptir. Asla kullanılamayacak bir mantık başlatmayacak ya da gerçekleştirmeyeceksiniz çünkü en iyi durumda yaptığınız işin temelini yapmak en iyisi.

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.