Fayda sınıfları kötü mü? [kapalı]


97

Bu konuyu gördüm

Bir "Utilities" sınıfı kötüyse, genel kodumu nereye koymalıyım?

ve neden sınıflar kötüdür diye düşündünüz mü?

Diyelim ki düzinelerce sınıf derinliğinde bir alan modelim var. Örnekleri xml-ify yapabilmem gerekiyor. Ebeveyn üzerinde bir toXml yöntemi yapmalı mıyım? MyDomainXmlUtility.toXml yardımcı sınıfı yapmalı mıyım? Bu, iş ihtiyacının tüm etki alanı modelini kapsadığı bir durumdur - gerçekten bir örnek yöntemi olarak mı ait? Peki ya uygulamanın xml işlevselliğinde bir dizi yardımcı yöntem varsa?


27
"Kötü" teriminin değerinin düşürülmesi kötüdür!
Matthew Lock

1
@matthew Sorumun dayandığı gönderinin şartlarını korudum ...;)
hvgotcodes

Fayda sınıfları, tekillerin aynı nedenlerle kötü bir fikirdir.
CurtainDog

1
Bir toXML yöntemine sahip olup olmama konusundaki tartışma, zengin ve anemik alan modellerine odaklanan bir yöntemdir. codeflow.blogspot.com/2007/05/anemic-vs-rich-domain-models.html
James P.

@james, toXML sadece bir örnek ... peki ya her yerde kullanılan bazı normal ifade işlevleri? Mesela, etki alanı modeliniz genelinde dizelerle bazı şeyler yapmanız gerekiyor, ancak bir süper sınıfınızı (
java'da

Yanıtlar:


129

Fayda sınıfları tam olarak kötü değildir, ancak iyi bir nesne yönelimli tasarım oluşturan ilkeleri ihlal edebilirler. İyi bir nesne yönelimli tasarımda, çoğu sınıf tek bir şeyi ve tüm niteliklerini ve işlemlerini temsil etmelidir. Bir şey üzerinde çalışıyorsanız, o yöntem muhtemelen o şeyin bir üyesi olmalıdır.

Ancak, bir dizi yöntemi bir arada gruplamak için yardımcı sınıfları kullanabileceğiniz zamanlar vardır - bir örnek java.util.Collections, herhangi bir Java Koleksiyonunda kullanılabilecek bir dizi yardımcı program sağlayan sınıftır. Bunlar, belirli bir Koleksiyon türüne özgü değildir, bunun yerine herhangi bir Koleksiyonda kullanılabilen algoritmaları uygular.

Gerçekten yapmanız gereken, tasarımınız hakkında düşünmek ve yöntemleri nereye koymanın en mantıklı olduğunu belirlemektir. Genellikle, bir sınıfın içindeki işlemler gibidir. Bununla birlikte, bazen, gerçekten de bir fayda sınıfıdır. Bununla birlikte, bir fayda sınıfı kullandığınızda, içine rastgele yöntemler atmayın, bunun yerine yöntemleri amaca ve işlevselliğe göre düzenleyin.


SOLID oo ilkesine göre yardımcı program / yardımcı sınıfını kontrol etme hakkındaki bu makaleyle ve okunabilirlik.com/articles/rk1vvcqy
melaos

8
Dil, sınıflar dışında herhangi bir ad alanı mekanizması sunmuyorsa, bir ad alanının yapacağı bir sınıfı kötüye kullanmaktan başka seçeneğiniz yoktur. C ++ 'da, diğer işlevlerle ilgisi olmayan bağımsız bir işlevi bir ad alanına yerleştirebilirsiniz. Java'da, onu bir statik (sınıf) üye olarak bir sınıfa koymalısınız.
Unslander Monica

1
Yöntem olarak gruplama, OOP açısından kurallı olabilir. Bununla birlikte, OOP nadiren çözümdür (istisnalar arasında, kalıtım yoluyla kodun yeniden kullanımının inanılmaz derecede bozuk anlamlarının gerçekten uyduğu örneklerden biri olarak yüksek düzeyli mimari ve bileşen araç takımları vardır) ve genellikle yöntemler eşleştirme ve bağımlılıkları artırır. Önemsiz bir örnek: Sınıfınıza yöntem olarak yazıcı / tarayıcı yöntemleri sağlarsanız, sınıfı yazıcı / tarayıcı kitaplıklarına bağlarsınız. Ayrıca, olası uygulama sayısını etkin bir şekilde bire sabitlersiniz. Arayüz eklemediğiniz ve bağımlılıkları daha da artırmadığınız sürece.
Jo So

Öte yandan, "amaca ve işlevselliğe göre gruplama" düşüncenize katılıyorum. Yazıcı / tarayıcı örneğini tekrar gözden geçirelim ve ortak bir amaç için tasarlayarak bir dizi konser dersi ile başlayalım. Bazı hata ayıklama kodları yazmak ve sınıflarınız için metinsel bir temsil tasarlamak isteyebilirsiniz. Hata ayıklama yazıcılarınızı , tüm bu sınıflara ve bir yazıcı kitaplığına bağlı olan tek bir dosyada uygulayabilirsiniz. Hata ayıklamayan kod, bu uygulamanın bağımlılıkları ile yüklenmeyecektir.
Jo So

1
Util ve Helper sınıfları, bu tür kısıtlamaların talihsiz bir ürünüdür ve OOP'yi anlamayan veya sorun alanını anlamayanlar prosedürel kod yazmaya devam etmişlerdir.
bilelovitch

57

Bence genel fikir birliği, fayda sınıflarının kendiliğinden kötü olmadığıdır . Sadece onları mantıklı bir şekilde kullanmanız gerekir:

  • Statik fayda yöntemlerini genel ve yeniden kullanılabilir olacak şekilde tasarlayın. Vatansız olduklarından emin olun; yani statik değişken yok.

  • Çok sayıda yardımcı yönteminiz varsa, geliştiricilerin bulmasını kolaylaştıracak şekilde bunları sınıflara ayırın.

  • Bir etki alanı sınıfındaki statik veya örnek yöntemlerin daha iyi bir çözüm olacağı yardımcı program sınıflarını kullanmayın. Örneğin, soyut bir temel sınıftaki veya başlatılabilir bir yardımcı sınıftaki yöntemlerin daha iyi bir çözüm olup olmayacağını düşünün.

  • Java 8'den itibaren, bir arayüzdeki "varsayılan yöntemler", yardımcı program sınıflarından daha iyi bir seçenek olabilir. ( Örneğin , Java 8'deki Abstract sınıfına karşı varsayılan yöntemlerle arabirim bölümüne bakın .)


