Yapıcıda “yeni” kullanmak her zaman kötü mü?


37

Bir yapıcıda "basit" olanı (basit değerli olanlardan başka nesneler için) kullanmanın, birim testini imkansız hale getirdiği için kötü bir uygulama olduğunu okudum (o zaman bu ortak çalışanların da yaratılması gerekiyor ve alay edilemez). Birim testinde gerçekten tecrübeli olmadığım için önce öğreneceğim bazı kuralları toplamaya çalışıyorum. Ayrıca, bu kullanılan dilden bağımsız olarak genel olarak geçerli bir kural mıdır?


9
Testi imkansız kılmaz. Yine de kodunuzu korumayı ve test etmeyi zorlaştırıyor. Yeni bir okuma var tutkal örneğin.
David Arno

38
"her zaman" her zaman yanlıştır. :) En iyi uygulamalar var, ancak istisnalar var.
Paul

63
Bu hangi dil? newfarklı dillerde farklı şeyler ifade eder.
user2357112

13
Bu soru dile biraz bağlı, değil mi? Her dilde bir newanahtar kelime bile yok . Hangi dilleri soruyorsun?
Bryan Oakley

7
Aptal kural. Bağımlılık enjeksiyonunu "yeni" anahtar kelime ile çok az yapmanız gerektiğinde kullanmamak . "Bağımlılık inversiyonunu kırmak için kullanıyorsanız" yenisini kullanmak bir problemdir "demek yerine, sadece" bağımlılık inversiyonunu bozmayın "deyin.
Matt Timmermans

Yanıtlar:


36

Her zaman istisnalar vardır ve başlıkta her zaman “her zaman” ile ilgili bir sorunum var, ancak evet, bu kılavuz genel olarak geçerlidir ve ayrıca kurucu dışında da geçerlidir.

Bir yapıcıda yeninin kullanılması, SOLID'deki D'yi (bağımlılık inversiyon ilkesi) ihlal eder. Kodunuzu test etmek zorlaştırır, çünkü ünite testi tamamen izolasyonla ilgilidir; Somut referansları varsa, sınıfı izole etmek zordur.

Ancak bu sadece ünite testi ile ilgili değil. Aynı anda iki farklı veritabanına bir havuzu işaret etmek istersem ne olur? Kendi bağlamımdan geçme yeteneği, farklı konumlara işaret eden iki farklı veri havuzunu başlatmamı sağlıyor.

Yapıcıda yeni kullanmama kodunuzu daha esnek hale getirir. Bu new, nesne başlatma dışındaki yapıları kullanabilecek diller için de geçerlidir .

Ancak, açıkça, iyi yargı kullanmanız gerekir. Kullanmanın iyi olduğu newveya kullanılmaması daha iyi olduğu durumlarda birçok zaman vardır, ancak olumsuz sonuçlara sahip olmayacaksınız. Bir noktada bir yerde, newçağrılması gerekiyor. Sadece newdiğer birçok sınıfın bağlı olduğu bir sınıfın içini çağırırken çok dikkatli olun .

Yapıcınızda boş bir özel koleksiyon başlatmak gibi bir şey yapmanız iyi olur ve enjekte etmek saçma olurdu.

Bir sınıfın kendisine ne kadar çok referansı olursa new, içinden o kadar dikkatli olmamanız gerekir.


12
Gerçekten bu kuralda bir değer göremiyorum. Kodda sıkı bağlantıdan kaçınmanın kuralları ve kuralları vardır. Bu kural aynı konuyu ele alıyor gibi görünüyor, ancak bize ek bir değer vermemekle birlikte inanılmaz derecede kısıtlayıcı olması dışında. Öyleyse amaç ne? Neden head = nullherhangi bir şekilde kodun geliştirilmesinden gelen tüm bu özel durumlarla ilgilenmek yerine LinkedL uygulamam için sahte bir nesne yaratıyorsunuz ? Neden dahil edilmiş koleksiyonları boş bırakmak ve talep üzerine yaratmak onları yapıcıda yapmaktan daha iyi olsun ki?
Voo

