Değiştirilemez nesneler için arayüzleri bildirmeyin


27

Değiştirilemez nesneler için arayüzleri bildirmeyin

[EDIT] Söz konusu nesnelerin Veri Aktarım Nesnelerini (DTO'lar) veya Eski Eski Verileri (POD) temsil ettiği yerlerde

Bu makul bir rehber mi?

Şimdiye kadar, çoğu zaman mühürlü sınıflar için değişmeyen arayüzler oluşturdum (veriler değiştirilemez). Değişkenliğe önem verdiğim yerde arayüzü kullanmamaya özen göstermeye çalıştım.

Ne yazık ki, arayüz kodu yıpratmaya başlar (ve endişelendiğim sadece benim kodum değil). Bir arabirimden geçirildikten sonra kuruyorsunuz ve daha sonra, kendisine iletilen şeyin değişmez olduğunu varsaymak isteyen bir koda geçmek istiyorsunuz.

Bu sorun nedeniyle, değişmez nesneler için hiçbir zaman arabirimler bildirmeyi düşünmüyorum.

Bunun, Birim Testine ilişkin sonuçları olabilir, fakat bunun dışında, bu makul bir kılavuz gibi görünüyor mu?

Yoksa gördüğüm "yayılma arayüzü" sorununu önlemek için kullanmam gereken başka bir örnek var mı?

(Bu değişmez nesneleri birkaç nedenden dolayı kullanıyorum: Temelde, çok iş parçacıklı kod yazdığım için iş parçacığı güvenliği için; ancak aynı zamanda, yöntemlere iletilen nesnelerin savunma kopyalarını almaktan kaçınabileceğim anlamına geliyor. Kod, çok daha basit hale geliyor Bir şey değişmez olduğunu bildiğiniz zaman birçok durumda - ki eğer bir arayüz kullandıysanız bunu yapmazsınız.Aslında, bir arayüz sağlamazsa, bir arayüz üzerinden referans verilen bir nesnenin savunma kopyasını bile yapamazsınız. klonlama işlemi veya seri hale getirmenin herhangi bir yolu ...)

[DÜZENLE]

Nesneleri değişmez yapmak isteme nedenlerim için çok daha fazla bağlam sağlamak için, Eric Lippert'in şu blog gönderisine bakın:

http://blogs.msdn.com/b/ericlippert/archive/tags/immutability/

Ayrıca burada, çok iş parçacıklı iş kuyruğlarında manipüle edilen / geçirilen öğeler gibi bazı alt düzey konseptlerle çalıştığımı da belirtmeliyim. Bunlar aslında DTO'lar.

Ayrıca Joshua Bloch , Etkili Java kitabında değişmez nesnelerin kullanılmasını önerir .


Takip et

Geri bildiriminiz için teşekkürler. Bu kılavuzu DTO'lar ve ilkleri için kullanmaya karar verdim. Şimdiye kadar iyi çalışıyor, ama sadece bir hafta oldu ... Yine de, iyi görünüyor.

Bununla ilgili sormak istediğim başka konular da var; özellikle "Derin veya Sığ Dalgısızlık" ("Derin ve Sığ klonlamadan çaldığım terminoloji") olarak adlandırdığım bir şey - ama bu başka bir zaman için bir soru.


3
+1 harika soru. Lütfen sizin için neden nesnenin yalnızca aynı arayüzü uygulayan bir şey yerine o belirli değişmez sınıfta olmasının önemli olduğunu açıklayın. Sorunlarınızın başka bir yerde olması olası. Bağımlılık Enjeksiyonu, ünite testi için son derece önemlidir, ancak kodunuzu belirli bir değişmez sınıfa zorlamanız gerektiğinde ortadan kaldıracak birçok önemli yararı vardır.
Steven Doggart

1
Değişmez terimini kullanımınızla biraz kafam karıştı . Açık olmak gerekirse, miras alınamaz ve geçersiz kılınamayan mühürlü bir sınıf mı demek istiyorsunuz, yoksa gösterdiği verilerin salt okunur olduğunu ve nesne oluşturulduktan sonra değiştirilemediğini mi kastediyorsunuz? Başka bir deyişle, nesnenin belirli bir tipte olduğunu veya değerinin asla değişmeyeceğini garanti etmek mi istiyorsunuz? İlk yorumumda, eskiyi kastettiğimi farz ettim, ancak şimdi düzenlemenizle, ikincisine daha çok benziyor. Yoksa ikisiyle de mi ilgileniyorsun?
Steven Doggart

3
Kafam karıştı, neden ayarlayıcıları arabirimden ayırmıyorsun? Ama öte yandan, gerçekten etki alanı olan nesneler ve / veya bu nesneler için fabrika gerektiren DTO'lar için arayüzleri görmekten yoruldum ... benim için bilişsel uyumsuzluğa neden oluyor.
Michael Brown

2
Ne sınıflar için arabirimler hakkında tüm değişmez? Örneğin, Java'nın Numara sınıf biri tanımlamanızı sağlar List<Number>tutabilir ki Integer, Float, Long, BigDecimal, vb ... Bütün bunlar kendilerini değişken değildir.

1
@MikeBrown Ben sadece arayüzü değişmezliği ima çünkü uygulamanın onu zorladığı anlamına gelmez sanırım. Sözleşmeye bırakabilirsin (yani değişmezliğin bir gereklilik olduğu arabirimini belgeleyebilirsin), ancak birisi sözleşmeyi ihlal ederse, gerçekten kötü bazı sorunlara neden olabilirsiniz.
vaughandroid

Yanıtlar:


17

Benim düşünceme göre, kuralınız iyi bir kuraldır (veya en azından kötü bir kural değildir), ancak yalnızca tanımladığınız durum nedeniyle. Her durumda aynı fikirdeyim diyemem, bu yüzden, içsel soydaşımın bakış açısından, kuralınızın teknik olarak çok geniş olduğunu söylemek zorunda kalacağım.

Genellikle değişmez nesneleri tanımlamazsınız, çünkü bunlar aslında veri aktarım nesneleri (DTO) olarak kullanılmazlarsa, bu, veri özellikleri içerdikleri, ancak çok az mantık ve bağımlılık olmadığı anlamına gelir. Bu durumda, göründüğü gibi, beton türlerini doğrudan arabirimler yerine kullanmanın güvenli olduğunu söyleyebilirim.

Eminim ki aynı fikirde olmayan bazı birim testi uzmanları olacak, ama bence DTO sınıfları birim testi ve bağımlılık enjeksiyon gereksinimlerinden güvenle dışlanabilir. Bir DTO oluşturmak için bir fabrika kullanmaya gerek yoktur, çünkü bağımlılığı yoktur. Her şey DTO'ları gerektiği gibi doğrudan yaratıyorsa, o zaman zaten farklı bir tip enjekte etmenin bir yolu yoktur, bu nedenle bir arayüze gerek yoktur. Ve hiç mantık içermediklerinden, birim testine gerek yok. Bağımlılığı olmadığı sürece, bazı mantıklar içerse bile, mantığın birim testinde yapılması önemsiz olmalıdır.

Bu nedenle, tüm DTO sınıflarının bir arayüz uygulamayacağına dair bir kural koymanın, potansiyel olarak gereksiz olsa da, yazılım tasarımınıza zarar vermeyeceğini düşünüyorum. Verilerin değişmez olması gerektiğine ilişkin bir gereksiniminiz olduğundan ve bunu bir arayüz aracılığıyla uygulayamazsanız, o zaman bu kuralı kodlama standardı olarak tanımlamanın tamamen meşru olduğunu söyleyebilirim.

Yine de daha büyük olan konu, temiz bir DTO katmanını kesin olarak uygulamanın gerekliliğidir. Arayüzsüz değişmez sınıflarınız yalnızca DTO katmanında mevcut olduğu ve DTO katmanınız mantık ve bağımlılıklardan arınmış olduğu sürece, güvende olursunuz. Yine de katmanlarınızı karıştırmaya başlarsanız ve işletme katmanı sınıfları olarak ikiye katlanan, arayüzsüz sınıflara sahipseniz, daha fazla sorun yaşamaya başlayacağınızı düşünüyorum.


Bir DTO veya değişmez değer nesnesiyle özel olarak ilgilenmeyi bekleyen kod, bu tipin bir arabirimin özellikleri yerine özelliklerini kullanmalıdır, ancak bu, böyle bir sınıf tarafından uygulanan bir arabirim için kullanım durumları olmayacağı anlamına gelmez. diğer sınıflar tarafından, asgari bir süre için geçerli olacak değerleri döndürmeyi vaat eden yöntemlerle (örneğin, eğer arayüz bir fonksiyona geçirilirse, bu fonksiyon geri dönene kadar metodlar geçerli değerler döndürmelidir). Bu tür bir arabirim, benzer verileri ve bir dilekleri içine alan çeşitli türler varsa yardımcı olabilir ...
supercat

... veriyi birinden diğerine kopyalamak için ... Belirli verileri içeren tüm türler onu okumak için aynı arayüzü kullanıyorsa, o zaman bu tür veriler, her birinin diğerinden nasıl içe aktarılacağını bilmek zorunda kalmadan, tüm türler arasında kolayca kopyalanabilir.
supercat

Bu noktada, alıcılarınızı (ve DTO'larınız aptalca bir sebepten dolayı değişebilirse ayarlayıcıları) ortadan kaldırabilir ve alanları halka açık hale getirebilirsiniz. Sen ediyoruz asla , orada hakkını herhangi mantığı koyacaktım?
Kevin,

@Kevin Sanırım, ancak otomatik özelliklerin modern rahatlığı sayesinde, bunları özellik yapmak çok kolaydır, neden olmasın? Performansın ve verimliliğin, belki de önemli olan şeyden daha fazla endişe duyduğunu varsayalım. Her durumda, soru şu: DTO'da hangi seviyede "mantık" a izin veriyorsunuz? Özelliklerine bir doğrulama mantığı koymuş olsanız bile, alay gerektirecek bir bağımlılığı olmadığı sürece, ünite testi yapmak zor olmaz. DTO, herhangi bir bağımlılık iş nesnesini kullanmadığı sürece, içinde test edilebilecekleri için, içine biraz mantık koymak güvenlidir.
Steven Doggart

Şahsen, onları olabildiğince temiz tutmayı tercih ederim, ancak kuralların bükülmesinin gerekli olduğu zamanlar her zaman vardır ve bu yüzden kapıyı açık olması için açık tutmak daha iyidir.
Steven Doggart

7

Kodumu kötüye kullanmaya karşı savunmasız bırakma konusunda büyük bir karışıklık çıkardım. Mutasyona uğramış üyeleri gizlemek için salt okunur arayüzler yaptım, genel imzalarıma vb. Çok fazla kısıtlama ekledim, vb. "Belki bir gün yeni bir giriş seviyesi adam işe alırlar ve XYZ sınıfının DTO ABC'yi güncelleyemediğini bilmez. Oh hayır!" Diğer zamanlarda yanlış soruna odaklanıyordum - açık çözümü görmezden geliyor - ormanı ağaçların arasından göremiyordum.

Artık DTO'larım için hiçbir zaman arayüzler oluşturmam. Koduma dokunan insanların (öncelikle kendim) neyin izin verildiğini ve neyin mantıklı olduğunu bildiği varsayımı altında çalışıyorum. Aynı aptal hatayı yapmaya devam edersem, genellikle arayüzlerimi sertleştirmeye çalışmam. Şimdi zamanımın çoğunu neden aynı hatayı yapmaya devam ettiğimi anlamaya çalışmakla geçiriyorum. Genelde bir şeyi fazla analiz etmem ya da anahtar bir konsepti kaybetmemden kaynaklanıyor. Paranoyak olmaktan vazgeçtiğimden beri kodumla çalışmak çok daha kolaydı. Ayrıca, içeriden öğrenenlerin sistem üzerinde çalışması için gereken bilgileri gerektiren daha az sayıda “çerçeve” ile de karşılaşıyorum.

Sonuç, işe yarayan en basit şeyi bulmak oldu. Güvenli arayüzler oluşturmanın ek karmaşıklığı geliştirme zamanını boşa harcar ve basit kodu zorlaştırır. Kütüphanelerinizi kullanan 10.000 geliştiriciniz olduğunda bunun gibi şeylerden endişe edin. İnan bana, seni bir sürü gereksiz gerginlikten koruyacak.


Genellikle birim testi için bağımlılık enjeksiyonuna ihtiyaç duyduğumda arayüzleri kaydederim.
Travis Parks

5

İyi bir rehber gibi görünüyor, ama garip nedenlerden dolayı. Bir arabirimin (veya soyut temel sınıfın) bir dizi değişmez nesneye eşit erişim sağladığı bir çok yere sahiptim. Stratejiler buraya düşme eğilimindedir. Devlet objeleri buraya düşme eğilimindedir. Bir arayüzü şekillendirmenin değişmez görünmesi ve API'nizde olduğu gibi belgelenmesi için makul olmadığını düşünüyorum.

Bununla birlikte, insanlar Plain Old Data (bundan sonra POD) nesnelerinin ve hatta basit (çoğu zaman değişmez) yapıların aşırı arayüzlenmesi eğilimindedir. Kodunuzda bazı temel yapıların mantıklı alternatifleri yoksa, bir arayüze ihtiyacı yoktur. Hayır, birim testi, tasarımınızı değiştirmek için yeterli bir neden değildir (veritabanı erişimini alay etmek, bunun için bir arayüz sağlamanın nedeni değildir, gelecekteki değişim için esnekliktir) - testlerinizde dünyanın sonu değil. bu temel temel yapıyı olduğu gibi kullanın.


2

Bir şey değişmez olduğunu bildiğiniz zaman birçok durumda kod çok daha basit hale gelir - ki eğer bir arayüz kullandıysanız, bunu yapmazsınız.

Bunun erişimci uygulayıcının endişelenmesi gereken bir endişe olduğunu sanmıyorum. Eğer interface Xdeğişmez olması gerekiyorsa, arayüzü değiştirilemez bir şekilde uyguladıklarından emin olmak için arayüz uygulayıcısının sorumluluğu değil mi?

Ancak, aklımda değişmez bir arayüz diye bir şey yoktur - bir arayüz tarafından belirtilen kod sözleşmesi yalnızca bir nesnenin açıkta kalan yöntemleri için geçerlidir ve bir nesnenin içindekiler hakkında hiçbir şey uygulanmaz.

Bir arayüzden ziyade bir dekoratör olarak uygulanan değişmezliği görmek çok daha yaygındır, ancak bu çözümün uygulanabilirliği gerçekten objenizin yapısına ve uygulamanızın karmaşıklığına bağlıdır.


1
Bir dekoratör olarak değişmezliği uygulama örneği verebilir misiniz?
vaughandroid

Önemsiz değil, sanmıyorum ama bu oldukça basit bir düşünce alıştırması - devleti değiştiren yöntemler ve devleti döndüren yöntemler varsa MutableObject, ndevleti mdöndüren ImmutableDecoratoryöntemleri ortaya koymaya devam edebilir ( m) ve çevreye bağlı olarak iddia veya değiştirilebilir yöntemlerden biri çağrıldığında bir istisna atmak.
Jonathan Rich,

Derleme zamanı kesinliğini çalışma zamanı istisnalarının olasılığına dönüştürmekten gerçekten hoşlanmıyorum ...
Matthew Watson

Tamam, ama ImmutableDecorator verilen bir yöntemin durumu değiştirip değiştirmeyeceğini nasıl bilebilir?
vaughandroid

Arayüzünüzü uygulayan bir sınıfın, arayüzünüz tarafından tanımlanan yöntemlerle ilgili olarak değişmez olup olmadığından emin olamazsınız. @Baqueta Dekoratör baz sınıfın uygulaması hakkında bilgi sahibi olmalıdır.
Jonathan Rich

0

Bir arabirimden geçirildikten sonra kuruyorsunuz ve daha sonra, kendisine iletilen şeyin değişmez olduğunu varsaymak isteyen bir koda geçmek istiyorsunuz.

C # 'da, "değişmez" bir tipte bir nesne bekleyen bir yöntem, bir arabirim tipinde bir parametreye sahip olmamalıdır, çünkü C # arabirimleri değişmezlik sözleşmesini belirleyemez. Bu nedenle, önerdiğiniz kılavuz C # 'da anlamsızdır, çünkü ilk etapta yapamazsınız (ve dillerin gelecekteki sürümleri bunu yapmanıza olanak vermez).

Sorunuz Eric Lippert'in değişmezlik konusundaki makalelerinin ince bir yanlış anlaşılmasından kaynaklanıyor. Eric arayüzleri tanımlamamış IStack<T>ve IQueue<T>değişmeyen yığınlar ve sıralar için sözleşmeler belirtmemiştir. Yapmazlar. Onları kolaylık sağlamak için tanımladı. Bu arayüzler, boş istifler ve sıralar için farklı tipler tanımlamasını sağladı. Boş yığını temsil etmek için bir arabirim veya ayrı bir tip gerektirmeden tek bir tip kullanan değişken bir yığının farklı bir tasarımını ve uygulanmasını önerebiliriz , ancak ortaya çıkan kod temiz görünmeyecek ve biraz daha az verimli olacaktır.

Şimdi Eric'in tasarımına devam edelim. Değişmez bir yığın gerektiren bir yöntem , genel anlamda bir yığın soyut veri tipini temsil eden Stack<T>genel arayüz yerine tür parametresine sahip olmalıdır IStack<T>. Eric'in değişmez yığınını kullanırken bunun nasıl yapılacağı belli değil ve bunu makalelerinde tartışmadı, ancak mümkün. Sorun boş yığının türündedir. Asla boş bir yığın almadığınızdan emin olarak bu sorunu çözebilirsiniz. Bu, yığındaki ilk değer olarak kukla bir değere basılarak ve asla açılmadan sağlanabilir. Bu şekilde, güvenli bir şekilde sonuçlarını yayın yapabilirsiniz Pushve Popkarşı Stack<T>.

Having Stack<T>uygular IStack<T>yararlı olabilir. Bir yığın gerektiren yöntemleri, mutlaka değişmez bir yığın değil, herhangi bir yığın tanımlayabilirsiniz. Bu yöntemlerin bir tür parametresi olabilir IStack<T>. Bu, ona değişmez yığınları iletmenizi sağlar. İdeal olarak, IStack<T>standart kütüphanenin bir parçası olacaktır. .NET'te, yoktur IStack<T>, ancak değişmez yığının uygulayabileceği, türü daha kullanışlı hale getiren başka standart arabirimler de vardır.

Daha önce bahsettiğim değişmez yığının alternatif tasarımı ve uygulaması, adı verilen bir arayüz kullanır IImmutableStack<T>. Tabii ki, arayüz adına "Immutable" eklemek onu uygulayan her tip değiştirilemez yapmaz. Ancak, bu arayüzde, değişmezlik sözleşmesi sadece sözlüdür. İyi bir geliştirici buna saygı göstermelidir.

Küçük, dahili bir kütüphane geliştiriyorsanız, bu sözleşmeyi kabul etmek için ekipteki herkesle aynı fikirdeyebilir ve IImmutableStack<T>parametre türü olarak kullanabilirsiniz . Aksi takdirde, arayüz türünü kullanmamalısınız.

C # sorusunu etiketlediğinizden, C # şartnamesinde DTO ve POD diye bir şey olmadığını eklemek isterim. Bu nedenle, onları atmak veya kesin olarak tanımlamak soruyu iyileştirir.

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.