Tanım olarak C # 'da soyut sınıfı kullanın


21

Bir C ++ geliştiricisi olarak C ++ başlık dosyalarına oldukça alışkınım ve kodun içinde bir tür zorunlu "dokümantasyon" bulundurmanın yararlı olduğunu düşünüyorum. Bu nedenle bazı C # kodlarını okumak zorunda kaldığımda genellikle kötü zaman geçiriyorum: Çalıştığım sınıfın zihinsel haritasına sahip değilim.

Bir yazılım mühendisi olarak bir programın çerçevesini tasarladığımı varsayalım. Her sınıfı, C ++ başlıklarıyla yaptığımıza benzer şekilde soyut, uygulanmamış bir sınıf olarak tanımlamak ve geliştiricilerin uygulamayı uygulamasına izin vermek çok çılgınca olur mu?

Birisinin bunu korkunç bir çözüm olarak bulabilmesinin bazı nedenleri olabileceğini tahmin ediyorum ama neden olduğundan emin değilim. Böyle bir çözüm için dikkate alınması gerekenler nelerdir?


13
C #, C ++ 'dan tamamen farklı bir programlama dilidir. Bazı sözdizimi benzer görünüyor, ancak iyi çalışabilmek için C # 'yi anlamanız gerekiyor. Ve başlık dosyalarına güvenerek sahip olduğunuz kötü alışkanlık hakkında bir şeyler yapmalısınız. On yıllardır C ++ ile çalıştım, başlık dosyalarını hiç okumadım, sadece onları yazdım.
Bent

17
C # ve Java'nın en iyi yanlarından biri başlık dosyalarından kurtulma özgürlüğüdür! Sadece iyi bir IDE kullanın.
Erik Eidt

9
C ++ üstbilgi dosyalarındaki bildirimler oluşturulan ikili dosyaya dahil değildir. Derleyici ve bağlayıcı için oradalar. Bu C # soyut sınıfları hiçbir şekilde fayda sağlamayacak şekilde oluşturulan kodun bir parçası olacaktır.
Mark Benningfield

16
C # 'daki eşdeğer yapı soyut bir sınıf değil bir arayüzdür.
Robert Harvey,

6
@DaniBarcaCasafont "Başlıkları, bir sınıf yönteminin ve özniteliğinin ne olduğunu, hangi parametre türlerini beklediğini ve ne döndüreceğini görmek için hızlı ve güvenilir bir yol olarak görüyorum". Visual Studio'yu kullanırken benim alternatifim Ctrl-MO kısayolu.
wha7ever - Monica

Yanıtlar:


44

Bunun C ++ 'da yapılmasının sebebi, derleyicilerin daha hızlı ve daha kolay uygulanmasını sağlamaktı. Bu, programlamayı kolaylaştıran bir tasarım değildi .

Üstbilgi dosyalarının amacı, derleyicinin beklenen tüm işlev adlarını bilmek için süper hızlı bir ilk geçiş yapmasını sağlamak ve cp dosyalarında çağrıldığında, bunları tanımlayan sınıf olsa bile başvuruda bulunabilmeleri için bellek konumlarını tahsis etmesini sağlamaktı. henüz ayrıştırılmadı.

Modern bir geliştirme ortamında eski donanım sınırlamalarının bir sonucunu çoğaltmaya çalışmak önerilmez!

Her sınıf için bir arayüz veya soyut bir sınıf tanımlamak verimliliğinizi düşürecektir; O zaman başka ne yapabilirdin ? Ayrıca, diğer geliştiriciler bu sözleşmeye uymayacak.

Aslında, diğer geliştiriciler soyut sınıflarınızı silebilir. Bu kriterlerin her ikisini de karşılayan kodda bir arayüz bulursam, silerim ve kodları yeniden düzenlerim:1. Does not conform to the interface segregation principle 2. Only has one class that inherits from it

Başka bir şey de, Visual Studio'da bulunan ve otomatik olarak başarmayı hedeflediğiniz şeyi yapan araçlar var:

  1. Class View
  2. Object Browser
  3. In Solution Explorersize görevlerini, parametrelerini ve dönüş türlerini görmek için sınıfları sarf etmek üçgenleri de tıklayabilirsiniz.
  4. Dosya sekmelerinin altında üç küçük açılır menü vardır, en sağı geçerli sınıfın tüm üyelerini listeler.

Yukarıdakilerden birini C # ile C ++ üstbilgi dosyalarını çoğaltmak için zaman ayırmadan önce deneyin.


Dahası, bunu yapmamak için teknik nedenler var ... bu, son ikili kodunuzu olması gerekenden daha büyük yapacak. Bu yorumu Mark Benningfield tarafından tekrarlayacağım:

