Programlama kuralında tutarlılık tercih edilmeli midir?


14

Bir sınıf tasarlarken ortak programlama pratiğine göre davranış tutarlılığı tercih edilmelidir mi? Belirli bir örnek vermek gerekirse:

Yaygın bir kural şudur: Bir sınıfın bir nesnesi varsa (örneğin, onu yarattıysa), tamamlandıktan sonra onu temizlemekten sorumludur. .NET'te belirli bir örnek, sınıfınız bir IDisposablenesneye sahipse nesneyi ömrünün sonunda atması gerektiğidir. Ve eğer ona sahip değilseniz, dokunmayın.

Şimdi .NET'teki StreamWritersınıfa bakarsak, belgelendirmede, kapalı / atıldığında temel akışı kapattığını bulabiliriz. Bu, StreamWriteryazar temel dosya akışını oluşturduğundan ve bu nedenle onu kapatması gerektiğinde bir dosya adının iletilmesiyle başlatılması durumunda gereklidir . Bununla birlikte, yazarın da kapattığı harici bir akıştan geçebilir.

Bu beni birçok kez rahatsız etti (evet, kapamayan bir sarıcı yapabileceğinizi biliyorum ama bu nokta değil), ancak görünüşe göre Microsoft, nereden olursa olsun akışı her zaman kapatmanın daha tutarlı olduğuna karar verdi.

Sınıflarımdan birinde böyle bir desenle karşılaştığımda, genellikle yapıcı aracılığıyla enjekte edilen ownsFooBardurumlarda yanlış olarak ayarlanan FooBarve aksi takdirde doğru olan bir bayrak oluştururum . Bu şekilde, temizlik görevini, örneği açıkça geçtiğinde arayan kişiye iletir.

Şimdi merak ediyorum belki de tutarlılık en iyi uygulamadan yana mı olmalı (ya da belki de en iyi uygulamam o kadar iyi değil)? Buna karşı / ona karşı argüman var mı?

Açıklama için düzenleyin

"Tutarlılık" ile demek istediğim: Sınıfın tutarlı davranışı her zaman yalnızca bir nesne oluşturduysanız veya sahipliğini açıkça aktardıysanız, bir nesnenin sahipliğini almak için her zaman sahiplik alan (ve akışı kapatma) ve "en iyi uygulama" ya karşıdır.

Enoying olduğu bir örneğe gelince:

Bazı verileri oluşturmak ve işlemek gibi, onunla bir şeyler yapmak için bir akışı kabul eden iki belirli sınıfınız (bazı 3. taraf kütüphanelerinden) olduğunu varsayın:

 public class DataProcessor
 {
     public Result ProcessData(Stream input)
     {
          using (var reader = new StreamReader(input))
          {
              ...
          }
     }
 }

 public class DataSource
 {
     public void GetData(Stream output)
     {
          using (var writer = new StreamWriter(output))
          {
               ....
          }
     }
 }

Şimdi şöyle kullanmak istiyorum:

 Result ProcessSomething(DataSource source)
 {
      var processor = new DataProcessor();
      ...
      var ms = new MemoryStream();
      source.GetData(ms);
      return processor.ProcessData(ms);
 }

Bu Cannot access a closed stream, veri işlemcide bir istisna dışında başarısız olur . Biraz yapılıdır ama konuyu açıklamalıdır. Bunu düzeltmenin çeşitli yolları var ama yine de yapmamam gereken bir şey üzerinde çalıştığımı hissediyorum.


StreamWriter'ın resimli davranışı neden acı çekmenize neden olur? Aynı akışı başka bir yerde kullanmak ister misiniz? Neden?
Meslek

@Job: Sorumu açıkladım
ChrisWue

Yanıtlar:


9

Ben de bu tekniği kullanıyorum, buna 'handoff' diyorum ve bir modelin durumunu hak ettiğine inanıyorum. Bir nesne A tek kullanımlık B nesnesini bir inşaat süresi parametresi olarak kabul ettiğinde, 'handoff' adı verilen (varsayılanı false değerindedir) bir boole kabul eder ve eğer doğruysa A kaskadlarının atılması için B imha edilir.

Diğer insanların talihsiz seçimlerini çoğaltmaktan yana değilim, bu yüzden Microsoft'un kötü uygulamasını asla yerleşik bir sözleşme olarak kabul etmem, ne de Microsoft'un talihsiz seçimlerini takip etmenin başkalarının körü körüne herhangi bir şekilde, şekil veya form olarak "tutarlı davranışı" olduğunu düşünmem. .


Not: Blogumdaki bir yazıda bu kalıbın resmi bir tanımını yazdım: michael.gr: "Handoff" Paterni .
Mike Nakis

Desenin başka bir uzantısı olarak, handOffyapıcıda böyle bir özellik ayarlanırsa, ancak onu değiştirmek için başka bir yöntem varsa, bu diğer yöntem de bir handOffbayrağı kabul etmelidir . Eğer handOffhalen tutulan öğe için belirtilen, yerleştirilmekte olmalı, daha sonra yeni öğe kabul edilmeli ve iç handOffbayrak yeni öğenin durumunu yansıtacak şekilde güncellenir. Yöntemin eski ve yeni referansları eşitlik açısından kontrol etmesi gerekmez, çünkü orijinal referans "teslim edildi" ise, orijinal arayan tarafından tutulan tüm referanslar terk edilmelidir.
supercat

