Temiz Kod neden korunan değişkenlerden kaçınmayı öneriyor?


168

Temiz Kod , "Biçimlendirme" bölümünün "Dikey Mesafe" bölümünde korumalı değişkenlerden kaçınılmasını önerir:

Yakından ilgili olan kavramlar birbirine dikey olarak yakın tutulmalıdır. Açıkçası bu kural, ayrı dosyalara ait kavramlar için işe yaramaz. Ancak, çok iyi bir nedeniniz olmadıkça, yakından ilgili kavramlar farklı dosyalara ayrılmamalıdır. Gerçekten de, korunan değişkenlerden kaçınılması gereken nedenlerden biri budur .

Sebep nedir?


7
ihtiyacınız olduğunu düşündüğünüz bir örneği gösterin
gnat

63
O kitapta müjde olarak fazla kabul etmem.
Rig

40
@Rig, bu kısımlardaki küfür üzerine, böyle bir yorum ile sınır kuşatıyorsunuz.
Ryathal

40
Ben bununla iyiyim. Kitabı okudum ve bu konuda kendi fikrim olabilir.
Rig

10
Kitabı okudum ve orada olanların çoğuyla aynı fikirde olmama rağmen, onun da İncil olduğunu düşündüğümü söyleyemem. Her durumun farklı olduğunu düşünüyorum .. ve kitapta belirtilen kurallara tam olarak uymak birçok proje için oldukça zor.
Simon Whitehead

Yanıtlar:


277

Korunan değişkenlerden kaçınılmalıdır, çünkü:

  1. YAGNI sorunlarına yol açma eğilimindedirler . Korumalı üyeyle bir şeyler yapan bir soy sınıfınız yoksa, onu özel yapın.
  2. LSP sorunlarına yol açma eğilimindedirler . Korunan değişkenler genellikle kendileriyle ilişkili bazı içsel değişmezliğe sahiptir (veya bunlar genel olur). Mirasçıların daha sonra insanların mahvedeceği veya isteyerek ihlal edebileceği bu özellikleri sürdürmesi gerekir.
  3. OCP'yi ihlal etme eğilimindedirler . Temel sınıf, korunan üye hakkında çok fazla varsayımda bulunursa veya mirasçı, sınıfın davranışı konusunda çok esnekse, temel sınıfın davranışının bu uzantı tarafından değiştirilmesine yol açabilir.
  4. Kompozisyondan ziyade genişleme mirasına yol açma eğilimindedirler. Bu, daha sıkı eşleşmelere, daha fazla SRP ihlaline , daha zor testlere ve “mirasa ilişkin iyilik kompozisyonu” tartışmasında yer alan bir takım başka şeylere yol açma eğilimindedir .

Ama gördüğünüz gibi, bunların hepsi 'eğilimindedir'. Bazen korunan bir üye en şık çözümdür. Korunan işlevler bu sorunlardan daha azına sahip olma eğilimindedir. Ancak onlara özenle bakılmalarına neden olan birçok şey vardır. Bu tür bakım gerektiren herhangi bir şeyle, insanlar hatalar yapar ve programlama dünyasında hatalar ve tasarım sorunları anlamına gelir.


Çok iyi nedenlerden dolayı teşekkürler. Ancak kitap, özellikle biçimlendirme ve dikey mesafe bağlamında bahsetmektedir, merak ettiğim kısım budur.
Matsemann

2
Görünüşe göre Bob Amca, birileri aynı sınıfta değil, sınıflar arasında korunan bir üyeye atıfta bulunurken dikey mesafeye atıfta bulunuyor .
Robert Harvey

5
@Matsemann - tabi. Kitabı doğru hatırlıyorsam, bu bölüm kodun okunabilirliğine ve keşfedilmesine odaklanır. Değişkenler ve fonksiyonlar bir kavram üzerinde çalışıyorsa kodlara yakın olmaları gerekir. Korumalı üyeler, tamamen farklı bir dosyada olduklarından, korumalı üyeye yakın olması gerekmeyen türetilmiş türler tarafından kullanılacaktır.
Telastyn

2
@ steve314 ben temel sınıf ve bir senaryo düşünemiyorum tüm onun mirasçılardan sadece hiç bir dosya yaşayacak. Bir örnek olmadan, mirasın kötüye kullanılması veya kötü soyutlama olduğunu düşünmeye meyilliyim.
Telastyn

3
YAGNI = İhtiyacınız Olmayacaksınız LSP = Liskov Değiştirme Prensibi OCP = Aç / Kapat Prensibi SRP = Tek Sorumluluk Prensibi
Bryan Glazer