C ++ üstbilgi dosyalarındaki bildirimler oluşturulan ikili dosyaya dahil değildir. Derleyici ve bağlayıcı için oradalar. Bu C # soyut sınıfları hiçbir şekilde fayda sağlamayacak şekilde oluşturulan kodun bir parçası olacaktır.


Ayrıca, Robert Harvey tarafından teknik olarak C # 'daki bir başlığın en yakın karşılığı, soyut bir sınıf değil, bir arayüz olacaktır.


2
Ayrıca pek çok gereksiz sanal gönderi çağrısı ile de karşılaşırsınız (kodunuz performans açısından duyarlıysa iyi olmaz).
Lucas Trzesniewski

5
burada belirtilen Arayüz Ayrıştırma İlkesi .
NH.

2
Bir de kuş görmek için tüm sınıfı (sağ tıkla -> Anahat -> Tanımlara Daralt ') daraltabilir.
jpmc26

"O zaman başka ne yapabilirdin" -> Uhm, kopyala ve yapıştır ve çok küçük postwork? Neyse, yaklaşık 3 saniye sürüyor. Tabii ki, o zaman sigarayı yuvarlamak için hazırlık yaparken bir filtre ucu çıkarmak için kullanabilirdim. Gerçekten önemli bir nokta değil. [Feragatname: Hem idiomatik C # hem de idiomatik C ++ 'ı ve biraz daha fazla dili
seviyorum

1
@phresnel: Bir sınıfın tanımlarını tekrarlamaya çalıştığınızı ve tekrarladığınızı görmek ve orijinal kuşu, soyut bir kuşun, 3'lü, hatasız bir şekilde, kolay bir kuşbakışı bakış açısına sahip olmayacak kadar büyük olan herhangi bir sınıf için miras aldığını görmek isterim. o (OP'nin önerdiği kullanım durumu: sözde karmaşık olan diğer sınıfları karmaşık hale getiriyor). Neredeyse doğası gereği, orijinal sınıfın karmaşık doğası, soyut sınıf tanımını sadece kalbinizle yazamayacağınız anlamına gelir (çünkü bunu zaten kalpten bildiğiniz anlamına gelir) Ve sonra bunu, bütün bir kod tabanı için yapmak için gereken zamanı düşünün .
Flater

14

İlk olarak, tamamen soyut bir sınıfın gerçekten sadece çoklu miras yapamayan bir arayüz olduğunu anlayın.

Yazma sınıfı, arayüz özü, beyin ölümü aktivitesidir. Öyle ki, bunun için bir yeniden düzenleme yapmamız gerekiyor. Bu üzücü. Bunu takiben “her sınıf bir arayüz elde eder” deseni sadece karışıklık yaratmaz, aynı zamanda noktayı tamamen özlüyor.

Arayüz, sınıfın ne yapabildiğine dair resmi bir açıklama olarak düşünülmemelidir. Bir arabirim, gereksinimlerini ayrıntılarıyla anlatan müşteri kodu kullanılarak uygulanan bir sözleşme olarak düşünülmelidir.

Şu anda sadece bir sınıfı uygulayan bir arayüz yazarken hiçbir sorunum yok. Aslında henüz hiçbir sınıfın uygulamaması umurumda değil. Çünkü kullandığım kodun neye ihtiyacı olduğunu düşünüyorum. Arayüz, kullanım kodunun ne istediğini ifade eder. Sonradan gelenler, bu beklentileri karşıladığı sürece, istediklerini yapabilir.

Şimdi bunu bir nesnenin diğerini kullandığı her zaman yapmam. Bunu bir sınırı geçerken yapıyorum. Bir nesnenin tam olarak hangi nesneyle konuştuğunu bilmesini istemediğimde yapıyorum. Polimorfizmin çalışacağı tek yol budur. Müşteri kodumun değişmesi muhtemel olduğunu düşündüğüm nesneyi beklerim. Kullandığım şey String sınıfı olduğunda kesinlikle bunu yapmam. String sınıfı hoş ve istikrarlı ve benden değişime karşı korunmaya ihtiyacım yok.

Bir soyutlamadan ziyade doğrudan somut bir uygulamayla etkileşime girmeye karar verdiğinizde, uygulamanın değişmemeye güvenecek kadar istikrarlı olduğunu öngörürsünüz.

İşte bu doğru Bağımlılık İnversiyon Prensibi'ni benimseme yöntemim var . Bunu kör bir şekilde fanatik olarak her şeye uygulamamalısın. Bir soyutlama eklediğinizde, projenin ömrü boyunca istikrarlı olması için uygulama sınıfı seçimine güvenmediğinizi söylüyorsunuz.

Bunların hepsi Açık Kapalı Prensibi takip etmeye çalıştığınızı varsayar . Bu ilke, yalnızca belirlenmiş koda doğrudan değişiklik yapmakla ilişkili maliyetler önemli olduğunda önemlidir. İnsanların ayrılma nesnelerinin ne kadar önemli olduğu konusunda aynı fikirde olmamalarının ana nedenlerinden biri, doğrudan değişiklikler yaparken herkesin aynı masrafları yaşamamasıdır. Kod tabanınızın tamamını yeniden test etmek, yeniden derlemek ve yeniden dağıtmak sizin için önemsiz ise, doğrudan değişiklikle bir değişiklik ihtiyacını çözmeniz bu sorunun çok çekici bir şekilde basitleştirilmesi olabilir.

Bu soruya basit bir beyin ölümü cevabı yok. Bir arayüz veya soyut bir sınıf, her sınıfa ekleyeceğiniz bir şey değildir ve sadece uygulama sınıflarının sayısını sayamaz ve gerekmediğine karar veremezsiniz. Değişimle baş etmekle ilgili. Bu, geleceği tahmin ettiğiniz anlamına gelir. Yanlış yaparsan şaşırma. Kendinizi bir köşeye sokmadan olabildiğince basit tutun.

Bu yüzden lütfen sadece kodu okumamıza yardımcı olacak soyutlamalar yazmayın. Bunun için araçlarımız var. Ayrılması gerekenleri ayırmak için soyutlamalar kullanın.


5

Evet bu korkunç olurdu çünkü (1) Gereksiz kod getirdi (2) Okuyucunun kafasını karıştırır.

C # programlamak istiyorsanız, sadece C # okumaya alışmalısınız. Başkaları tarafından yazılan kod bu modeli yine de takip etmeyecektir.


1

Birim testleri

Kodları arabirimler veya soyut sınıflarla karıştırmak yerine Ünite testleri yazmanızı şiddetle tavsiye ederim (başka nedenlerle garanti altına alınmış olanlar hariç).

İyi yazılmış bir ünite testi sadece sınıfınızın arayüzünü değil (bir başlık dosyası, soyut sınıf veya arayüzün yapacağı gibi) değil, aynı zamanda istenen işlevselliği de açıklar.

Örnek: myclass.h başlık dosyasını şöyle yazmış olabilirsiniz:

class MyClass
{
public:
  void foo();
};

Bunun yerine, c # dilinde şöyle bir test yazın:

[TestClass]
public class MyClassTests
{
    [TestMethod]
    public void MyClass_should_have_method_Foo()
    {
        //Arrange
        var myClass = new MyClass();
        //Act
        myClass.Foo();
        //Verify
        Assert.Inconclusive("TODO: Write a more detailed test");
    }
}

Bu çok basit test, başlık dosyasıyla aynı bilgileri iletir. ("Foo" parametresiz işlevli "Sınıfım" adında bir sınıfımız olmalıdır.) Başlık dosyası daha küçükken, test çok daha fazla bilgi içerir.

Bir ihtar: böyle bir üst düzey yazılım mühendisi tedariki (başarısız), diğer geliştiricilerin TDD gibi metodolojilerle çatışmaları şiddetli bir şekilde çözme testlerini yapma süreci, ancak sizin durumunuzda çok büyük bir gelişme olacaktır.


Birim Testi (izole edilmiş testler) = Arayüzsüz, ihtiyaç duyulan mocks nasıl yapılır? Hangi çerçevenin gerçek bir uygulamadan alay etmeyi desteklediğini gördüm, çoğu gördüğüm gibi alay uygulamasıyla uygulamayı değiştirmek için bir arayüz kullanıyor.
Bulan

OP: amaçları için, zorunlu olarak alaylara ihtiyaç olduğunu sanmıyorum. Unutmayın, bu TDD için araç olarak birim testleri değil, başlık dosyalarının yerine birim testidir. (Elbette alaylarla vb. İle "olağan" birim testlerine dönüşebilirler)
Guran

Tamam, sadece OP'nin yanını düşünürsem, daha iyi anlarım. Cevabınızı daha genel bir başvuru cevabı olarak okuyun. Netleştirmek için teşekkürler!
Bulan

@Bulan'da yaygın alaycılık ihtiyacı çoğu zaman kötü bir tasarımın göstergesidir
TheCatWhisperer

@ TheCatWhisperer Evet, bunun farkındayım, bu yüzden hiçbir tartışma yok, bu ifadeyi herhangi bir yerde de yaptığımı göremiyorum: DI genel olarak testten bahsediyordu, kullandığım tüm Mock-framework takas için Arayüzleri kullanıyor Asıl uygulamayı ve eğer arayüzleriniz yoksa, nasıl alay etmeye gittiğinizle ilgili başka bir teknik varsa.
Bulan
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.