Gelen bir parametrenin değiştirilmesi antipattern midir? [kapalı]


60

Java ile programlama yapıyorum ve her zaman şöyle dönüştürücüler yaparım:

public OtherObject MyObject2OtherObject(MyObject mo){
    ... Do the conversion
    return otherObject;
}

Yeni işyerinde kalıp:

public void MyObject2OtherObject(MyObject mo, OtherObject oo){
    ... Do the conversion
}

Benim için gelen parametreleri değiştirmemeye alışkın olduğum için biraz koklamak. Bu gelen parametre değişikliği bir antipattern mi yoksa tamam mı? Bazı ciddi sakıncaları var mı?


4
Bunun için doğru cevap dile özgü olacaktır, çünkü değer-by-pass parametrelerinin etkili bir şekilde onları yerel değişkenlere dönüştürdüğü durumlar.
Blrfl

1
İkinci desen bazen nesneleri yeniden kullanmak için bir verimlilik ölçüsü olarak kullanılır. Bunun oo, yeni bir nesneye ayarlanmış bir işaretçi değil, metoda iletilen bir nesne olduğunu varsayalım. Buradaki durum bu mu? Eğer bu Java ise muhtemelen, eğer C ++ ise olmayabilir
Richard Tingle

3
Bu erken optimizasyona benziyor, evet. Genel olarak ilk formu kullanmanızı öneririm, ancak performans darboğazıyla karşılaşırsanız, haklı çıkarmak için ikinci formu bir yorumla kullanabilirsiniz . Bir yorum, kendinizin ve başkalarının bu koda bakmasını ve bu aynı soruyu tekrar tekrar başlatmasını önler.
Keen

2
İkinci desen, fonksiyonun, örneğin başarı / başarısızlık için bir değer döndürdüğü durumlarda kullanışlıdır. Fakat bunda gerçekten 'yanlış' bir şey yok. Gerçekten nesneyi nerede oluşturmak istediğinize bağlı.
GrandmasterB

12
Bu iki farklı modeli aynı şekilde uygulayan işlevleri isimlendirmem.
Casey,

Yanıtlar:


70

Bu bir antipattern değil, kötü bir uygulama.

Bir antipattern ve sadece kötü bir uygulama arasındaki fark burada: anti-patern tanımı .

Gösterdiğiniz yeni işyeri tarzı , Bob Amca'nın Temiz Yasasına göre, kötü bir uygulama , uzun süreli veya ÇOP öncesi zamanlar.

Argümanlar en doğal olarak bir fonksiyona giriş olarak yorumlanır.

Sizi işlev imzasını kontrol etmeye zorlayan herhangi bir şey çift çekime eşdeğerdir. Bilişsel bir moladır ve bundan kaçınılmalıdır. Nesne yönelimli programlamadan önceki günlerde bazen çıktı argümanlarının olması gerekliydi. Bununla birlikte, OO dillerinde çıktı argümanlarına duyulan ihtiyacın büyük kısmı ortadan kalkar


7
Bağladığınız tanımın neden bunun bir kalıp karşıtı olarak değerlendirildiğini engellediğini anlamıyorum.
Samuel Edwin Ward

7
Öyle olduğunu gerekçe "yerine elimizdeki birini geri dönüşüm ve bir arg olarak geçmelidir, yeni bir nesne oluşturarak değil, CPU / mem kurtarıyorsun" çünkü öyle varsayalım açıkçası yanlış hakkında herkes ciddi cepten arka bu bununla olabilir hiç bir desen oluşturmazlar - bu yüzden tanımı gereği anti-patern olarak
düşünmemize imkan yok

1
Bir giriş parametresinin değiştirilmesi gereken geniş bir nesne koleksiyonu olduğu durumdan ne haber?
Steve Chambers

Seçenek 1'i de tercih etsem de, OtherObjectarayüz ne ise ? Sadece arayan (umarım) ne tür bir beton istediğini bilir.
user949300,

