kaydedicinin parametre listesindeki konumu ne olmalıdır [kapalı]


12

Kodumda, yapıcılarının parametre listesi aracılığıyla sınıflarımın çoğuna bir logger enjekte ediyorum

Rastgele koyduğumu fark ettim: bazen listede ilk, bazen son ve bazen arasında

Herhangi bir tercihin var mı? Sezgim, bu durumda tutarlılığın yararlı olduğunu söylüyor ve kişisel tercihim ilk önce koymaktır, böylece eksik olduğunda fark edilmesi daha kolay ve orada atlamak daha kolay olacaktır.

Yanıtlar:


30

Kaydediciler "kesişen bir sorun" diyoruz. En Boy Odaklı Programlama; sınıflarınızı bir öznitelikle süslemek veya bazı kod dokumaları yapmak için bir yolunuz varsa, bu, nesnelerinizi ve parametre listelerinizi "saf" tutarken günlüğe kaydetme özelliklerini elde etmenin iyi bir yoludur.

Bir günlükçüye geçmek isteyebilmenizin tek nedeni, farklı günlük kaydı uygulamaları belirtmek isteyip istemediğinizdir, ancak çoğu günlükleme çerçevesi, bunları farklı günlükleme hedefleri için yapılandırmanıza izin verme esnekliğine sahiptir. (günlük dosyası, Windows Olay Yöneticisi vb.)

Bu nedenlerden dolayı, günlük tutma amacıyla her sınıfa bir günlük kaydedici aktarmak yerine, günlük kaydını sistemin doğal bir parçası yapmayı tercih ederim. Bu yüzden genellikle yaptığım uygun günlük ad alanına başvurmak ve sınıflarımdaki logger'ı kullanmak.

Hala bir kaydediciyi iletmek istiyorsanız, tercihim parametre listesindeki son parametreyi yapmak (mümkünse isteğe bağlı bir parametre yapmak). İlk parametre olması pek mantıklı değil; ilk parametre en önemlisi, sınıfın işleyişi ile en ilgili olan parametre olmalıdır.


11
+! günlükçü yapıcı parms dışında tutmak; herkesin aynı şeyi kullandığını biliyorum - statik bir logger tercih
Steven A. Lowe

1
@ StevenA.Lowe Dikkatli değilseniz, statik kaydedicilerin kullanılmasının diğer sorunlara yol açabileceğini unutmayın (örn. C ++ 'da statik başlatma siparişi fiyasko). Logger'ın küresel olarak erişilebilir bir varlık olarak sahip olmanın cazibesine sahip olduğunu kabul ediyorum, ancak böyle bir tasarımın genel mimariye uyup uymadığına ve nasıl uyduğuna dikkatle değerlendirilmelidir.
ComicSansMS

@ComicSansMS: tabii ki, artı iplik sorunları vb Sadece kişisel bir tercih - "mümkün olduğunca basit, ama daha basit değil";)
Steven A. Lowe

