'Fayda fonksiyonları' sınıflarının evcilleştirilmesi


15

Java kod tabanımızda aşağıdaki modeli görmeye devam ediyorum:

/**
 This is a stateless utility class
 that groups useful foo-related operations, often with side effects.
*/
public class FooUtil {
  public int foo(...) {...}
  public void bar(...) {...}
}

/**
 This class does applied foo-related things.
*/
class FooSomething {
  int DoBusinessWithFoo(FooUtil fooUtil, ...) {
    if (fooUtil.foo(...)) fooUtil.bar(...);
  }
}

Beni rahatsız eden şey, FooUtil her yerde , çünkü test.

  • Yapamam FooUtilYöntemlerini statik , çünkü her ikisini de FooUtilve istemci sınıflarını test etmek için onları alay edemeyeceğim .
  • İle FooUtiltüketim yerinde bir örnek oluşturamıyorumnew , çünkü test için alay edemeyeceğim.

En iyi seçeneğimin enjeksiyon kullanmak olduğunu düşünüyorum (ve yapıyorum), ancak kendi zorluklarını ekliyor. Ayrıca, birkaç util örneğinin iletilmesi, yöntem parametre listelerinin boyutunu şişirir.

Görmediğim kadar iyi idare etmenin bir yolu var mı?

Güncelleme: yardımcı sınıflar vatansız olduğundan, muhtemelen test için sınıfın temel statik alanını güncelleme yeteneğini korurken statik bir tekil INSTANCEüye veya statik bir getInstance()yöntem ekleyebilirim . Çok temiz görünmüyor.


4
Ve birim testleri etkili bir şekilde yapmak için tüm bağımlılıkların alay konusu olması gerektiği varsayımı doğru değil (yine de bunu tartışan soruyu arıyorum).
Telastyn

4
Bu yöntemler neden verilerini işledikleri sınıfın üyeleri değil?
Jules

3
FooUtility ise statik fns adını veren ayrı bir Junit setine sahip olun. Sonra alay etmeden kullanabilirsiniz. Eğer taklit etmeniz gereken bir şey varsa, o zaman sadece bir yardımcı program değil, bazı IO veya iş mantığı yapıyor ve aslında bir sınıf üyesi referansı olmalı ve yapıcı, init veya setter yöntemi ile başlatılmalıdır.
tgkprog

2
Jules yorumu için +1. Util sınıfları bir kod kokusudur. Anlambilime bağlı olarak daha iyi bir çözüm olmalıdır. Belki FooUtilyöntemlerin içine konulması FooSomething, belki de bu yöntemlerin FooSomethingdeğiştirilmesi / genişletilmesi gereken özellikler vardır, böylece FooUtilyöntemlere artık ihtiyaç duyulmaz. Lütfen bize FooSomethingbu sorulara iyi bir cevap sunmamıza yardımcı olacak daha somut bir örnek verin.
valenterry

13
@valenterry: util sınıflarının kod kokusu olmasının tek nedeni Java'nın bağımsız işlevlere sahip olmak için iyi bir yol sunmamasıdır. Sınıflara özgü olmayan işlevlere sahip olmanın çok iyi nedenleri vardır.
Robert Harvey

Yanıtlar:


16

Öncelikle farklı problemler için farklı programlama yaklaşımları olduğunu söyleyeyim. İşim için orjinazasyon ve bakım için güçlü bir OO tasarımı tercih ediyorum ve cevabım bunu yansıtıyor. İşlevsel bir yaklaşımın çok farklı bir yanıtı olacaktır.