22
Sen noktayı kaçırıyorsun. Evet, kalıcılık modülü kesinlikle yüksek seviyeli bir modülün bağımlı olmaması gereken bir şeydir. Ama yok "yeni hiç kullanmıyorum" değil "bazı şeyler enjekte doğrudan bağlı değildir edilmelidir" dan takip edin. Çevik Yazılım Geliştirme'nin güvenilir kopyasını alırsanız, Martin'in genellikle tek sınıflardan değil modüller hakkında konuştuğunu görürsünüz: "Yüksek seviyeli modüller, uygulamanın yüksek düzeyli politikalarıyla ilgilenir. Bu politikalar genellikle uygulanan ayrıntıları önemsemez onlar."
Voo

11
Yani mesele şu ki modüller arası - özellikle de farklı soyutlama seviyelerindeki modüller arasındaki bağımlılıklardan kaçınmalısınız . Ancak bir modül tek bir sınıftan daha fazlası olabilir ve aynı soyutlama katmanının sıkı bir şekilde birbirine bağlanmış kodu arasında arayüzleri kurmanın hiçbir anlamı yoktur. SOLID ilkelerini izleyen iyi tasarlanmış bir uygulamada, her bir sınıf bir arayüz uygulamamalı ve her kurucu her zaman sadece parametre olarak arayüz almalıdır.
Voo

18
Reddettim çünkü çok fazla terim çorbası vardı ve pratik düşünceler hakkında fazla tartışma yok. İki DB'ye bir repo ile ilgili bir şey var, ama dürüst olmak gerekirse, bu gerçek bir dünya örneği değil.
jpmc26 01

5
@ jpmc26, BJoBnh Bu cevabı kabul edip etmediğime bakmadan, nokta çok net bir şekilde ifade edilir. "Bağımlılık inversiyonu prensibi", "somut referans" veya "nesne başlatma" nın sadece kelimeler olduğunu düşünüyorsanız, aslında ne anlama geldiklerini bilmiyorsunuz. Cevap bu değil.
R. Schmitz

50

Yapıcıyı yalnızca başka bir çok nesne oluşturmak yerine yeni örneği başlatmak için kullanmaktan yana olsam da, yardımcı nesneler tamamdır ve bir şeyin böyle bir iç yardımcı olup olmadığına dair kararınızı kullanmanız gerekir.

Sınıf bir koleksiyonu temsil ediyorsa, dahili bir yardımcı diziye veya listeye veya karma gruba sahip olabilir. Bu newyardımcıları oluşturmak için kullanır ve oldukça normal kabul edilirdi. Sınıf, farklı iç yardımcıları kullanmak için enjeksiyon yapmaz ve bunun için hiçbir sebep yoktur. Bu durumda, nesnenin koleksiyondaki öğeleri biriktirme, kaldırma ve değiştirme işlemine yol açabilecek ortak yöntemlerini test etmek istersiniz.


Bir anlamda, bir programlama dilinin sınıf kurgusu, üst seviye soyutlamalar yaratma mekanizmasıdır ve biz problem alanı ile programlama dili ilkelleri arasındaki boşluğu doldurmak için bu soyutlamaları yaratırız. Ancak, sınıf mekanizması sadece bir araçtır; programlama diline göre değişir ve bazı alan adındaki soyutlamalar, bazı dillerde, programlama dili düzeyinde birden fazla nesne gerektirir.

Özetle, soyutlamanın basitçe bir veya daha fazla dahili / yardımcı nesne gerektirip gerektirmediğini, hala arayan kişi tarafından tek bir soyutlama olarak görülüp görülmediğini veya diğer nesnelerin arayan için daha iyi pozlanıp korunmayacağına karar vermek zorundasınız. Örneğin, arayan kişi sınıfı kullanırken bu diğer nesneleri gördüğü zaman, önerilebilecek olan bağımlılıkların kontrolü.