54

Bu, küresellerden kaçınmanızın aynı nedeni, sadece daha küçük bir ölçekte. Bunun nedeni, her değişkeni bir değişkenin okunduğu ya da daha kötüsü, yazılması ve kullanımı tutarlı kılmak zor olmasıdır. Kullanım yazılmış olması gibi sınırlı ise bir türetilmiş sınıftaki bariz bir yerde ve okumak bir taban sınıftaki bariz bir yerde, ardından korumalı değişkenler kod daha açık ve özlü yapabilirsiniz. Birden fazla dosya boyunca willy-nilly değişkenini okumaya ve yazmaya özen gösterirseniz, onu kapsüllemek daha iyidir.


26

Kitabı okumamıştım, ama Bob Amca'nın kastettiği şeyi bıçaklayabiliyorum.

Bir protectedşey giyerseniz, bu bir sınıfın onu devralabileceği anlamına gelir. Ancak üye değişkenlerin içinde bulundukları sınıfa ait olmaları gerekir; bu, temel kapsüllemenin bir parçasıdır. protectedBir üye değişkenine koymak , artık türetilmiş bir sınıfın temel sınıfın uygulama ayrıntılarına erişebildiği için kapsüllemeyi keser. publicSıradan bir sınıfta bir değişken yaptığınızda ortaya çıkan aynı sorun .

Sorunu gidermek için, değişkeni korumalı bir özelliğe kapatabilirsiniz, şöyle:

protected string Name
{
    get { return name; }
    private set { name = value; }
}

Bu name, temel sınıfın uygulama ayrıntılarını göstermeden, yapıcı bir argüman kullanarak türetilmiş bir sınıftan güvenli bir şekilde ayarlanmasını sağlar .


Korumalı setter ile ortak bir mülk yarattınız. Korunan alan, özel ayarlayıcı ile korunan bir mülk ile çözülmelidir.
Andy,

2
Şimdi +1. Setter de sanırım korunmuş olabilir, ancak yeni değer geçerli olsun veya olmasın, tabanın kimliğini zorlayabilir.
Andy,

Sadece karşıt bir bakış açısı sunmak için - Bütün değişkenlerimi temel sınıfta korumalı yapıyorum. Eğer sadece ortak bir arayüzden erişilebiliyorsa, o zaman türetilmiş bir sınıf olmamalı, sadece bir baseclass nesnesi içermelidir. Ama aynı zamanda 2 sınıftan derin hiyerarşilerden de kaçınıyorum
Martin Beckett

2
Bir yoktur kocaman arasındaki fark protectedve publicüyeleri. BaseTypeBir kamu üyesini ortaya çıkarması durumunda , bu, türetilmiş tüm türlerin aynı üyenin aynı şekilde çalışması gerektiğini ifade eder, çünkü bir DerivedTypeörnek BaseTypereferans alan ve bu üyeyi kullanmayı umduğu bir koda geçirilebilir . Bunun tersine, BaseTypebir açığa protectedüyesi, o DerivedTypebu üyeyi erişmek için bekleyebilir base, ama basebir şey olamaz başka bir daha BaseType.
Supercat

18

Bob Amca'nın argümanı temelde mesafelerden biri: Eğer sınıf için önemli bir kavramınız varsa, kavramı o dosyadaki sınıfla birlikte paketleyin. Diskteki iki dosya arasında ayrılmış değil.

Korunan üye değişkenleri iki yerde dağılmış ve sihir gibi gözüküyor. Bu değişkeni referans, henüz nerede ... burada tanımlanmamış olan bu tarif? Ve böylece av başlar. Tamamıyla önlemek için daha iyi protected, onun argümanı.

Şimdi kuralın her zaman mektuba uyması gerektiğine inanmıyorum (mesela: kullanmazsın protected). Neyin elde edildiğinin ruhuna bakın ... ilgili şeyleri tek bir dosyada bir araya getirin - bunu yapmak için programlama tekniklerini ve özelliklerini kullanın. Fazla analiz etmemenizi ve bu konudaki ayrıntılara yetişmemenizi tavsiye ederim.


9

Zaten burada çok iyi cevaplar var. Ama eklemek için bir şey:

