İş nesnesi sınıfı tasarımın bu “tamamen halka açık” zihniyetine nasıl karşı çıkılır?


9

İş nesnelerimiz üzerinde çok sayıda birim testi ve yeniden düzenleme yapıyoruz ve sınıf tasarımı konusunda diğer akranlardan çok farklı görüşlere sahibim .

Ben hayranı değilim örnek bir sınıf:

public class Foo
{
    private string field1;
    private string field2;
    private string field3;
    private string field4;
    private string field5;

    public Foo() { }

    public Foo(string in1, string in2)
    {
        field1 = in1;
        field2 = in2;
    }

    public Foo(string in1, string in2, string in3, string in4)
    {
        field1 = in1;
        field2 = in2;
        field3 = in3;
    }

    public Prop1
    { get { return field1; } }
    { set { field1 = value; } }

    public Prop2
    { get { return field2; } }
    { set { field2 = value; } }

    public Prop3
    { get { return field3; } }
    { set { field3 = value; } }

    public Prop4
    { get { return field4; } }
    { set { field4 = value; } }

    public Prop5
    { get { return field5; } }
    { set { field5 = value; } }

}

"Gerçek" sınıfta, hepsi dize değildir, ancak bazı durumlarda tamamen herkese açık mülkler için 30 destek alanımız vardır.

Ben nefret Bu sınıf, ve ben sadece seçici davranıyorum eğer bilmiyorum. Dikkat edilmesi gereken birkaç nokta:

  • Özelliklerde mantığı olmayan özel destek alanları gereksiz görünüyor ve sınıfı şişiriyor
  • Birden fazla kurucu (biraz iyi) ancak
  • tüm özellikleri bir kamu ayarlayıcı sahip, ben bir hayran değilim.
  • Potansiyel olarak boş bir yapıcı nedeniyle hiçbir özelliğe değer atanmaz, bir arayan farkında değilse potansiyel olarak çok istenmeyen ve davranışlarını test etmek zor olabilir.
  • Çok fazla özellik var! (30 vakada)

Herhangi bir zamanda, bir uygulayıcı olarak nesnenin hangi durumda olduğunu gerçekten bilmek çok daha zor Foo. Argüman " Prop5nesne yapımı sırasında ayarlamak için gerekli bilgiye sahip olmayabiliriz . Tamam, sanırım bunu anlayabiliyorum, ama eğer durum buysa Prop5, her zaman bir sınıfta en fazla 30 mülkü değil, sadece ayarlayıcıyı herkese açık hale getirin .

"Yazması kolay (herkese açık)" olmanın aksine "kullanımı kolay" bir sınıf istemek için nit seçici ve / veya çılgın mıyım? Yukarıdaki gibi sınıflar bana çığlık atıyor, bunun nasıl kullanılacağını bilmiyorum, bu yüzden her ihtimale karşı her şeyi herkese açık hale getireceğim .

Çok seçici davranmıyorsam, bu tür düşüncelerle mücadele etmek için iyi argümanlar nelerdir? Tartışmaları açıklamakta çok iyi değilim, çünkü kastettiğim noktaya değinmeye çalışırken çok sinirliyim (kasıtlı olarak değil).



3
Bir nesne her zaman geçerli bir durumda olmalıdır; yani, kısmen somutlaşmış nesnelere sahip olmanız gerekmez . Bazı bilgiler bir nesnenin çekirdek durumunun bir parçası değildir, diğer bilgiler ise (örneğin, bir tablonun bacakları olmalıdır, ancak kumaş isteğe bağlıdır); isteğe bağlı bilgiler, örneklemeden sonra sağlanabilir, temel bilgiler örneklemede sağlanmalıdır. Bazı bilgiler temel ise ancak örneklemede bilinmiyorsa, sınıfların yeniden düzenlenmesi gerektiğini söyleyebilirim. Sınıfın içeriğini bilmeden daha fazlasını söylemek zor.
Marvin

1
"Nesne inşası sırasında Prop5'i ayarlamak için gerekli bilgiye sahip olamayabiliriz" diyerek bana soruyor "diyor, inşaat sırasında bu bilgilere sahip olacağınız bazı zamanlar ve olmayacağınız diğer zamanlarda bu bilgi var mı? " Bu sınıfın temsil etmeye çalıştığı iki farklı şey olup olmadığını ya da belki de bu sınıfın daha küçük sınıflara bölünmesi gerekip gerekmediğini merak ediyor. Ama eğer "her ihtimale karşı" yapılırsa bu da kötü, IMO; bana, nesnelerin nasıl yaratıldığı / doldurulduğuna dair net bir tasarım olmadığını söylüyor.
Dr.Wily's Apprentice