4
+1. Bu kesinlikle doğru. Sınıfın neyi hedeflediğini ve hangi uygulama ayrıntılarının açığa çıkması gerektiğini ve hangilerinin gerektirmediğini belirlemesi gerekir. “Sorumluluk” sınıfının da ne olduğunu belirleyerek oynamak zorunda olduğunuz bir tür ince sezgi oyunu var.
jpmc26

27

Tüm ortak çalışanlar ayrı olarak test etmek için yeterince ilgi çekici değildir, onları barındırma / örnekleme sınıfında (dolaylı olarak) test edebilirsiniz. Bu, bazılarının, özellikle sonradan test yaparken, her bir sınıfı, her bir genel yöntemi vb. TDD'yi kullanırken, bu 'ortak çalışanı' yeniden test edebilirsiniz; bu, zaten testinizin ilk aşamasından test edilmiş olduğu bir sınıfı çıkarır.


14
Not all collaborators are interesting enough to unit-test separatelyhikayenin sonu :-), Bu dava mümkün ve kimse bunu söylemeye cesaret edemedi. @Joppe Size biraz cevap vermenizi tavsiye ediyorum. Örneğin, uygulama ayrıntıları olan (değiştirme için uygun olmayan) bazı sınıf örnekleri ve gerekli görürsek son kullanma yöntemlerini ekleyebilirsiniz.
Laiv

@Laiv alan modelleri genellikle somuttur, soyut değildir, oraya yerleştirilmiş nesnelerin enjekte edilmesini istemezsiniz. Aksi takdirde mantığı olmayan basit değer nesnesi / karmaşık nesne de bir adaydır.
Joppe

4
+1, kesinlikle. Bir dil böylece kurulmuş ise sahip çağırmak için new File()birşey dosya ile ilgili olarak yapmanız, çağrı yasaklayan hiçbir anlamı yoktur. Ne yapacaksın, stdlib Filemodülüne karşı regresyon testleri yazacak mısın? Olası değil. Öte yandan, çağıran newbir üzerinde kendini icat sınıfının daha şüphelidir.
Kilian Foth

7
@KilianFoth: Yine de doğrudan çağıran her şeyi test eden iyi şanslar birimi new File().
Phoshi

1
Bu bir tahmin çalışması . Mantıklı olmadığı durumlarda, işe yaramaz olduğu durumlarda, mantıklı olduğu durumlarda yararlı olabilir. Bu bir ihtiyaç ve tercih meselesi.
Laiv

13

Birim testinde gerçekten tecrübeli olmadığım için önce öğreneceğim bazı kuralları toplamaya çalışıyorum.

Hiç karşılaşmadığınız problemler için “kuralları” öğrenirken dikkatli olun. Bazı "kurallar" veya "en iyi uygulamalar" ile karşılaşırsanız, bu kuralın "kullanılması" gerektiği ve basitçe "oyuncak " kuralının ne dediğini göz ardı ederek bu sorunu kendiniz çözmeye çalışmak için basit bir oyuncak örneği bulmanızı öneririm .

Bu durumda, 2 veya 3 basit sınıf ve uygulamaları gereken bazı davranışlar bulmaya çalışabilirsiniz. Sınıfları doğal hissettiren şekilde uygulayın ve her davranış için bir birim testi yazın. Karşılaştığınız sorunların bir listesini yapın, örneğin bir şekilde çalışan şeylerle başlarsanız, o zaman geri dönüp daha sonra değiştirmek zorunda kaldı; işlerin nasıl bir araya gelmesi gerektiği konusunda kafanız karışmışsa; Eğer kazan yazı yazarken rahatsız olduysanız; vb.

Sonra aynı kuralı "kuralı" izleyerek de çözmeye çalışın. Yine, karşılaştığınız sorunların bir listesini yapın. Listeleri karşılaştırın ve kurala uyurken hangi durumların daha iyi olacağını ve hangilerinin olmayabileceğini düşünün.