@ SteveChambers Bu durumda, sınıfın veya yöntemin tasarımının kötü olduğunu ve bundan kaçınmak için yenilenmesi gerektiğini düşünüyorum.
piotr.wittchen

16

Robert C. Martin'in ünlü kitabı "Temiz Kod" dan alıntı:

Çıktı argümanlarından kaçınılmalıdır

İşlevler az sayıda argümana sahip olmalıdır

İkinci model her iki kuralı, özellikle de "çıktı argümanı" olanı ihlal eder. Bu anlamda ilk düzenden daha kötü.


1
Birincisini ihlal ettiğini söyleyebilirim, birçok argüman dağınık kod anlamına gelir, ancak iki argümanın tamamen iyi olduğunu düşünüyorum. Temiz kodla ilgili kuralın, 5 veya 6 gelen verilere ihtiyaç duymanız durumunda, tek bir yöntemde çok fazla şey yapmak istediğiniz anlamına gelir; bu nedenle, kod okunabilirliğini ve OOP kapsamını arttırmak için yeniden denemelisiniz.
CsBalazsHungary

2
Neyse, bunun katı bir kanun değil, güçlü bir öneri olduğundan şüpheleniyorum. İlkel türlerle çalışmayacağını biliyorum, eğer bir projede geniş yayılmış bir modelse, bir genç bir int'den geçmek için fikirler alır ve Nesneler gibi değişmesini bekler.
CsBalazsHungary

27
Temiz Kod'un ne kadar doğru olduğuna bakılmaksızın, neden bunlardan kaçınılması gerektiğini açıklamadan bunun değerli bir cevap olduğunu sanmıyorum . Onlar taştan bir tablete gelen emirler değil, anlama önemlidir. Bu cevap, kitaptaki akıl yürütmenin bir özetini ve bir bölüm referansını
vererek geliştirilebilir

3
Cehennem, eğer birileri bir kitapta yazdıysa akla gelebilecek herhangi bir durum için iyi bir tavsiye olmalı . Düşünceye gerek yok.
Casey,

2
@emodendroket Ama dostum, bu kadar adam!
Pierre Arlaud

15

İkinci deyim daha hızlı olabilir; çünkü arayan, yeni örnek oluşturan her yineleme yerine bir değişkeni uzun döngü boyunca yeniden kullanabilir.

Genelde kullanmazdım, mesela. Oyun programlamasında yeri vardır. Örneğin, JavaMonkey'in Vector3f bölümüne bakın, birçok işlem değiştirilmeli ve sonuç olarak döndürülmelidir.


12
Peki, bu tıkanıklık olup olmadığına katılıyorum, ama çalıştığım her yerde, darboğazlar genellikle düşük verimli algoritmalar, klonlama veya nesne yaratma değil. Oyun geliştirme hakkında daha az şey biliyorum.
CsBalazsHungary

4
@CsBalazsHungary Nesne yaratmanın bir endişe kaynağı olduğu zaman sorunların bellek tahsisi ve çöp toplama ile ilgili olacağına inanıyorum, ki algoritmik karmaşıklığı izleyen bir sonraki darboğaz seviyesi (özellikle sınırlı hafızalı bir ortamda, örneğin akıllı telefonlar ve benzeri). .
JAB

JMonkey ayrıca, genellikle performans açısından kritik olduğu için bunu çok gördüğüm yer. Yine de "JavaMonkey" dediğini duymadım
Richard Tingle

3
@ JAB: JVM'nin GC'si, geçici nesneler için özel olarak ayarlanmıştır. Büyük miktarlarda çok kısa ömürlü nesnelerin toplanması önemsizdir ve çoğu durumda geçici neslinizin tamamı tek bir işaretçi hareketi ile toplanabilir.
Phoshi