3
Özelliklerde mantığı olmayan özel yedekleme alanları için otomatik özellikleri kullanmamanızın bir nedeni var mı?
Carson63000

2
@Kritner, otomatik özelliklerin bütün mesele, gelecekte gerçek mantığa ihtiyacınız varsa, otomobili yerine sorunsuz bir şekilde ekleyebilmeniz getveya set:-)
Carson63000

Yanıtlar:


10

Tamamen kamu sınıfları, belirli durumlar için ve diğer uç noktalarda, sadece tek bir kamusal yöntemle (ve muhtemelen birçok özel yöntemle) bir gerekçeye sahiptir. Ve bazı kamuya açık, bazı özel yöntemlerle dersler.

Her şey onlarla modelleyeceğiniz soyutlama türüne, sisteminizde hangi katmanlara sahip olduğunuza, farklı katmanlarda ihtiyacınız olan kapsülleme derecesine ve (elbette) sınıfın yazarının hangi düşünce okulunun geldiğine bağlıdır. dan. Bu türlerin tümünü SOLID kodunda bulabilirsiniz.

Ne tür bir tasarımın ne zaman tercih edileceği hakkında yazılmış tüm kitaplar var, bu yüzden burada herhangi bir kural listelemeyeceğim, bu bölümdeki alan yeterli olmayacaktır. Bununla birlikte, bir sınıfla modellemek istediğiniz bir soyutlama için gerçek bir dünya örneğiniz varsa, eminim buradaki topluluk tasarımı geliştirmenize yardımcı olacaktır.

Diğer noktalarınızı ele almak için:

  • "özellikleri hiçbir mantık ile özel destek alanları": Evet, haklısın, önemsiz alıcılar ve ayarlayıcılar için bu sadece gereksiz "gürültü". Bu tür bir "şişkinlik "ten kaçınmak için, C # özellik alma / ayarlama yöntemleri için bir kısayol sözdizimine sahiptir:

Yani yerine

   private string field1;
   public string Prop1
   { get { return field1; } }
   { set { field1 = value; } }

yazmak

   public string Prop1 { get;set;}

veya

   public string Prop1 { get;private set;}
  • "Birden fazla kurucu": bu kendi başına bir sorun değildir. İçinde örneğinizde gösterildiği gibi gereksiz kod çoğaltması olduğunda veya arama hiyerarşisi kıvrıldığında bir sorun oluşur. Bu, ortak parçaları ayrı bir işleve yeniden düzenleyerek ve yapıcı zincirini tek yönlü bir şekilde düzenleyerek kolayca çözülebilir.

  • "Potansiyel olarak boş yapıcı nedeniyle hiçbir özelliğe değer atanmaz": C # 'da her veri tipinin açıkça tanımlanmış bir varsayılan değeri vardır. Özellikler bir kurucuda açıkça başlatılmazsa, bu varsayılan değeri atarlar. Bu kasıtlı olarak kullanılırsa , mükemmel olur - bu yüzden yazar ne yaptığını bilirse boş bir kurucu iyi olabilir.

  • "Çok fazla özellik! (30 vakada)": evet, böyle bir sınıfı yeşil alan olarak tasarlamakta özgürseniz, 30 çok fazla, katılıyorum. Ancak, hepimizin bu lüksü yoktur (aşağıdaki yorumda eski bir sistem mi yazmadınız?). Bazen mevcut bir veritabanındaki veya dosyadaki kayıtları veya verileri bir üçüncü taraf API'sından sisteminize eşlemeniz gerekir. Yani bu durumlar için 30 özellik kişinin yaşaması gereken bir şey olabilir.


Teşekkürler, evet, POCO / POJO'ların yerlerini aldıklarını biliyorum ve örneğim oldukça soyut. Ancak bir romana girmeden daha spesifik olabileceğimi sanmıyorum.
Kritner

@Kritner: benim düzenlememi gör
Doc Brown

evet genellikle yepyeni bir sınıf oluştururken otomatik mülkiyet yoluna giderim. Özel destek alanlarına sahip olmak "çünkü" (aklımda) gereksizdir ve hatta sorunlara neden olur. Bir sınıfa ve 30+ sınıfa ~ 30 mülkünüz olduğunda, bazı Özellikler ayarı veya yanlış destek alanından alacağınızı söylemek çok zor değil ... :)
Kritner