Asıl sorunuza gelince, "temel mantık" ve "hizmetler" arasında bir ayrım yaptığımız bir bağlantı noktası ve adaptör yaklaşımı tercih etme eğilimindeyim (bu, saf işlevler ile etkili prosedürler arasında ayrım yapmaya benzer).

Çekirdek mantık, sorun alanını temel alarak uygulamanın “içinde” olduğu şeyleri hesaplamakla ilgilidir. Sanki sınıflar içerebilir User, Document, Order, Invoice, vb It yolunda çekirdek sınıfları aramasını new, diğer temel sınıflar için o zamandan beri ediyoruz "iç" uygulama ayrıntılarını. Örneğin, bir tane oluşturmak Orderda neyin sipariş edildiğini Invoiceve Documentayrıntılarını yaratabilir . Testler sırasında bunları alay etmeye gerek yok çünkü bunlar test etmek istediğimiz gerçek şeyler!

Bağlantı noktaları ve adaptörler, çekirdek mantığın dış dünyayla nasıl etkileşimde bulunduğudur. Şeyler gibi burasıdır Database, ConfigFile, EmailSendervb yaşıyor. Bunlar testi zorlaştıran şeylerdir, bu yüzden bunları çekirdek mantık dışında yaratmanız ve gerektiğinde (bağımlılık enjeksiyonuyla veya yöntem argümanları vb. İle) aktarmanız önerilir .

Bu şekilde, temel mantık (önemli iş mantığının yaşadığı ve en fazla çalkalamaya tabi olduğu uygulamaya özel olan bölüm) veritabanları, dosyalar, e-postalar, vb. İle ilgilenmek zorunda kalmadan kendi başına test edilebilir. Bazı örnek değerleri iletebilir ve doğru çıktı değerlerini aldığımızı kontrol edebiliriz.

Bağlantı noktaları ve bağdaştırıcılar, iş mantığını önemsemeden, veritabanı, dosya sistemi vb. İçin alay kullanarak ayrı ayrı test edilebilir. Bazı örnek değerleri geçebilir ve bunların depolandıklarından / okunduklarından / gönderildiğinden / vb. Olduklarından emin olabiliriz. uygun şekilde.


6

Soruyu cevaplamama izin verin, burada kilit nokta olarak düşündüğüm şeyi toplayın. Kısacası için bazı kullanıcılara teklif vereceğim.

Her zaman istisnalar vardır, ancak evet, bu kural genel olarak geçerlidir ve ayrıca kurucu dışında da geçerlidir.

Bir yapıcıda yeni kullanmak , SOLID'deki D'yi (bağımlılık inversiyon sorumlusu) ihlal eder . Kodunuzu test etmek zorlaştırır, çünkü ünite testi tamamen izolasyonla ilgilidir; Somut referansları varsa sınıfı izole etmek zordur.

-TheCatWhisperer-

Evet, newiç yapıcıların kullanılması genellikle tasarım kusurlarımızı ortaya çıkaran tasarım kusurlarına (örneğin sıkı bağlantıya) yol açar. Test etmek zor , ama imkansız değil. Burada oyundaki özellik esnekliktir (değişikliklere tolerans) 1 .

Bununla birlikte, yukarıdaki alıntı her zaman doğru değildir. Bazı durumlarda, söz konusu olabilir sınıflar olmak içindir sıkı bağlı . David Arno bir çift yorumladı.

Elbette, sınıfın değişmez bir değer nesnesi, bir uygulama detayı , vb. Olduğu istisnalar vardır .

-David Arno-

Kesinlikle. Bazı sınıflar (örneğin iç sınıflar) ana sınıfın uygulama detayları olabilir. Bunların ana sınıfla birlikte test edilmesi amaçlanmıştır ve mutlaka değiştirilemez veya genişletilemezler.