Bu Soruya bakmanın diğer yolu, alıntılanan Soru'da "Fayda sınıfları" kötü "ise" saman adam argümanı " olduğunu gözlemlemektir. Benim sorduğum gibi:

"Domuzlar uçabiliyorsa şemsiye taşımalı mıyım?"

Yukarıdaki soruda aslında domuzlar uçabilir söylemiyorum ... yoksa o anlayışını kabul olduğunu olabilir uçarlar.

Tipik "xyz kötüdür" ifadeleri, aşırı bir bakış açısı sunarak sizi düşündürmeyi amaçlayan retorik araçlardır. Nadiren (varsa) gerçek gerçeklerin beyanları olarak tasarlanırlar.


16

Hizmet sınıfları sorunludur çünkü sorumlulukları kendilerini destekleyen verilerle gruplandırmada başarısız olurlar.

Bununla birlikte, son derece kullanışlıdırlar ve onları her zaman kalıcı yapılar olarak veya daha kapsamlı bir yeniden düzenleme sırasında basamak taşları olarak inşa ediyorum.

Bir itibaren Temiz Kod perspektifinden yarar sınıfları Tek Sorumluluk ve Açık-Kapalı prensibi ihlal. Değiştirmek için pek çok nedenleri vardır ve tasarım gereği genişletilemezler. Gerçekten yalnızca yeniden düzenleme sırasında orta düzeyde bir hamle olarak var olmalıdırlar.