Statik bir kaydediciye sahip olmak bir sorun olabilir. Bağımlılık enjeksiyonunu zorlaştırır (DI kabınız tarafından başlatılan tek bir logger'ı kastediyorsanız) ve mimarinizi değiştirmek isterseniz ağrılı olabilir. Şu anda, örneğin, bir günlükçü her işlev yürütme için bir parametre olarak ileten Azure işlevlerini kullanıyorum, bu yüzden günlükçümü yapıcıdan geçirerek pişman oldum.
Slothario

@Slothario: Bu statik bir kaydedicinin güzelliği. Herhangi bir enjeksiyon gerekmez.
Robert Harvey

7

İşlev aşırı yüklemesi olan dillerde, bir argümanın isteğe bağlı olma olasılığının daha fazla olması gerektiğini savunuyorum. Bu, eksik oldukları yerde aşırı yükler oluşturduğunuzda tutarlılık yaratır:

foo(mandatory);
foo(mandatory, optional);
foo(mandatory, optional, evenMoreOptional);

İşlevsel dillerde, bunun tersi daha yararlıdır - bazı varsayılanları seçme olasılığınız arttıkça, daha fazla bırakılmalıdır. Bu, yalnızca bağımsız değişkenler uygulayarak işlevin özelleştirilmesini kolaylaştırır:

addThreeToList = map (+3)

Ancak diğer cevaplarda belirtildiği gibi, büyük olasılıkla sistemdeki her sınıfın argüman listesine kaydediciyi açıkça iletmek istemezsiniz.


3

Bu sorunu aşmak için kesinlikle çok zaman harcayabilirsiniz.

Standart günlük uygulamalarına sahip diller için, standart günlük kaydını her sınıfta doğrudan başlatmanız yeterlidir.

Kurallı bir uygulaması olmayan diller için, bir günlük cephesi çerçevesi bulmaya çalışın ve buna sadık kalın. slf4j Java için iyi bir seçimdir.

Şahsen tek bir somut günlük uygulamasına bağlı kalmayı ve her şeyi syslog'a göndermeyi tercih ederim. Tüm iyi günlük analizi araçları, birden çok uygulama sunucusundan gelen sysout günlüklerini kapsamlı bir rapora birleştirebilir.

Bir işlev imzası bir veya iki bağımlılık hizmetinin yanı sıra bazı "gerçek" bağımsız değişkenler içerdiğinde, bağımlılıkları en son yerleştiririm:

int calculateFooBarSum(int foo, int bar, IntegerSummationService svc)

Benim sistemleri sadece beş veya daha az tür hizmetleri sahip olma eğilimi beri, hep emin hizmetler tüm işlev imzalar arasında aynı sırayla dahildir olun. Alfabetik sıralamalar herhangi bir şey kadar iyidir. (Kenara: Muteks işleme için bu metodolojik yaklaşımı sürdürmek de kilitlenme geliştirme şansınızı azaltacaktır.)

Kendinizi uygulamanız boyunca bir düzineden fazla bağımlılık enjekte ederken bulursanız, sistemin muhtemelen ayrı alt sistemlere bölünmesi gerekir (mikro servisler demeye cesaret edebilir miyim?).


CalculateFooBarSum'u çağırmak için özellik enjeksiyonu kullanmak bana garip geliyor.
bir phu

2

Kaydediciler biraz özel bir durumdur çünkü tam anlamıyla her yerde mevcut olmaları gerekir.

Her sınıfın yapıcısına bir günlükçü iletmek istediğinize karar verdiyseniz, bunu nasıl yapacağınız için kesinlikle tutarlı bir kural belirlemelisiniz (örneğin, her zaman referansla her zaman ilk parametre, yapıcı başlatma listesi her zaman başlar) m_logger (theLogger) vb. ile). Kod tabanınızın tamamında kullanılacak olan her şey, bir gün tutarlılıktan yararlanacaktır.

Alternatif olarak, her sınıfın, herhangi bir şey geçirmeye gerek kalmadan kendi logger nesnesini başlatmasını sağlayabilirsiniz. Logger'ın çalışması için "sihirle" birkaç şey bilmesi gerekebilir, ancak sınıf tanımındaki bir dosya yolunu kodlamak potansiyel olarak bir yüzlerce farklı sınıfa doğru bir şekilde geçirmekten daha sürdürülebilir ve daha az sıkıcı ve söz konusu tediumu atlamak için küresel bir değişken kullanmaktan çok daha az kötü. (Kuşkusuz, kaydediciler küresel değişkenler için çok az yasal kullanım örneği arasındadır)


1

Logger'ın sınıfa geçmek yerine statik olarak erişilmesi gerektiğini önerenlere katılıyorum. Eğer bunu geçmek istiyorum güçlü bir neden varsa Ancak (belki farklı örnekleri farklı yerlerde falan oturum açmak istiyorum) o zaman yapıcı kullanarak geçemiyor öneririm ziyade, bu nedenle örneğin yapmak için ayrı bir arama yapmak Class* C = new C(); C->SetLogger(logger);yerine göreClass* C = new C(logger);

Bu yöntemi tercih etmenin nedeni, kaydedicinin esasen sınıfın bir parçası değil, başka bir amaçla kullanılan enjekte edilen bir özellik olmasıdır. Yapıcı listesine yerleştirmek onu sınıfın bir gereksinimi haline getirir ve sınıfın gerçek mantıksal durumunun bir parçası olduğunu ima eder. Beklemek mantıklıdır (örneğin çoğu sınıfları Hepsi olmamakla beraber birlikte), eğer X != Yo C(X) != C(Y)ama çok aynı sınıfın örneklerini karşılaştırarak eğer logger eşitsizliği test olacağını olası değildir.