2
biri için varsa aslında bir nesne oluşturmak , bu olmayacak performans etkili; Bir kod bloğu ağır hız için optimize edilecek olması durumunda, gereken temeli ve onun yerine özgün diziler kullanın; bu nedenle, bu cevaptaki ifadenin geçerli olmadığını düşünüyorum.
vaxquis

10

Bunların iki eşdeğer kod parçası olduğunu sanmıyorum. İlk durumda, oluşturmanız gerekir otherObject. İkincisinde varolan örneği değiştirebilirsiniz. İkisinin de kullanımı var. Kod kokusu birbirini tercih ederdi.


Eşdeğer olmadığını biliyorum. Haklısın, bu şeylere devam ediyoruz, bu yüzden fark yaratacaktır. Yani hiçbirinin antipattern olmadığını söylüyorsun, bu sadece kullanım davası mı?
CsBalazsHungary

@CsBalazsHungary Evet derdim. Sağladığınız küçük kod parçasına bakarak.
Euphoric

Elbette güncellenmeyecek olan bir ilkel parametreniz varsa, bunun daha ciddi bir sorun olacağını tahmin ediyorum, bu yüzden @claasz'ın yazdığı gibi: gerekli olmadıkça kaçınılması gerekir.
CsBalazsHungary

1
@CsBalazsHungary İlkel argüman durumunda, fonksiyon bile çalışmaz. Yani, değişkenleri değiştirmekten daha kötü bir problemin var.
Euphoric

Bir gencin, ilkel bir tür çıktı parametresi haline getirme tuzağına düşeceğini söyleyebilirim. Bu nedenle, ilk dönüştürücünün mümkünse tercih edilmesi gerektiğini söyleyebilirim.
CsBalazsHungary

7

Gerçekten dile bağlı.

Java'da ikinci form bir kalıp önleme olabilir, ancak bazı diller parametrenin farklı şekilde geçişini ele alır. Yerine geçmek değerine göre ya da referans ile bu örneğin Ada ve VHDL,, parametreler modlara sahip olabilir in, outya da in out.

Bir inparametreyi değiştirmek veya bir parametreyi okumak bir hatadır out, ancak bir in outparametrede yapılan değişiklikler arayana geri iletilir.

Dolayısıyla Ada'daki iki form (bunlar aynı zamanda yasal VHDL)

function MyObject2OtherObject(mo : in MyObject) return OtherObject is
begin
    ... Do the conversion
    return otherObject;
end MyObject2OtherObject;

ve

procedure MyObject2OtherObject(mo : in MyObject; oo : out OtherObject) is
begin
    ... Do the conversion
    oo := ... the conversion result;
end MyObject2OtherObject;

Her ikisinin de kullanımları vardır; Bir işlem Outsadece tek bir sonuç verirken bir prosedür birden fazla parametrede birden fazla değer döndürür. İkinci parametrenin amacı işlem bildiriminde açıkça belirtildiği için bu forma itiraz edilemez. Okunabilirlik için işlevi tercih etme eğilimindeyim. ancak prosedürün daha iyi olduğu, örneğin arayan bir kişinin nesneyi yarattığı durumlar olacaktır.


3

Gerçekten koklamak gibi görünüyor ve daha fazla bağlam görmeden kesin olarak söylemek mümkün değil. Her ikisinin de alternatifi olmasına rağmen, bunu yapmanın iki nedeni olabilir.

Birincisi, kısmi dönüşüm uygulamanın veya dönüşüm başarısız olursa sonucu varsayılan değerlerle bırakmanın kısa bir yoludur. Yani, bu olabilir:

public void ConvertFoo(Foo from, Foo to) {
    if (can't convert) {
        return;
    }
    ...
}

Foo a;
Foo b = DefaultFoo();
ConvertFoo(a, b);
// If conversion fails, b is unchanged

Tabii ki, genellikle bu istisnalar kullanılarak ele alınır. Ancak, hangi nedenle olursa olsun istisnalardan kaçınılması gerekse bile, bunu yapmanın daha iyi bir yolu var - TryParse modeli bir seçenek.