2
Ben buna pratik bir problem diyebilirim, çünkü örneğin Java'da bir sınıfta metot olmayan herhangi bir fonksiyon yaratamazsınız. Böyle bir dilde, "değişken içermeyen ve yalnızca statik yöntemler içeren sınıf", yardımcı program işlevlerinin ad alanını ifade eden bir deyimdir ... Java'da böyle bir sınıfla karşılaşıldığında, IMHO geçersizdir ve bir sınıftan bahsetmek anlamsızdır. , classanahtar kelime kullanılmasına rağmen . Bir ad alanı gibi
şarlatır

2
Açık sözlü olduğum için üzgünüm, ancak "devletsiz statik yöntemler [...] OOP sistemindeki tuhaf yöntemler [...] felsefi sorun" son derece yanlış yönlendirilmiş. Durumdan kaçınmanız HARİKA çünkü doğru kod yazmayı kolaylaştırır. Yazmak zorunda olduğumda KIRILMIŞTIR, x.equals(y)çünkü 1) bunun herhangi bir durumu gerçekten değiştirmemesi gerektiği gerçeği aktarılmaz (ve uygulamayı bilmeden buna güvenemem) 2) Hiçbir zaman xbir " y3) 'e kıyasla tercih edilen "veya" üzerine hareket eden "bir konuma sahip olmak istemiyorum xveya ysadece değerleriyle ilgileniyorum.
Jo So

4
Gerçekte gerçek dünyadaki çoğu işlevsellik en iyi statik, durumsuz, matematiksel işlevler olarak ifade edilir. Math.PI, mutasyona uğratılması gereken bir nesne mi? Sadece bir sayının sinüsünü elde etmek için IAbstractRealOperation uygulayan bir AbstractSineCalculator gerçekten somutlaştırmak zorunda mıyım?
Jo So

1
@JoSo, vatansızlığın ve doğrudan hesaplamanın değerine tamamen katılıyorum. Naif OO felsefi olarak buna karşı çıkıyor ve bence bu onu derinden kusurlu yapıyor. İhtiyaç duymayan şeylerin soyutlanmasını teşvik eder (gösterdiğiniz gibi) ve gerçek hesaplama için anlamlı soyutlamalar sağlamada başarısız olur. Bununla birlikte, OO dilini kullanmaya yönelik dengeli bir yaklaşım, modülerliği ve sürdürülebilirliği teşvik ettikleri için değişmezlik ve vatansızlık eğilimindedir.
Alain O'Dea

2
@ AlainO'Dea: Yorumunuzu vatansız işlevselliğe aykırı olarak yanlış okuduğumu fark ettim. Üslubum için özür dilerim - şu anda ilk Java projeme dahil oldum (10 yıllık programlamamın büyük bir kısmında OOP'den kaçındıktan sonra) ve belirsiz durum ve anlamlara sahip soyut şeylerin katmanlarının altına gömüldüm. Ve biraz temiz havaya ihtiyacım var :-).
Jo So

9

Sanırım kötü olmaya başladığında

1) Çok büyüyor (bu durumda onları anlamlı kategorilere ayırmanız yeterli).
2) Statik olmaması gereken yöntemler mevcut

Ama bu şartlar sağlanmadığı sürece çok faydalı olduklarını düşünüyorum.


"Statik olmaması gereken yöntemler mevcuttur." Bu nasıl mümkün olabilir?
Koray Tugay

@KorayTugay Durağan Widget.compareTo(Widget widget)ve durağan olanı düşünün WidgetUtils.compare(Widget widgetOne, Widget widgetTwo). Karşılaştırma, statik olarak yapılmaması gereken bir şeye örnektir.
ndm13

5

Başparmak kuralı

Bu soruna iki açıdan bakabilirsiniz:

  • Genel *Utilyöntemler genellikle kötü kod tasarımının veya tembel adlandırma kurallarının bir önerisidir.
  • Yeniden kullanılabilir alanlar arası durum bilgisi içermeyen işlevler için yasal bir tasarım çözümüdür. Lütfen hemen hemen tüm yaygın sorunlar için mevcut çözümler olduğunu unutmayın.

Örnek 1. utilSınıfların / modüllerin doğru kullanımı . Harici kitaplık örneği