teşekkürler, dize için varsayılan değer nulldeğil mi? bu nedenle, a'da kullanılan özelliklerden biri .ToUpper(), ancak bunun için hiçbir değer ayarlanmadıysa, bir çalışma zamanı istisnası atar. Bu yüzden başka iyi bir örnektir gerekli sınıf için veri gerektiğini sahip nesne inşaat sırasında ayarlanmalıdır. Sadece kullanıcıya bırakmayın. THanks
Kritner

Çok fazla ayarlanmamış özelliğe ek olarak, bu sınıfla ilgili temel sorun sıfır mantığına (ZRP) sahip olması ve bir Anemik Model için bir arketip olmasıdır. DTO gibi kelimelerle ifade edilebilen buna gerek kalmadan, zayıf bir tasarımdır.
user949300

2
  1. "Özelliklerde mantığı olmayan özel destek alanları gereksiz görünüyor ve sınıfı şişiriyor" diyerek zaten iyi bir argümanınız var.

  2. Burada sorun birden fazla kurucu gibi görünmüyor.

  3. Tüm mülkleri herkese açık olarak görmeye gelince ... Belki bu şekilde düşünün, eğer iş parçacıkları arasındaki tüm bu özelliklere erişimi senkronize etmek isteseydiniz zor zamanınız olurdu çünkü her yerde senkronizasyon kodu yazmanız gerekebilir Kullanılmış. Ancak, tüm özellikler getters / setters tarafından çevrilmişse, senkronizasyonu sınıfa kolayca oluşturabilirsiniz.

  4. "Davranışı test etmek zor" derken argüman kendisi için konuşur. Testinizin boş olup olmadığını veya böyle bir şey olup olmadığını kontrol etmesi gerekebilir (mülkün kullanıldığı her bir yer). Gerçekten bilmiyorum çünkü testinizin / uygulamanızın neye benzediğini bilmiyorum.

  5. Çok fazla özelliğe sahipsin, miras kullanmayı deneyebilirsin (özellikler yeterince benziyorsa) ve sonra jenerikleri kullanarak bir liste / dizi var mı? Daha sonra bilgilere erişmek ve tüm özellikleri etkileşime girme şeklinizi basitleştirmek için alıcılar / ayarlayıcılar yazabilirsiniz.


1
Teşekkürler - # 1 ve # 4 Argümanımı yaparken kesinlikle en rahat hissediyorum. # 3'te ne demek istediğinizden tam olarak emin değilim. # 5 Sınıflardaki özelliklerin çoğu db üzerindeki birincil anahtarı oluşturur. Bazen 8 sütuna kadar rekabetçi anahtarlar kullanıyoruz - ama bu başka bir sorun. Çok sayıda özelliği (en azından bu sınıfta) ortadan kaldıracak kendi sınıfına / yapısına anahtarı oluşturan parçaları koymaya çalışmayı düşünüyordum - ancak bu, kodlu binlerce satırdan oluşan eski bir uygulamadır. Arayanların. Sanırım düşünecek çok şeyim var :)
Kritner

Eh. Sınıf değişmez olsaydı, sınıfın içine herhangi bir senkronizasyon kodu yazmak için bir neden olmazdı.
RubberDuck

@RubberDuck Bu sınıf değişmez mi? Ne demek istiyorsun?
Snoop

3
Kesinlikle var değil değişmez @StevieV. Özellik ayarlayıcıları olan herhangi bir sınıf değiştirilebilir.
RubberDuck