Yöntemlerin üzerinde olması gereken nesne mevcut olmadığından çoğu yardımcı program sınıfının oluşturulduğunu bulma eğilimindeyim. Bu hemen hemen her zaman iki durumdan birinden gelir, biri etrafından koleksiyonlar geçirir veya biri etrafından fasulye geçirir (Bunlar genellikle POJO'lar olarak adlandırılır, ancak bir grup pasör ve alıcıların en iyi fasulye olarak tanımlandığına inanıyorum).

Etrafta dolaştığınız bir koleksiyonunuz olduğunda, bu koleksiyonun bir parçası olmak isteyen bir kod olmalıdır ve bu da sisteminize serpilir ve sonunda yardımcı program sınıflarına toplanır.

Benim önerim, koleksiyonlarınızı (veya fasulyeleri) yakından ilişkili diğer verilerle yeni sınıflara sarmak ve "yardımcı program" kodunu gerçek nesnelere koymaktır.

Koleksiyonu genişletebilir ve yöntemleri oraya ekleyebilirsiniz, ancak nesnenizin durumunun denetimini bu şekilde kaybedersiniz - bunu tamamen kapsüllemenizi ve yalnızca gerekli olan iş mantığı yöntemlerini ortaya çıkarmanızı öneririm (Düşün "Nesneleriniz için veri istemeyin, nesnelerinize komutlar verin ve onların özel verileri üzerinde hareket etmelerine izin verin ", önemli bir OO kiracısı ve bunu düşündüğünüzde burada önerdiğim yaklaşımı gerektirir).

Bu "sarma", verileri tutan herhangi bir genel amaçlı kütüphane nesnesi için kullanışlıdır, ancak kod ekleyemezsiniz. - Yönettiği verilerle kod koymak, OO tasarımındaki bir diğer önemli kavramdır.

Bu sarma kavramının kötü bir fikir olacağı birçok kodlama stili vardır - sadece bir kerelik bir senaryo veya test uygularken kim bir sınıf yazmak ister? Ancak, kendinize kod ekleyemediğiniz temel türlerin / koleksiyonların kapsüllenmesinin OO için zorunlu olduğunu düşünüyorum.


Bu cevap çok harika. (Açıkçası) bu sorunu vardı, bazı şüpheyle bu cevabı okuyun, geri gitti ve koduma baktı ve "bu yöntemleri olması gereken hangi nesne eksik" sordu. Bakın, zarif bir çözüm bulun ve nesne-model boşluğu doldurun.
GreenAsJade


@Deduplicator 40 yıllık programlamada nadiren (asla?) Çok fazla dolaylı seviyeyle engellendiğimi gördüm (bir arayüz yerine bir uygulamaya atlamak için IDE'm ile ara sıra kavga dışında) ama ben ' ve çoğu zaman (Çok Sıklıkla) insanların açıkça ihtiyaç duyulan bir sınıf yaratmaktan ziyade korkunç kod yazdığı durumu gördüm. Çok fazla dolaylamanın bazı insanları ısırmış olabileceğine inanmama rağmen, her sınıfın bir şeyi iyi yapması, uygun kontrol altına alma
Bill K

@BillK Genellikle iyi bir fikir. Ancak kaçış kapağı olmadan, kapsülleme gerçekten engel olabilir. Ve kesin algoritmalara rağmen OO oldukça garip olsa da, istediğiniz herhangi bir türle eşleştirebileceğiniz ücretsiz algoritmaların belirli bir güzelliği var.
Tekilleştirici

@Deduplicator 'Fayda' yazarken Belirli iş gereksinimlerinin dışında yazılması gereken işlevler doğrudur. İşlevlerle (koleksiyonlar gibi) dolu yardımcı program sınıfları, verilerle ilişkili olmadıkları için OO'da gariptir. bunlar, OO kullanımı konusunda rahat olmayan kişilerin (Escape aracıdır) (ayrıca, alet satıcılarının bunları çok genel işlevsellik için kullanmaları gerekir). Yukarıdaki önerim, iş mantığı üzerinde çalışırken kodlarınızı verilerinizle yeniden ilişkilendirebilmeniz için bu saldırıları sınıflara sarmaktı.
Bill K

1

Sağlayıcı kalıbı kullanabilir ve nesnenize FooSomethingbir FooUtilProvidernesne enjekte edebilirsiniz (bir kurucuda olabilir; sadece bir kez).

FooUtilProviderSadece bir yöntem olurdu: FooUtils get();. Daha sonra sınıf,FooUtils sağladığı örneği .

Şimdi, DI cointainer'ı sadece iki kez kablolamanız gerektiğinde, Bağımlılık Enjeksiyonu çerçevesini kullanmaktan bir adım uzakta: üretim kodu ve test paketiniz için. FooUtilsArayüzü sadece ya RealFooUtilsda birine MockedFooUtilsbağlarsınız ve gerisi otomatik olarak gerçekleşir. Her bağımlı nesne FooUtilsProviderdoğru sürümünü alır FooUtils.

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.