Kredileri ve kredi kartlarını yöneten bir uygulama yazdığınızı varsayalım. Bu modüllerden gelen veriler, web servisleri aracılığıyla jsonformatta sunulur. Teoride, nesneleri manuel olarak dizelere dönüştürebilirsiniz json, ancak bu tekerleği yeniden icat eder. Doğru çözüm, java nesnelerini istenen formata dönüştürmek için kullanılan her iki modüle de harici kitaplığı dahil etmek olacaktır. (örnek resimde gson'u gösterdim )

görüntü açıklamasını buraya girin


Örnek 2. utilSınıfların / modüllerin doğru kullanımı . utilDiğer ekip üyelerine bahane olmadan kendi yazınızı yazmak

Bir kullanım örneği olarak, iki uygulama modülünde bazı hesaplamalar yapmamız gerektiğini varsayalım, ancak her ikisinin de Polonya'da resmi tatiller olduğunda bilmesi gerekir. Teorik olarak bu hesaplamaları modüller içinde yapabilirsiniz, ancak bu işlevi ayrı modüle çıkarmak daha iyi olacaktır.

İşte küçük ama önemli detay. Yazdığınız sınıf / modül çağrılmaz HolidayUtilama PolishHolidayCalculator. İşlevsel olarak bir utilsınıftır, ancak genel kelimeden kaçınmayı başardık.

görüntü açıklamasını buraya girin


1
Bir yEd hayranı görüyorum;) İnanılmaz bir uygulama.
Sridhar Sarnobat

3

Yardımcı sınıflar kötüdür çünkü sınıf için daha iyi bir isim bulamayacak kadar tembel olduğunuzu gösterir :)

Olduğu söyleniyor, ben tembelim. Bazen sadece işi halletmeniz gerekir ve zihniniz boştur .. İşte o zaman "Fayda" dersleri içeri girmeye başlar.


3

Şimdi bu soruya dönüp baktığımda, C # genişletme yöntemlerinin, yardımcı sınıflara olan ihtiyacı tamamen ortadan kaldırdığını söyleyebilirim. Ancak tüm dillerin böyle dahice bir yapısı yoktur.

Ayrıca, mevcut nesneye doğrudan yeni bir işlev ekleyebileceğiniz JavaScript'e de sahipsiniz.

Ama bu sorunu C ++ gibi eski bir dilde çözmenin gerçekten zarif bir yolu olduğundan emin değilim ...

İyi OO kodunu yazmak biraz zordur ve bulmak zordur, çünkü İyi OO yazmak, düzgün işlevsel kod yazmaktan çok daha fazla zaman / bilgi gerektirir.

Ve bütçeniz kısıtlı olduğunda, patronunuz bütün günü bir sürü dersle geçirdiğinizi görmekten her zaman mutlu olmaz ...


3

Fayda sınıflarının kötü olduğuna tamamen katılmıyorum.

Bir fayda sınıfı, OO ilkelerini bazı şekillerde ihlal etse de, her zaman kötü değildir.

Örneğin, değerle eşleşen tüm alt dizelerden oluşan bir dizeyi temizleyecek bir işlev istediğinizi düşünün x.

stl c ++ (şu an itibariyle) bunu doğrudan desteklemiyor.

İçin polimorfik bir uzantı oluşturabilirsiniz std::string.

Ama sorun şu ki, projenizde kullandığınız HER dizenin sizin genişletilmiş dizge sınıfınız olmasını gerçekten istiyor musunuz?

OO'nun gerçekten mantıklı olmadığı zamanlar vardır ve bu onlardan biridir. Programımızın diğer programlarla uyumlu olmasını istiyoruz, bu yüzden bağlı kalacağız std::stringve bir sınıf StringUtil_(veya başka bir şey) oluşturacağız.

Sınıf başına bir kullanımda kalmanızın en iyisi olduğunu söyleyebilirim. Tüm sınıflar için tek bir kullanımın veya bir sınıf için birçok programın olması aptalca olduğunu söyleyebilirim.


2

