Kod kokusu, tasarımda böcek sayısını potansiyel olarak artıracak bir sorun olduğunu belirten bir belirtidir: bölgeler için durum böyle değildir, ancak bölgeler uzun yöntemler gibi kod kokusu oluşturmaya katkıda bulunabilir.
Dan beri:
Anti-patern (veya antipattern), sosyal veya iş operasyonlarında veya yazılım mühendisliğinde kullanılan, yaygın olarak kullanılabilen ancak pratikte etkisiz ve / veya verimsiz olan bir paterndir.
bölgeler olan , anti-desenler. Kodun kalitesini veya okunabilirliğini artırmayacak, hata sayısını azaltmayacak ve kodu yalnızca refactor için daha karmaşık hale getirebilecek daha fazla çalışmaya ihtiyaç duyarlar.
Yöntemlerin içindeki bölgeleri kullanmayın; refactor yerine
Yöntemler kısa olmalı . Bir yöntemde sadece on satır varsa, muhtemelen diğer beşte çalışırken beşini gizlemek için bölgeleri kullanmazsınız.
Ayrıca, her yöntemin bir ve bir tek şey yapması gerekir . Diğer taraftan bölgeler farklı şeyleri ayırmak için tasarlanmıştır . Metodunuz A, B ise, iki bölge oluşturmak mantıklıdır, ancak bu yanlış bir yaklaşımdır; bunun yerine, yöntemi iki ayrı yönteme yeniden uygulamalısınız.
Bu durumda bölgelerin kullanılması yeniden yapılanmayı daha da zorlaştırabilir. Bir hayaliniz olduğunu düşünün:
private void DoSomething()
{
var data = LoadData();
#region Work with database
var verification = VerifySomething();
if (!verification)
{
throw new DataCorruptedException();
}
Do(data);
DoSomethingElse(data);
#endregion
#region Audit
var auditEngine = InitializeAuditEngine();
auditEngine.Submit(data);
#endregion
}
Birinci bölgeye ikinciye konsantre olacak şekilde çökertmek sadece riskli değildir: akışı durdurma istisnasını kolayca unutabiliriz ( return
bunun yerine bir bekçi maddesi olabilir , bunun yerine tespit edilmesi daha da zor olabilir), fakat aynı zamanda bir sorunu olurdu Kodun bu şekilde yeniden yapılandırılması gerekiyorsa:
private void DoSomething()
{
var data = LoadData();
#region Work with database
var verification = VerifySomething();
var info = DoSomethingElse(data);
if (verification)
{
Do(data);
}
#endregion
#region Audit
var auditEngine = InitializeAuditEngine(info);
auditEngine.Submit(
verification ? new AcceptedDataAudit(data) : new CorruptedDataAudit(data));
#endregion
}
Artık bölgeler bir anlam ifade etmiyor ve ilk bölgedeki kodu görmeden ikinci bölgedeki kodu okuyamıyor ve anlayamıyorsunuz.
Bazen gördüğüm başka bir dava şudur:
public void DoSomething(string a, int b)
{
#region Validation of arguments
if (a == null)
{
throw new ArgumentNullException("a");
}
if (b <= 0)
{
throw new ArgumentOutOfScopeException("b", ...);
}
#endregion
#region Do real work
...
#endregion
}
Bağımsız değişkenler doğrulaması onlarca LOC'ye yayılmaya başladığında bölgeleri kullanmak cazip gelir, ancak bu sorunu çözmenin daha iyi bir yolu vardır: .NET Framework kaynak kodu tarafından kullanılan:
public void DoSomething(string a, int b)
{
if (a == null)
{
throw new ArgumentNullException("a");
}
if (b <= 0)
{
throw new ArgumentOutOfScopeException("b", ...);
}
InternalDoSomething(a, b);
}
private void InternalDoSomething(string a, int b)
{
...
}
Gruplandırmak için yöntemler dışındaki bölgeleri kullanmayın
Bazı insanlar alanları, özellikleri vb. Birlikte gruplamak için kullanırlar . Bu yaklaşım yanlıştır: kodunuz StyleCop uyumluysa, alanlar, özellikler, özel yöntemler, yapıcılar vb. Zaten bir araya getirilmiş ve bulmak kolaydır. Değilse, kod tabanınızda aynılığı sağlayacak kurallar uygulamayı düşünmeye başlamanın zamanı gelmiştir.
Diğer insanlar birçok benzer varlığı gizlemek için bölgeleri kullanır . Örneğin, yüz alanı olan (sınıfları ve boşlukları sayarsanız en az 500 satırlık kod yapan) bir sınıfınız olduğunda, bu alanları bir bölgeye koymak, daraltmak ve onları unutmak isteyebilirsiniz. Yine, bunu yanlış yapıyorsunuz: bir sınıftaki o kadar çok alanda, devralmayı kullanmayı veya nesneyi birkaç nesneye dilimlemeyi daha iyi düşünmelisiniz.
Son olarak, bazı insanlar bölgeleri ilgili şeyleri birlikte gruplamak için kullanmaya özen gösterirler: delegesiyle bir olay veya IO ile ilgili diğer yöntemlerle IO ile ilgili bir yöntem vb. İlk durumda, bakımı zor bir karmaşa olur. , oku ve anla. İkinci durumda, daha iyi tasarım muhtemelen birkaç sınıf oluşturmak olacaktır.
Bölgeler için iyi bir kullanım var mı?
Hayır. Eski bir kullanım vardı: oluşturulan kod. Yine de, kod oluşturma araçları bunun yerine kısmi sınıfları kullanmak zorunda. C # bölgeleri destekliyorsa, bunun nedeni çoğunlukla bu eski kullanımdır ve şimdi kodlarının çoğunda bölgeleri kullandığı için, varolan kod tabanlarını kırmadan bunları kaldırmak imkansız olacaktır.
Bunun hakkında düşün goto
. Dil veya IDE'nin bir özelliği desteklemesi, günlük kullanılması gerektiği anlamına gelmez. StyleCop SA1124 kuralı açık: bölgeleri kullanmamalısınız. Asla.
Örnekler
Şu anda iş arkadaşımın kodunu kod incelemesi yapıyorum. Kod temeli birçok bölge içerir ve aslında hem bölgelerin nasıl kullanılmayacağına hem de bölgelerin neden kötü kodlara yol açtığına mükemmel bir örnektir. İşte bazı örnekler:
4 000 LOC canavarı:
Son zamanlarda bir yerde Programmers.SE'yi okudum. Bir dosya çok fazla using
s içerdiğinde ("Kullanılmayan Kullanımları Kaldır" komutunu yürüttükten sonra), bu dosyanın içindeki sınıfın çok fazla çalıştığını gösteren bir işarettir. Aynısı dosyanın kendisi için de geçerlidir.
Kodu incelerken 4.000 LOC dosyası ile karşılaştım. Bu kodun yazarı, değişkenlerin adlarını ve çağrılan yöntemi hafifçe değiştirerek, aynı 15 satır yöntemini yüzlerce kez kopyalayıp yapıştırdığı ortaya çıktı. Basit bir regex sadece birkaç jenerik ekleyerek dosyayı 4.000 LOC'dan 500 LOC'ye kesmeye izin verdi; Daha akıllı bir yeniden düzenlemeyle, bu sınıfın birkaç düzine satıra indirgenebileceğinden eminim.
Yazar, bölgeleri kullanarak, kodun sürdürülmesinin ve kötü bir şekilde yazılmasının imkansız olduğu gerçeğini görmezden gelmeye ve yeniden yazmak yerine kodu ağır şekilde çoğaltmaya teşvik etti.
Bölge “Do A”, Bölge “Do B”:
Başka bir mükemmel örnek, basitçe görev 1, sonra görev 2, sonra görev 3, vb. Yapan bir canavar başlatma yöntemidir. Her biri bir konteyner sınıfında bir şeyi başlatan, tamamen bağımsız olan beş ya da altı görev vardı. Tüm bu görevler tek bir yöntemde gruplandı ve bölgelere göre gruplandı.
Bunun bir avantajı vardı:
- Metot bölge adlarına bakarak anlaşılması oldukça açıktı. Bu söylenirse, refactored bir kez aynı yöntem, orijinal kadar net olacaktır.
Öte yandan, meseleler çoktur:
Bölgeler arasında bağımlılıklar olup olmadığı belli değildi. Umarım, değişkenlerin yeniden kullanımı yoktu; Aksi takdirde, bakım daha da kabus olabilirdi.
Yöntem test etmek neredeyse imkansızdı. Aynı anda yirmi şeyi yapan yöntemin bunları doğru yapıp yapmadığını kolayca nasıl bilebilirsin?
Alanlar bölgesi, özellikler bölgesi, yapıcı bölge:
Gözden geçirilmiş kod aynı zamanda bütün alanları bir araya getiren tüm bölgeleri, bütün özellikleri birlikte vb. İçeren bir çok bölgeyi içeriyordu.
Bir dosyayı açtığınızda ve çok büyük bir alan listesi gördüğünüzde, önce sınıfı yeniden ele geçirmeye daha meyillidir, sonra kodla çalışırsınız. Bölgelerle, işleri çökmekten ve unutmayı alışkanlık haline getiriyorsunuz.
Başka bir sorun ise, her yerde yaparsanız, kendinizi mantıklı olmayan tek bloklu bölgeler yaratırken bulacaksınız. Bu aslında incelediğim kodda, #region Constructor
bir kurucu içeren çok fazla olan durumdu.
Son olarak, alanlar, özellikleri, vb kurucular zaten sırada olmalıdır . Eğer bunlar ve sözleşmelere uyuyorlarsa (büyük harfle başlayan sabitler vb.), Öğelerin tipinin nerede durduğu ve diğerlerinin başladığı açıktır, bu nedenle açıkça bunun için bölgeler oluşturmanız gerekmez.