Çoğu modern OO dilinde (Java AFAIK'te gerekli olan) her bir sınıfı kendi dosyasına koymak iyi bir alışkanlıktır.

Bu nedenle, fonksiyonların kaynak kodunda "dikey olarak yakın" bir temel sınıfın korumalı değişkenine göre değişken tanımına göre türetilmiş bir sınıfta tutulması neredeyse imkansızdır. Ancak ideal olarak orada tutulmaları gerekir çünkü korunan değişkenler, genellikle "yakından ilgili bir kavram" olan işlevlerini kendilerine ayıran dahili kullanım için tasarlanmıştır.


1
Swift'de değil. İlginç bir şekilde, Swift “korumalı” değil, “korumalı” ve C ++ “arkadaşının” yerini alan “özel” olmuştur.
gnasher729

@ gnasher729 - tabii ki, Swift mirasında bir sürü Smalltalk var. Smalltalk özel değişkenler ve ortak yöntemlerden başka bir şeye sahip değildir. Smalltalk'ta özel ve özel: aynı sınıftan bile iki nesne birbirinin değişkenlerine erişemez.
Jules

4

Temel fikir, korumalı olarak ilan edilen bir "alan" (örnek seviye değişkeni) muhtemelen olması gerekenden daha görünür, ve isteyebileceğinizden daha az "korumalı" olmasıdır. C / C ++ / Java / C # öğesinde "yalnızca aynı derlemedeki alt sınıflar tarafından erişilebilir" ifadesiyle eşdeğer bir erişim değiştiricisi yoktur, bu nedenle size derlemenizdeki alana erişebilecek kendi çocuklarınızı tanımlama olanağı sağlar, ancak diğer meclislerde yaratılan çocukların aynı erişime izin vermemesi; C #, dahili ve korumalı değiştiricilere sahiptir, ancak bunları birleştirmek, erişimi "dahili veya korumalı" değil "iç veya korumalı" yapar. Öyleyse, korunan bir alana, o çocuğu ya da başkasını yazdıysanız, herhangi bir çocuk tarafından erişilebilir. Korumalı böylece bir hacker için açık bir kapıdır.

Ayrıca, tanımlarına göre alanlar, onları değiştirmek için doğal olan hiçbir doğrulamaya sahip değildir. C # 'da bir tane yapabilir, bu değer türlerini etkili bir şekilde sabit hale getirir ve referans türleri yeniden başlatılamaz (ama yine de çok değişken). Bu nedenle, korumalı bile olsa, çocuklarınız (güvenemeyeceğiniz) bu alana erişebilir ve nesnenin durumunu tutarsız hale getirerek (kaçınılması gereken bir şey), onu geçersiz bir şeye ayarlayabilir.

Alanlarla çalışmanın kabul edilen yolu, onları özel kılmak ve bir özelliğe ve / veya alıcı ve ayarlayıcı yöntemine erişmek. Sınıftaki tüm tüketicilerin değere ihtiyacı varsa, alıcıyı (en azından) herkese açık yapın. Sadece çocuklar ihtiyaç duyuyorsa, alıcıyı koruyun.

Soruyu cevaplayan bir başka yaklaşım kendinize sormak; neden bir çocuk yöntemindeki kod, durum verilerimi doğrudan değiştirme yeteneğine ihtiyaç duyuyor? Bu kod hakkında ne diyor? Bu, yüzündeki "dikey mesafe" argümanıdır. Bir çocukta doğrudan ebeveyn durumunu değiştirmesi gereken bir kod varsa, o zaman belki de bu kod ilk olarak ebeveyne ait olmalıdır?


4

Bu ilginç bir tartışma.

Dürüst olmak gerekirse, iyi araçlar bu "dikey" sorunların çoğunu azaltabilir. Aslında, bence “dosya” kavramı aslında yazılım geliştirmeyi engelleyen bir şeydir - Işık Masası projesinin üzerinde çalıştığı bir şey (http://www.chris-granger.com/2012/04/12/light- tablo --- a-yeni-ide-kavram /).

Resimden dosyaları çıkarın ve korumalı kapsam çok daha çekici hale gelir. Korumalı konsept, SmallTalk gibi dillerde en belirgin hale gelir - herhangi bir miras, bir sınıfın orijinal kavramını genişletir. Her şeyi yapmalı ve ana sınıfın yaptığı her şeye sahip olmalıdır.

Bence sınıf hiyerarşileri mümkün olduğunca sığ olmalı, çoğu uzantı kompozisyondan geliyor. Böyle modellerde, ben özel değişkenler için herhangi bir neden görmüyorum değil bunun yerine korunmuş yapılan edilecek. Genişletilmiş sınıf, üst sınıfın tüm davranışlarını içeren bir uzantıyı temsil etmeli ((olması gerektiğine inanıyorum ve dolayısıyla özel yöntemlerin doğal olarak kötü olduğuna inanıyorsa), genişletilmiş sınıf neden bu tür verilerin depolanmasını temsil etmiyor?

Bunu bir başka deyişle, özel bir değişkenin korumalı olandan daha iyi olacağını düşündüğüm herhangi bir durumda, miras, ilk başta sorunun çözümü değildir.


0

Orada söylediği gibi, bu sebeplerden sadece bir tanesi, mantıksal olarak bağlantılı kodun fiziksel bağlı varlıkların (dosyalar, paketler ve diğerleri) içerisine yerleştirilmesi gerekir. Daha küçük bir ölçekte bu, kullanıcı arayüzünü görüntüleyen sınıflarla birlikte DB sorgularını paket içine alan bir sınıf koymamanızın nedeniyle aynıdır.

Bununla birlikte, korunan değişkenlerin önerilmemesinin ana nedeni, kapsülleme kırma eğilimindedir. Değişkenler ve yöntemler mümkün olduğunca sınırlı görünürlükte olmalıdır; daha fazla referans için bkz. Joshua Bloch'un Etkili Java'sı , Madde 13 - "Sınıfların ve üyelerin erişilebilirliğini en aza indirin".

Ancak, bu reklam litteramının tamamını yalnızca bir lonca çizgisi olarak almamalısınız; Korunan değişkenler çok kötü ise, ilk etapta dilin içine yerleştirilmiş olmazlardı. Korunan alanlar için makul bir yer, bir test çerçevesinden temel sınıfın içindedir (entegrasyon test sınıfları bunu genişletir).


1
Bir dilin izin verdiği için bunu yapmanın sorun olmadığını varsaymayın. Korunan alanlara izin vermek için gerçekten bir tanrı sebebi olduğundan emin değilim; aslında onları işverenime vermeme izin veriyoruz.
Andy,

2
@Andy Ne dediğimi okumadın; Korunan alanların önerilmediğini ve bunun nedenlerini söyledim. Korunan değişkenlerin gerekli olduğu senaryolar açıkça vardır; Sadece alt sınıflarda sabit bir nihai değer isteyebilirsiniz.
m3th0dman

Değişken ve alanı birbirinin yerine kullanabiliyor göründüğünüz ve korunan consts gibi şeyleri bir istisna olarak göreceğinizi açıkça belirteceğiniz için, gönderinizin bir kısmını yeniden değerlendirmek isteyebilirsiniz.
Andy,

3
@Andy Korunan değişkenlere atıfta bulunduğumdan beri yerel ya da parametre değişkenlerinden değil alanlardan bahsettiğim çok açık. Sabit olmayan korunan değişkenlerin yararlı olduğu bir senaryodan da bahsettim; bir şirketin programcıların kod yazma şeklini zorlaması yanlıştır.
m3th0dman

1
Çok açık olsaydı, kafam karışmaz ve size karşı oy kullanmazdım. Kural, StyleCop, FxCop'u çalıştırma kararımız ve birim testleri ve kod incelemeleri gerektirmesi gibi üst düzey geliştiricilerimiz tarafından yapıldı.
Andy,

0

JUnit testleri için korumalı değişkenleri kullanmanın faydalı olabileceğini düşünüyorum. Onları özel yaparsanız, test sırasında yansıma olmadan erişemezsiniz. Bu, birçok durum değişikliği ile karmaşık bir işlemi test ediyorsanız yararlı olabilir.

Korumalı alıcı yöntemleriyle onları özel yapabilirsiniz, ancak değişkenler yalnızca bir JUnit testi sırasında harici olarak kullanılacaksa bunun daha iyi bir çözüm olduğundan emin değilim.


-1

Evet, (hatırladığım gibi) kitabın tüm özel olmayan değişkenleri de kaçınılması gereken bir şey olarak tartıştığı göz önüne alındığında biraz garip olduğuna katılıyorum.

Korunan değiştirici ile ilgili sorun, yalnızca alt sınıfa maruz kalan üye değil, aynı zamanda tüm paket için de görülebilir olması olduğunu düşünüyorum. Bence Bob Amca’nın burada istisna olması bu çifte görünüş. Tabii ki, kişi korunan yöntemlere ve dolayısıyla korunan değişken kalifikasyonuna sahip olmaktan her zaman geçemez.

Bence daha ilginç bir yaklaşım her şeyi halka açık yapmak, bu da sizi küçük sınıflara zorlatacağından, bu da tüm dikey mesafe metrikini biraz fazla çekiyor.

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.