1
@Kritner Özellikler birincil anahtar olarak nasıl kullanılır? Bu muhtemelen bazı mantık (boş kontroller) veya kurallar gerektirir (örneğin, NAME AGE'ye göre önceliklidir). OOP'a göre, bu kod bu sınıfa aittir. Bunu bir argüman olarak kullanabilir misin?
user949300

2

Söylediğin gibi Foo, bir iş nesnesi. Alan adınıza göre yöntemlerde bazı davranışları kapsüllemeli ve verileri mümkün olduğunca kapsüllenmiş olarak kalmalıdır.

Gösterdiğiniz örnekte, Foodaha çok bir DTO'ya benziyor ve tüm OOP ilkelerine karşı çıkıyor (bkz. Anemik Alan Modeli )!

Iyileştirmeler olarak ben tavsiye ederim:

  • Bu sınıfı mümkün olduğunca değişmez yapın . Söylediğiniz gibi, bazı birim testleri yapmak istiyorsunuz. Bir birim test için zorunlu yeteneği determinizmdir ve çözer çünkü determinism değişmezlik ile elde edilebilir yan etkilerin sorun.
  • Bu tanrı sınıfını , her biri sadece 1 şey yapan birden fazla sınıfa ayırın (bakınız SRP ). Birim gerçekten bir PITA'da 30 nitelik sınıfını test eder .
  • Yalnızca 1 ana kurucuya ve ana kurucuyu çağıran diğerlerine sahip olarak kurucu artıklığını kaldırın .
  • Gereksiz alıcıları kaldırın, tüm alıcıların gerçekten yararlı olduğundan şüpheliyim.
  • İşletme sınıflarında iş mantığını geri getirin, işte orası ait!

Bu, şu ifadelerle potansiyel olarak yeniden düzenlenmiş bir sınıf olabilir:

public sealed class Foo
{
    private readonly string field1;
    private readonly string field2;
    private readonly string field3;
    private readonly string field4;

    public Foo() : this("default1", "default2")
    { }

    public Foo(string in1, string in2) : this(in1, in2, "default3", "default4")
    { }

    public Foo(string in1, string in2, string in3, string in4)
    {
        field1 = in1;
        field2 = in2;
        field3 = in3;
        field4 = in4;
    }

    //Methods with business logic

    //Really needed ?
    public Prop1
    { get; }

    //Really needed ?
    public Prop2
    { get; }

    //Really needed ?
    public Prop3
    { get; }

    //Really needed ?
    public Prop4
    { get; }

    //Really needed ?
    public Prop5
    { get; }
}

2

İş nesnesi sınıfı tasarımın bu “tamamen halka açık” zihniyetine nasıl karşı çıkılır?

  • "Müşteri sınıf (lar) ına" sadece DTO görevi "nden daha fazlasını yapan çeşitli yöntemler vardır. Bunlar birbirlerine aittir."
  • "Bu bir 'DTO' olabilir ama iş kimliği vardır - Eşittir'i geçersiz kılmamız gerekir."
  • "Bunları sıralamamız gerekiyor - IComparable'ı uygulamamız gerekiyor"
  • "Bu özelliklerin birkaçını bir araya getiriyoruz. Her istemcinin bu kodu yazmak zorunda kalmaması için ToString'i geçersiz kılalım."
  • "Bu sınıfa aynı şeyi yapan birden fazla istemcimiz var. Kodu KURUYORUZ."
  • "Müşteri bu şeylerin bir koleksiyonunu manipüle ediyor. Özel bir koleksiyon sınıfı olması gerektiği açıktır ve önceki noktaların birçoğu bu özel koleksiyonu şu anda olduğundan daha işlevsel hale getirecektir."
  • "Tüm sıkıcı, açıkta, dizi manipülasyonlarına bakın! İş ile ilgili yöntemlerde kodlama verimliliğimizi artıracak."
  • "Bu yöntemleri birlikte sınıfa çekmek onları test edilebilir yapacaktır çünkü daha karmaşık müşteri sınıfıyla uğraşmak zorunda değiliz."
  • "Şimdi bu yeniden düzenlenmiş yöntemleri birim test edebiliriz. Çünkü x, y, z nedenlerinden dolayı müşteri test edilemez.

  • Yukarıdaki argümanlardan herhangi biri, toplu olarak yapılabilir:

    • İşlevsellik yeniden kullanılabilir hale gelir.
    • Bu kuru
    • Müşterilerden ayrıldı.
    • sınıfa karşı kodlama daha hızlı ve daha az hataya açıktır.
  • "İyi yapılmış bir sınıf, durumu gizler ve işlevselliği ortaya koyar. Bu, OO sınıflarını tasarlamak için başlangıç ​​önerisidir."
  • "Nihai sonuç, iş alanını, farklı varlıkları ve açık etkileşimlerini ifade etmekten çok daha iyi bir iş çıkarıyor."
  • "Bu 'genel gider' işlevselliği (yani açık işlevsel gereksinimler değil, yapılması gerekiyor) açıkça öne çıkıyor ve Tek Sorumluluk İlkesine bağlı kalıyor.
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.