Tasarımcı, kodu yerleştirmek için uygun bir yer düşünemediği için bir şeyi bir yardımcı program olarak markalamak çok kolaydır. Genellikle birkaç gerçek "yardımcı program" vardır.

Genel bir kural olarak, kodu genellikle ilk kullanıldığı pakette tutarım ve daha sonra yalnızca daha genel bir yere yeniden düzenleme yaparım eğer daha sonra gerçekten başka bir yerde gerekli olduğunu anlarsam. Tek istisna, benzer / ilgili işlevselliği gerçekleştiren bir paketim olması ve kodun oraya en iyi şekilde uymasıdır.


2

Durumsuz statik yöntemler içeren yardımcı sınıflar yararlı olabilir. Bunların birim testi genellikle çok kolaydır.



1

Util sınıflarının çoğu kötüdür çünkü:

  1. Yöntemlerin kapsamını genişletirler. Aksi takdirde özel olacak olan kodu herkese açık hale getiriyorlar. Eğer yararlan yöntemi, farklı sınıflardaki birden çok arayan tarafından gerekliyse ve kararlıysa (yani güncellenmesi gerekmiyorsa), özel yardımcı yöntemleri kopyalayıp çağıran sınıfa yapıştırmak benim görüşüme göre daha iyidir. Bir API olarak ortaya çıkardığınızda, bir jar bileşenine genel giriş noktasının ne olduğunu anlamayı zorlaştırırsınız (yöntem başına bir ebeveyn ile hiyerarşi adı verilen bir ağaç yapısına sahip olursunuz. Bu, bileşenlere zihinsel olarak ayrılmak daha kolaydır, birden çok ana yöntemden çağrılan yöntemleriniz var).
  2. Ölü kodla sonuçlanırlar. Zamanla, uygulamanız geliştikçe Util yöntemleri kullanılmaz hale gelir ve sonuçta kod tabanınızı kirleten kullanılmayan kodlar ortaya çıkar. Özel kalsaydı, derleyiciniz size yöntemin kullanılmadığını söylerdi ve onu kaldırabilirdiniz (en iyi kod hiç kod olmamasıdır). Böyle bir yöntemi özel olmayan bir yöntem haline getirdiğinizde, bilgisayarınız kullanılmayan kodu kaldırmanıza yardımcı olmak için güçsüz kalacaktır. Tüm bilgisayarın bildiği için farklı bir jar dosyasından çağrılabilir.

Statik ve dinamik kitaplıklar arasında bazı benzerlikler vardır.


0

Bir sınıfa bir yöntem ekleyemediğimde (örneğin, AccountJr. Geliştiriciler tarafından yapılan değişikliklere karşı kilitlendiğinde), Utilities sınıfıma şu şekilde birkaç statik yöntem ekliyorum:

public static int method01_Account(Object o, String... args) {
    Account acc = (Account)o;
    ...
    return acc.getInt();
}  

1
Görünüşe göre sen bana Jr'sun ..: P Bunu yapmanın kötü olmayan yolu kibarca "Jr Geliştiricinizden" Accountdosyanın kilidini açmasını istemektir . Ya da daha iyisi, kilitlenmeyen kaynak kontrol sistemleriyle gidin.
Sudeep

1
AccountDosyanın, Jr. Geliştiricilerin üzerinde değişiklik yapamayacağı şekilde kilitlendiğini kastettiğini varsayıyorum . Bir Jr. Geliştirici olarak, aşmak için yukarıdaki gibi yaptı. Bununla birlikte, dosyaya yazma erişimi elde etmek ve bunu düzgün bir şekilde yapmak hala daha iyi dedi.
Doktor

Buna +1 eklemek, kimse bu yanıtı neden verdiğinizle ilgili tam bağlamı bilmediğinde olumsuz oylar sert görünmektedir
joelc

0

Fayda sınıfları her zaman kötü değildir. Ancak yalnızca geniş bir işlevsellik yelpazesinde ortak olan yöntemleri içermelidirler. Yalnızca sınırlı sayıda sınıf arasında kullanılabilen yöntemler varsa, ortak bir ebeveyn olarak soyut bir sınıf oluşturmayı düşünün ve içine yöntemleri koyun.

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.