1
Tabii ki bu, kaydedicinin yapıcı tarafından kullanılamaması dezavantajına sahiptir.
Ben Voigt

1
Bu cevabı gerçekten çok beğendim. Günlüğe kaydetmeyi, sınıfın yalnızca kullanmaktan ayrı olarak dikkat etmeniz gereken bir yan endişesi haline getirir. Büyük olasılıkla günlükçüyü çok sayıda sınıfın yapıcısına ekliyorsanız, muhtemelen bağımlılık enjeksiyonu kullanmanızdır. Tüm diller için konuşamıyorum, ancak C # 'da biliyorum, bazı DI uygulamaları da doğrudan özelliklere (getter / setter) enjekte edilecek .
jpmc26

@BenVoigt: Bu doğrudur ve bunu bu şekilde yapmamanın katil bir nedeni olabilir, ancak genellikle ayarlanan bir günlükçüye yanıt olarak kurucuda yapacağınız günlük kaydını yapabilirsiniz.
Jack Aidley

0

Bahsetmeye değer, diğer cevapların burada değinmediğini gördüğüm bir şey, kaydediciyi özellik veya statik yoluyla enjekte ederek, sınıfı test etmeyi zorlaştırıyor. Örneğin, günlükçünüzü özellik yoluyla enjekte ederseniz, artık günlükçüyü kullanan bir yöntemi her test ettiğinizde bu günlükçü enjekte etmeniz gerekir. Bu araçlar size de olabilir sınıfı bunu gerektirir çünkü bir yapıcı bağımlılık olarak ayarlar var.

Statik aynı konuya borç verir; kaydedici çalışmazsa, o zaman tüm sınıfınız başarısız olur (sınıfınız kaydediciyi kullanıyorsa) - kaydedici mutlaka sınıfın sorumluluğunun bir parçası olmasa da - özellik enjeksiyonu kadar kötü olmasa da, en azından kaydedicinin her zaman bir anlamda "orada" olduğunu bilin.

Düşünce için sadece yiyecek, özellikle de TDD kullanıyorsanız. Bence, bir kaydedici gerçekten bir sınıfın test edilebilir bir parçasının parçası olmamalıdır (sınıfı test ettiğinizde, günlüğünüzü de test etmemelisiniz).


1
hmmm ... böylece sınıfınızın günlük kaydı yapmasını istiyorsunuz (günlük kaydı belirtimde olmalıdır) ancak bir günlükçü kullanarak sınamak istemezsiniz. Mümkün mü? Sanýrým amacýn devam etmiyor. Test araçlarınız bozulursa test edemezsiniz - bir test aracına güvenmeyecek şekilde tasarım yapmak biraz aşırı mühendislik IMHO
Hogan

1
Demek istediğim, bir kurucu kullanır ve sınıf üzerinde bir yöntem çağırırsam ve hala bir özellik ayarlamadığım için başarısız olursa, o zaman sınıfın tasarımcısı bir kurucunun arkasındaki kavramı yanlış anlamış demektir. Sınıf tarafından bir günlükçü gerekiyorsa, yapıcıya enjekte edilmelidir - kurucu bunun için oradadır.
Dan Pantry

hımm .. hayır gerçekten değil. Günlük sistemini "çerçevenin" bir parçası olarak görürseniz, kurucunun bir parçası olarak bir anlam ifade etmez. Ancak bu, diğer cevaplarda açıkça belirtilmiştir.
Hogan

1
Mülkiyet enjeksiyonuna karşı tartışıyorum. Yapıcıya enjekte edilmesinin kullanımını savunmam gerekmiyor. Sadece bence, bu özellik enjeksiyonuna tercih edilir.
Dan Pantry

"Mümkün mü?" Ayrıca, evet, IL dokuma var ve üst cevapta bahsedilen bir şey ... mono-project.com/docs/tools+libraries/libraries/Mono.Cecil
Dan Pantry

0

Her sınıf örneğine bir logger nesnesi iletmek çok tembelim. Yani, benim kodumda, bu tür şeyler ya statik bir alanda ya da statik bir alanda yerel-yerel bir değişken oturur. İkincisi havalı ve her iş parçacığı için farklı bir günlükçü kullanmanıza izin verir ve çok iş parçacıklı bir uygulamada anlamlı ve beklenen bir şey yapan günlüğü açıp kapatmak için yöntemler eklemenize izin verir.

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.