Ayrıca, eğer SOLID tarikatımız bu sınıfları çıkarmamızı sağlarsa , başka bir iyi ilkeyi ihlal ediyor olabiliriz. Sözde Demeter Yasası . Diğer taraftan tasarım açısından gerçekten çok önemli buluyorum.

Bu nedenle, her zaman olduğu gibi muhtemel cevap bağlıdır . İç yapıcıları kullanmak newkötü bir uygulama olabilir. Fakat sistematik olarak her zaman değil.

Bu nedenle, sınıfların ana sınıfın uygulama ayrıntıları (yapamayacakları çoğu durumda) olup olmadığını değerlendirmemiz gerekir. Eğer öyleyse, onları yalnız bırakın. Olmazlarsa, IoC Containers'ın Kompozisyon Kökü veya Bağımlılık Enjeksiyonu gibi teknikleri düşünün .


1: SOLID'in asıl amacı kodumuzu daha test edilebilir hale getirmek değildir. Kodumuzu değişikliklere daha toleranslı kılmak. Daha esnek ve sonuçta test edilmesi daha kolay

Not: David Arno, TheWhisperCat, umarım sakıncası yoktur.


3

Basit bir örnek olarak, aşağıdaki sözde kodu göz önünde bulundurun

class Foo {
  private:
     class Bar {}
     class Baz inherits Bar {}
     Bar myBar
  public:
     Foo(bool useBaz) { if (useBaz) myBar = new Baz else myBar = new Bar; }
}

Yana newsaf bir uygulama olup Foo, hem Foo::Barve Foo::Bazbir parçası olan Fooünite içinde test ederken, Foobazı kısımlarını alay hiçbir anlamı yoktur Foo. Üniteleri test ederken sadece dış kısımlarla alay ediyorsunuz .FooFoo


-3

Evet, uygulama kök sınıflarınızda 'new' kullanmak bir kod kokusudur. Bu, sınıfı belirli bir uygulamayı kullanmaya kilitlediğiniz ve başka birini kullanamayacağınız anlamına gelir. Her zaman bağımlılığı yapıcıya enjekte etmeyi seçin. Bu yolla, test sırasında sadece alay bağımlılıklarını kolayca enjekte edebilmeyecek, aynı zamanda gerektiğinde farklı uygulamaları hızlı bir şekilde yerine koymanıza izin vererek uygulamanızı daha esnek hale getirecektir.

EDIT: İndirmenler için - işte, 'kodunu' yeni bir kod kokusu olarak işaretleyen bir yazılım geliştirme kitabına bir link: https://books.google.com/books?id=18SuDgAAQBAJ&lpg=PT169&dq=new%20keyword%20code%20smell&pg=PT169 # v = onepage ve q = yeni% 20keyword% 20code% 20smell & f = yanlış


15
Yes, using 'new' in your non-root classes is a code smell. It means you are locking the class into using a specific implementation, and will not be able to substitute another.Bu neden bir problem? Bağımlılık ağacındaki her bağımlılık
değişime

4
@ Paul: Varsayılan bir uygulamaya sahip olmak, varsayılan olarak belirtilen beton sınıfına sıkı sıkıya bağlı bir referans almanız anlamına gelir. Buna rağmen "kod kokusu" denmiyor.
Robert Harvey,

8
@EelaelaVacca: Herhangi bir bağlamda "kod kokusu" kelimelerini kullanma konusunda temkinli olurdum. "Cumhuriyetçi" veya "demokrat" demek gibi bir şey; bu kelimeler ne anlama geliyor? Buna benzer bir etiket verir vermez, asıl meseleleri düşünmeyi bırakıp öğrenme durur.
Robert Harvey

4
"Daha esnek" otomatik olarak "daha iyi" değildir.
whatsisname,

4
@EzoelaVacca: newAnahtar kelimeyi kullanmak kötü bir uygulama değildir ve asla olmamıştır. Bu var nasıl sen konularda bu aracı kullanın. Bir balyoz kullanmazsınız, örneğin bir top çekiçinin yeterli olduğu yerlerde.
Robert Harvey,
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.