Diğer bir sebep, tamamen tutarlılık nedeniyle olabilir, örneğin, bu yöntemin, herhangi bir nedenden ötürü tüm dönüşüm işlevleri için kullanıldığı (çoklu çıkışlara sahip diğer dönüşüm fonksiyonları gibi), halka açık bir API'nin bir parçası olması.

Java, birden fazla çıktıyla başa çıkmak için harika değildir - bazı diller gibi sadece çıktı parametrelerine sahip olamaz veya diğerleri gibi birden fazla dönüş değerine sahip olamaz - ancak yine de, dönüş nesneleri kullanabilirsiniz.

Tutarlılık nedeni oldukça topaldır, ancak ne yazık ki en yaygın olanı olabilir.

  • Belki de iş yerinizdeki (ya da kod tabanınızdaki) stiller Java dışı bir arka plandan geliyor ve değişmekte isteksizler.
  • Kodunuz, bu tarzın daha aptalca olduğu bir dilden yapılmış bir port olabilir.
  • Kuruluşunuzun farklı dillerde API tutarlılığını sürdürmesi gerekebilir ve bu en düşük ortak payda stiliydi (bu kesin ama Google için bile geçerli ).
  • Veya belki de stil uzak geçmişte daha anlamlıydı ve şu anki biçimine uyuyordu (örneğin, TryParse modeli olabilirdi, ancak bazı iyi niyetli bir selef, kimsenin kontrol etmediğini keşfettikten sonra geri dönüş değerini kaldırmıştı).

2

İkinci modelin avantajı, arayanı yaratılan nesne için mülkiyet ve sorumluluk almaya zorlamasıdır. Yöntemin nesneyi yaratıp yaratmadığı veya yeniden kullanılabilir bir havuzdan alıp almadığı konusunda hiçbir soru yoktur. Arayan, yeni nesnenin kullanım ömrü ve elden çıkarılmasından sorumlu olduklarını bilir.

Bu yöntemin dezavantajları:

  1. Fabrika yöntemi olarak kullanılamaz, arayan kişi OtherObjectihtiyaç duyduğu tam alt tipini bilmek ve önceden inşa etmek zorundadır .
  2. Bu parametreler gelirse yapıcılarında parametre gerektiren nesnelerle kullanılamaz MyObject. OtherObjectbilmeden inşa edilebilir olmalı MyObject.

Cevap c # konusundaki deneyimlerime dayanıyor, umarım mantık Java'ya çevrilir.


Bahsettiğiniz sorumlulukla, aynı zamanda nesnelerin yaşam döngüsünü "görmek daha zor" hale getiriyor. Karmaşık bir yöntem sisteminde, geçtiğimiz tüm yöntemlere bakmak yerine bir nesnenin doğrudan değiştirilmesini tercih ederim.
CsBalazsHungary

Ben de öyle düşünüyordum. İlkel bir Bağımlılık Enjeksiyonu türü
Lyndon White

Diğer bir avantaj, OtherObject'in soyut veya bir arabirim olmasıdır. İşlevin hangi tür yaratacağı hakkında bir fikri yok, ancak arayan kişi olabilir.
user949300,

2

Gevşek anlambilim düşünüldüğünde - myObjectToOtherObjecteşit olarak bazı verileri ilk nesneden ikinciye taşıdığınız veya tamamen yeni bir şekilde dönüştürdüğünüz anlamına gelebilir , ikinci yöntem daha yeterli görünüyor.

Bununla birlikte, eğer yöntem adı Convert("dönüştürme işlemini yap" bölümüne bakarsak, olması gerektiği gibi) olsaydı, ikinci yöntemin bir anlamı olmayacağını söylerdim. Bir değeri zaten var olan başka bir değere dönüştürmezsiniz, IMO.

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.