@supercat Katılıyorum, ancak son cümleyle ilgili bir sorunum var: şu anda elde tutulan ürün için el atma doğruysa, ancak yeni tedarik edilen ürün, şu anda tutulan öğeye referans olarak eşitse, şu anda tutulan öğeyi atıyorsa aslında yeni tedarik edilen öğenin atılmasıdır. Aynı öğenin tekrar tedarik edilmesinin yasadışı olması gerektiğini düşünüyorum.
Mike Nakis

Nesne değişmezse, gerçekte herhangi bir kaynak tutmuyorsa ve elden çıkarılıp takılmadığı önemli değilse, aynı öğenin tekrar tedarik edilmesi genellikle yasa dışı olmalıdır. Örneğin, Disposesistem fırçaları için istekler dikkate alınmazsa, bir "color.CreateBrush` yöntemi boş zamanlarında yeni bir fırça oluşturabilir (atılmasını gerektirir) veya bir sistem fırçasını (atılmasını yoksayar) geri getirebilir. imha etmeyi önemsiyorsa bir nesnenin tekrar kullanılması, ancak
atılmaması

3

Dürüst olmak gerekirse haklı olduğunu düşünüyorum. Microsoft'un özellikle açıkladığınız nedenlerle StreamWriter sınıfıyla uğraştığını düşünüyorum.

Ancak, o zamandan beri insanların kendi Akışını atmaya çalışmadığı bir sürü kod gördüm, çünkü çerçeve onlar için yapacak, bu yüzden şimdi düzeltmek faydadan daha fazla zarar verecektir.


2

Tutarlılıkla giderdim. Çoğu insana bahsettiğiniz gibi, nesneniz bir alt nesne oluşturuyorsa, nesnenin kendisine ait olması ve onu yok etmesi mantıklıdır. Eğer dışarıdan bir referans verilirse, bir zamanlar "dışarıda" da aynı nesneyi tutar ve ona ait olmamalıdır. Sahipliği dışarıdan nesnenize aktarmak istiyorsanız, Ekle / Çıkar yöntemlerini veya sahipliğin aktarıldığını açıkça belirten bir boole kabul eden bir yapıcı kullanın.

Tam bir cevap için tüm bunları yazdıktan sonra, çoğu genel tasarım deseninin mutlaka StreamWriter / StreamReader sınıflarıyla aynı kategoriye girmeyeceğini düşünüyorum. Her zaman MS'in StreamWriter / Reader'ın otomatik olarak sahipliği devralmasını seçmesinin nedeninin, bu özel durumda, paylaşılan mülkiyete sahip olmanın çok mantıklı olmamasıdır. Aynı akıştan aynı anda okuma / yazma için iki farklı nesneniz olamaz. Eminim bir çeşit deterministik zaman payı yazabilirsiniz, ama bu bir anti-desen cehennemi olurdu. Muhtemelen dediler ki, bir akışı paylaşmak hiç mantıklı olmadığından, onu her zaman ele alalım. Ancak bu davranışın tüm sınıflar için genel bir kural haline geldiğini düşünmüyorum.

Akışları, aktarılan nesnenin tasarım açısından paylaşılamadığı ve dolayısıyla herhangi bir ek bayrak olmadan, okuyucu / yazarın sahipliğini devraldığı tutarlı bir model olduğunu düşünebilirsiniz.


Sorumu açıklığa kavuştum - tutarlılıkla her zaman sahiplik alma davranışı demek istedim.
ChrisWue

2

Dikkatli ol. "Tutarlılık" olarak düşündüğünüz şey rastgele bir alışkanlık koleksiyonu olabilir.

"Kullanıcı arayüzlerinin tutarlılık için koordine edilmesi", SIGCHI Bulletin 20, (1989), 63-65. Üzgünüm, bağlantı yok, eski bir makale. “... 15 uzmandan oluşan iki günlük bir atölye çalışması tutarlılık tanımı üretemedi.” Evet, "arayüz" ile ilgili, ama bir süre "tutarlılık" düşünürseniz, onu da tanımlayamayacağınıza inanıyorum.


haklısınız, uzmanlar farklı çevrelerden bir araya gelirse, ancak kod geliştirirken, ekibimin zaten aşina olduğu mevcut bazı kalıpları (veya eğer isterseniz alışkanlığı) takip etmeyi kolay buldum. Örneğin, geçen gün adamlarımızdan biri, sahip olduğumuz bazı eşzamansız işlemleri soruyordu ve ona Win32 Çakışan G / Ç ile aynı şekilde davrandıklarını söylediğimde, 30 saniye içinde tüm arayüzü anladı ... bu durumda her ikisi de ben ve onu aynı sunucu arka uç Win32 arka plandan geldi. Tutarlılık ftw! :)
DXM

@ Brruce Anladım - Tutarlılıkla ne demek istediğimin net olduğunu düşündüm ama görünüşe göre değildi.
ChrisWue
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.