Geç cevap ama dayanamıyorum.
X'in çoğu Y sınıfı için iyi mi yoksa bir anti-desen mi?
Çoğu durumda, düşünmeden uygulanan çoğu kural çoğunlukla korkunç bir şekilde yanlış gidecektir (bu kural dahil).
Size bir nesnenin doğuşu hakkında, tasarımla değil çaresizlikle gerçekleşen, doğru, hızlı ve kirli, prosedürel bir kodun kaosunun ortasında bir hikaye anlatayım.
Stajyerim ve ben hızlı bir şekilde bir web sayfasını kazımak için bazı atmak kodu oluşturmak için çift programlama vardır. Bu kodun uzun süre yaşayacağını düşünmek için kesinlikle bir nedenimiz yok, bu yüzden işe yarayan bir şey patlatıyoruz. Tüm sayfayı bir dize olarak alıyoruz ve ihtiyacımız olan şeyleri hayal edebileceğiniz en şaşırtıcı şekilde kırıyoruz. Yargılama. İşe yarıyor.
Şimdi bunu yaparken doğrama yapmak için bazı statik yöntemler yarattım. Stajyerim sizin gibi bir DTO sınıfı yarattı CatData
.
DTO'ya ilk baktığımda beni rahatsız etti. Java'nın beynime verdiği yıllar, beni kamuya açık alanlarda geri tepti. Ama C # 'da çalışıyorduk. C #, verileri değişmez hale getirme veya daha sonra kapsülleme yapma hakkınızı korumak için erken alıcılara ve ayarlayıcılara ihtiyaç duymaz. Arayüzü değiştirmeden istediğiniz zaman ekleyebilirsiniz. Belki sadece bir kesme noktası ayarlayabilirsiniz. Hepsi müşterilerinize bu konuda bir şey söylemeden. Evet C #. Boo Java.
Dilini tuttum. Kullanmadan önce bu şeyi başlatmak için statik yöntemlerimi kullandığını izledim. Yaklaşık 14 tanemiz vardı. Çirkin, ama umursamamamız için bir neden yoktu.
Sonra başka yerlerde ihtiyacımız vardı. Kendimizi kodu kopyalayıp yapıştırmak istediğimizi gördük. 14 başlangıç hattı fırlatıldı. Acı çekmeye başlamıştı. Tereddüt etti ve benden fikir istedi.
İsteksizce, "bir nesneyi düşünür müsünüz?" Diye sordum.
DTO'suna tekrar baktı ve yüzünü şaşkına çevirdi. "Bu bir nesne".
"Gerçek bir nesne demek istiyorum"
"Ha?"
"Size bir şey göstereyim. Yararlı olup olmadığına siz karar verin"
Yeni bir isim seçtim ve hızlı bir şekilde şuna benzer bir şey çırptım:
public class Cat{
CatData(string catPage) {
this.catPage = catPage
}
private readonly string catPage;
public string name() { return chop("name prefix", "name suffix"); }
public string weight() { return chop("weight prefix", "weight suffix"); }
public string image() { return chop("image prefix", "image suffix"); }
private string chop(string prefix, string suffix) {
int start = catPage.indexOf(prefix) + prefix.Length;
int end = catPage.indexOf(suffix);
int length = end - start;
return catPage.Substring(start, length);
}
}
Bu, statik yöntemlerin henüz yapmadığı hiçbir şey yapmadı. Ama şimdi 14 statik yöntemi üzerinde çalıştıkları verilerle yalnız kalabilecekleri bir sınıfa emdim.
Stajyeri kullanmaya zorlamadım. Sadece teklif ettim ve statik yöntemlere bağlı kalmak isteyip istemediğine karar verdim. Muhtemelen zaten çalıştığı şeye bağlı kalacağını düşünerek eve gittim. Ertesi gün onu bir sürü yerde kullandığını gördüm. Hala çirkin ve prosedürel olan kodun geri kalanını çözdü, ancak bu karmaşıklık artık bir nesnenin arkasında bizden gizlendi. Biraz daha iyiydi.
Şimdi buna her eriştiğinizde oldukça fazla iş yapıyor. DTO güzel bir hızlı önbellek değeridir. Bu konuda endişelendim ama biz hiç kullanarak kod herhangi bir dokunmadan ihtiyaç duyduysa önbellek ekleyebilirsiniz fark etti. Bu yüzden umursana kadar rahatsız etmeyeceğim.
DTO'lar üzerinde daima OO nesnelerine bağlı kalman gerektiğini mi söylüyorum? Hayır. DTO'nun sizi hareketli yöntemlerden koruyan bir sınırı geçmeniz gerektiğinde parlıyor. DTO'ların yeri var.
Ama OO nesneleri de öyle. Her iki aracı da nasıl kullanacağınızı öğrenin. Her birinin maliyetini öğrenin. Sorunun, durumun ve stajyerin karar vermesine izin vermeyi öğrenin. Dogma burada senin arkadaşın değil.
Cevabım zaten gülünç derecede uzun olduğundan, kodunuzu gözden geçirerek sizi bazı yanlış anlamaları devre dışı bırakmama izin verin.
Örneğin, bir sınıf genellikle sınıf üyelerine ve yöntemlerine sahiptir, örneğin:
public class Cat{
private String name;
private int weight;
private Image image;
public void printInfo(){
System.out.println("Name:"+this.name+",weight:"+this.weight);
}
public void draw(){
//some draw code which uses this.image
}
}
Yapımcınız nerede? Bu bana işe yarayıp yaramadığını bilmek için yeterli değil.
Ancak Tek sorumluluk ilkesi ve Açık kapalı ilkesi hakkında okuduktan sonra, bir sınıfı DTO'ya ve yardımcı sınıfı yalnızca statik yöntemlerle ayırmayı tercih ederim, örneğin:
public class CatData{
public String name;
public int weight;
public Image image;
}
public class CatMethods{
public static void printInfo(Cat cat){
System.out.println("Name:"+cat.name+",weight:"+cat.weight);
}
public static void draw(Cat cat){
//some draw code which uses cat.image
}
}
Tek sorumluluk ilkesine uyduğunu düşünüyorum çünkü artık CatData'nın sorumluluğu sadece verileri tutmak, yöntemleri umursamıyor (ayrıca CatMethods için).
Tek Sorumluluk İlkesi adına birçok aptalca şey yapabilirsiniz. Cat Strings ve Cat ints'ın ayrılması gerektiğini söyleyebilirim. Bu çizim yöntemlerinin ve Görseller'in kendi sınıfları olmalıdır. Çalışan programınızın tek bir sorumluluk olduğunu, bu yüzden sadece bir sınıfınız olmalıdır. : P
Bana göre, Tek Sorumluluk İlkesini izlemenin en iyi yolu, bir kutuya karmaşıklığı koymanıza izin veren iyi bir soyutlama bulmaktır. Eğer insanlara, içine baktıklarında buldukları şeylerden şaşmamak için iyi bir isim verebilirseniz, onu oldukça iyi takip ettiniz. Bunun daha fazla karar vermesini beklemek, o zaman sorun istiyor. Dürüst olmak gerekirse, kod listelemelerinizin her ikisi de bunu yapıyor, bu yüzden SRP'nin neden önemli olduğunu anlamıyorum.
Ve ayrıca açık kapalı prensibine de uyar, çünkü yeni yöntemler eklemek için CatData sınıfını değiştirmek gerekmez.
Peki hayır. Açık kapanış ilkesi yeni yöntemler eklemekle ilgili değildir. Eski yöntemlerin uygulanmasını değiştirmek ve hiçbir şeyi düzenlemek zorunda kalmakla ilgilidir. Eski yöntemlerinizi değil, sizi kullanan hiçbir şey yok. Bunun yerine başka bir yere yeni bir kod yazarsınız. Bir çeşit polimorfizm bunu güzel yapacak. Burada görmüyorum.
Sorum şu: İyi mi yoksa anti-desen mi?
Pekala, nasıl bilebilirim? Bak, her iki şekilde yapmanın faydaları ve maliyetleri var. Kodu verilerden ayırdığınızda, diğerini yeniden derlemek zorunda kalmadan değiştirebilirsiniz. Belki bu sizin için kritik öneme sahiptir. Belki de kodunuzu anlamsız bir şekilde karmaşık hale getirir.
Sizi daha iyi hissettirirse, Martin Fowler bir parametre nesnesi olarak adlandırılan bir şeyden çok uzak değilsiniz . Nesnenize sadece ilkelleri almak zorunda değilsiniz.
Yapmanızı istediğim şey, her iki kodlama stilinde de ayrılmanızın nasıl yapılacağına dair bir his geliştirmektir. Çünkü ister inanın ister inanmayın, bir stil seçmek zorunda değilsiniz. Sadece seçiminizle yaşamak zorundasınız.