Özel ve Korumalı - Görünürlük İyi Uygulama Endişesi [kapalı]


145

Aradım ve teorik farkı biliyorum.

  • public - Herhangi bir sınıf / işlev yönteme / özelliğe erişebilir.
  • korumalı - Yönteme / özelliğe yalnızca bu sınıf ve tüm alt sınıflar erişebilir.
  • private - Yönteme / özelliğe yalnızca bu sınıf erişebilir. Kalıtımsal bile olmayacak.

Her şey yolunda ve iyi, soru şu, aralarındaki pratik fark nedir? Ne zaman privateve ne zaman kullanırsınız protected? Bunun üzerinde standart veya kabul edilebilir iyi bir uygulama var mı?

Şimdiye kadar, kalıtım ve polimorfizm kavramını korumak için publicdışarıdan erişilmesi gereken herhangi bir şey (inşaatçılar ve ana sınıf işlevselliği gibi) ve protectediç yöntemler (mantık, yardımcı yöntemler vb.) İçin kullanıyorum. Ben doğru yolda mıyım?

(Bu soru benim için değil, aynı zamanda böyle bir soru görmedim gelecekteki referans için olduğunu unutmayın SO).


33
Önemli mi? OOP desteği olan herhangi bir dilin bu endişesi vardır. Ben PHP programlamak olur, ama soru herhangi bir OOP destek dili için geçerli olduğunu düşünüyorum.
Madara'nın Hayaleti

2
Tamam, yeterince adil, sadece etiketlemeyi unuttun mu diye merak ettim. Şimdi oop etiketini görüyorum.
Paul Bellora

Sınıfın her bir özelliği için görünürlüğü (özel / genel / korumalı) ayarlamam gerekiyor mu? Ya da sadece bazılarının görünürlük türüne sahip olması gerekir? Evet ise, sınıfın en üstünde bir görünürlük olması için hangi özelliğe ihtiyaç duyulacağına nasıl karar verilir?
user4271704

1
mahremiyetle korunan ve özel arasındaki fark açıktır. Alt sınıflar yöntemi / değişkeni kullanıyorsa korumalı kullanın, aksi takdirde private kullanın. Özellikle, alt sınıfların üst öğede çok benzer bir özel değişkeni yeniden tanımlaması gerekiyorsa, onu korumalı yapın.
iPherian

internalC # dilinde, bir sınıfın veya fiziksel bir derleme içindeki üyelerinin erişim düzeyini kısıtlayan başka bir erişim belirteci var . Yine de, desteğinden veya diğer dillerde benzer bir şeyden emin değilim.
RBT

Yanıtlar:


128

Hayır, doğru yolda değilsiniz. İyi bir kural: her şeyi mümkün olduğunca özel yapmak. Bu, sınıfınızı daha kapsüllenmiş hale getirir ve sınıfınızı kullanarak kodu etkilemeden sınıfın içini değiştirmenize olanak tanır.

Sınıfınızı kalıtsal olacak şekilde tasarlarsanız, o zaman geçersiz kılınabilecek ve alt sınıflardan erişilebilecek şeyleri dikkatlice seçin ve bunu erişilebilir kılmak, ancak geçersiz kılmak istemiyorsanız, korumalı hale getirin (ve son olarak Java'dan bahsedin). Ancak, sınıfınızın alt sınıflarını kabul ettiğinizde ve korumalı bir alan veya yöntem olduğunda, bu alanın veya yöntemin sınıfın genel API'sının bir parçası olduğunu ve daha sonra alt sınıfları bozmadan değiştirilemeyeceğini unutmayın.

Devralınması amaçlanmayan bir sınıfın final yapılması gerekir (Java dilinde). Birim testi uğruna bazı erişim kurallarını (özelden korumalı, finalden finale kadar) gevşetebilir, ancak daha sonra bunu belgeleyebilir ve yöntem korunmasına rağmen, geçersiz kılınmaması gerektiğini açıkça belirtebilirsiniz.


1
Buradaki asıl soru, privatevs'ye karşılık protectedBir mülkün ne zaman miras alınmasını istiyorum ve ne zaman yapmam? Gerçekten gelecekte bir kullanıcının bazen dersimi alıp genişletmek isteyip istemediğini söyleyemem ...
Madara'nın Hayaleti

16
Soru, kullanıcının geçersiz kılmak istediği şey değil , soru geçersiz kılınmasına izin vermek istediğiniz şeydir . Genellikle taraf değiştirmeye ve düşünmeye çalışmaya yardımcı olur: Eğer o sınıfı kullansam ve bir alt sınıf oluştursaydım, neyi geçersiz kılmak isterdim? Bazen diğer kullanıcılar hala bazı şeyleri özleyecek ...
Thorsten Dittmar

3
Korunan alanlar kalıtımda kötü bir uygulamadır! . Lütfen buna dikkat edin. İşte nedeni
RBT

4
Özel alanlar mirasta uygulamada kötüdür !. (abartılı) Lütfen cevabımı oku.
Larry

6
Tipik kontrol manyağı görüşü.
bruno desthuilliers

58

Öncelikle burada yöntem erişiminden bahsettiğimi söyleyerek ve biraz daha az ölçüde, sınıfları üye erişimi değil , finalleri işaretleyerek bunu önceden yazayım .

Eski bilgelik

"yapmamak için iyi bir nedeniniz yoksa özel olarak işaretleyin"

açık kaynak geliştirici kütüphane alanı ve VCS / bağımlılık mgmt hakim önce yazıldığı günlerde mantıklı. Github, Maven vb. sayesinde hiper işbirlikçi oldu. O zamanlar bir kütüphanenin nasıl kullanılabileceğini kısıtlayarak para kazanacaktı. Muhtemelen kariyerimin ilk 8 veya 9 yılını bu "en iyi uygulamaya" kesinlikle bağlı kalarak geçirdim.

Bugün bunun kötü bir tavsiye olduğuna inanıyorum. Bazen özel bir yöntemi veya bir sınıf finalini işaretlemek için makul bir argüman vardır, ancak son derece nadirdir ve o zaman bile muhtemelen hiçbir şey geliştirmez.

Sen hiç:

  • Hayal kırıklığına uğramış, miras ve birkaç kod satırı ile düzeltilmiş bir hataya sahip olan bir kütüphane vb. Sahibim.
  • Bir kütüphane, yazarlar tarafından hayal edilenden biraz farklı bir kullanım durumu için kullanmak istedi, ancak özel / son yöntemler ve sınıflar nedeniyle bunu yapamadı? Sahibim.
  • Hayal kırıklığına uğramış, şaşırmış veya genişletilebilirliğinde aşırı derecede izin verilen bir kütüphane vb. Bende yok.

Bunlar, varsayılan olarak özel işaretleme yöntemleri için duyduğum en büyük üç rasyonelleştirmedir:

Rasyonalizasyon # 1: Güvenli değil ve belirli bir yöntemi geçersiz kılmak için hiçbir neden yok

Yazdığım belirli bir yöntemi geçersiz kılmanın gerekip gerekmediği konusunda kaç kez yanlış olduğumu sayamıyorum. Birkaç popüler açık kaynak kütüphanesinde çalıştıktan sonra, şeyleri özel olarak işaretlemenin gerçek maliyetini zor yoldan öğrendim. Genellikle öngörülemeyen sorunlara veya kullanım durumlarına tek pratik çözümü ortadan kaldırır. Tersine, ben ettik asla API güvenliği ile ilgili nedenlerle yerine özel korumalı bir işaretleme yöntemi mesleki gelişim 16+ yıl içinde pişman. Bir geliştirici bir sınıfı genişletmeyi ve bir yöntemi geçersiz kılmayı seçtiğinde, bilinçli olarak "Ne yaptığımı biliyorum" diyorlar. ve verimlilik için bu yeterli olmalı. dönem. Tehlikeli ise, Javadocs sınıfında / yönteminde not edin, sadece körü körüne kapıyı kapatmayın.

Varsayılan olarak korunan markalama yöntemleri, modern SW geliştirmedeki ana sorunlardan biri için bir azaltmadır: hayal gücünün başarısızlığı.

Rasyonalizasyon # 2: Genel API / Javadoc'ları temiz tutar

Bu daha mantıklı ve hedef kitleye bağlı olarak yapılması gereken doğru şey bile olabilir, ancak API'yi "temiz" tutmanın maliyetinin gerçekte ne olduğunu düşünmeye değer: genişletilebilirlik. Yukarıda belirtilen nedenlerden dolayı, muhtemelen her durumda varsayılan olarak korunan şeyleri işaretlemek daha mantıklıdır.

Rasyonalizasyon # 3: Yazılımım ticari olarak kullanılıyor ve kullanımını kısıtlamam gerekiyor.

Bu da mantıklı, ancak bir tüketici olarak her seferinde daha az kısıtlayıcı olan rakiplerle (önemli bir kalite farkının olmadığı varsayılarak) giderdim.

Asla asla Deme

Asla yöntemleri özel markalama demiyorum. Ben daha iyi bir kural "iyi bir neden olmadığı sürece yöntemleri korumak yapmak" olduğunu söylüyorum.

Bu tavsiye, kütüphaneler veya modüllere ayrılmış daha büyük ölçekli projeler üzerinde çalışanlar için en uygunudur. Daha küçük veya daha fazla monolitik projeler için, zaten tüm kodu kontrol ettiğiniz için çok önemli değildir ve ihtiyacınız olduğunda / gerektiğinde kodunuzun erişim seviyesini değiştirmek kolaydır. O zaman bile yine de aynı tavsiyeyi veririm :-)


Wrt senin Rationalization # 1- Ben bu davranış son değildir ve çocuğum sınıfları geçersiz isteyeyim bir durum olabilir hissediyorum olarak açıkça bunu tercih korumalı olarak ne zaman işareti şey. O kadar emin değilseniz, varsayılan olarak özel yapmak istiyorum. Özel olarak sadece korumalı erişim belirtici ekleyerek, varsayılan olarak bir yöntem sanal olmayan tutar C # gibi bir dil yok sense.You yapar hem eklemek zorunda virtualve protectedsizin niyet çok açık hale getirmek için anahtar kelimeler. Ayrıca, insanlar protectedvarsayılan olarak algılanması zor olduğu için miras yerine ilişkiyi tercih ederler
RBT

4
Bir yöntemi geçersiz kılmak için gereken anahtar kelimelerin kombinasyonu, bir dil ayrıntısı IMHO'dur. 1 numaralı rasyonalizasyona sunduğum karşı argüman, "Eğer o kadar emin değilsem ..." ibaren sözünü ele almayı hedefliyor. Pratik olarak, bunun tehlikeli olmasının bir nedenini düşünemiyorsanız, genişletilebilirliği seçerek kazanılması gereken daha fazla şey vardır. 'İlişki' dediğinde bunu kompozisyonla eşanlamlı olarak alıyorum, bu durumda her iki şekilde de önemli olduğunu görmüyorum.
Nick

3
Ben sadece 1 veya 2 veya yöntemleri geçersiz kılmak istedim, çünkü temel sınıfları kaç kez "klon" zorunda hatırlamıyorum. Söyledikleriniz gibi, her zamankinden daha fazla kod paylaştığımız sosyal trendle, korumalı olarak varsayılan olarak gizli olmaktan çok daha mantıklı geliyor. yani kendi mantığınıza daha açık olun.
shinkou

1
Katılıyorum. Özel satır sınırlayıcılarını işlemek için Java'nın BufferedReader'ını değiştirmek istedim. Özel alanlar sayesinde, sadece onu genişletmek ve readLine () 'i geçersiz kılmak yerine tüm sınıfı kopyalamak zorundayım.
Ron Dunn

21

Özel alanları kötüye kullanmayı bırakın !!!

Buradaki yorumlar, özel alanların kullanılmasına büyük ölçüde destekleyici görünmektedir. O zaman söyleyecek farklı bir şeyim var.

Özel alanlar prensipte iyi mi? Evet. Ama altın bir kural olduğunu söylemek kesinlikle yanlış olduğundan emin değilseniz her şeyi özel yapmaktır ! Biriyle karşılaşana kadar sorunu göremezsiniz. Bence, emin değilseniz alanları korumalı olarak işaretlemelisiniz .

Bir sınıfı genişletmek istediğiniz iki durum vardır:

  • Temel sınıfa ekstra işlevsellik eklemek istiyorsunuz
  • Geçerli paketin dışındaki mevcut sınıfı değiştirmek istiyorsunuz (belki bazı kitaplıklarda)

İlk durumda özel alanlarla ilgili yanlış bir şey yok. İnsanların özel alanları kötüye kullanması, bokta değişiklik yapamayacağınızı öğrendiğinizde onu çok sinirlendiriyor.

Otomobilleri modelleyen basit bir kütüphane düşünün:

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}

Kütüphane yazarı düşündü: kütüphanem kullanıcılarının assembleCar()doğru uygulama detaylarına erişmeleri için bir neden yok mu? Vidayı özel olarak işaretleyelim.

Yazar yanlış. assembleCar()Tüm sınıfı paketinize kopyalamadan yalnızca yöntemi değiştirmek isterseniz, şansınız kalmaz . Kendi screwalanınızı yeniden yazmalısınız . Diyelim ki bu araba bir düzine vida kullanıyor ve her biri farklı özel yöntemlerde bazı gereksiz başlatma kodları içeriyor ve bu vidaların hepsi özel olarak işaretlenmiş. Bu noktada, emmeye başlar.

Evet, benimle , kütüphane yazarının daha iyi kod yazmış olabileceğini ve böylece özel alanlarda yanlış bir şey olmadığını iddia edebilirsiniz . Özel alanın OOP ile ilgili bir sorun olduğunu iddia etmiyorum . İnsanlar bunları kullanırken bir sorundur.

Hikayenin ahlaki, bir kütüphane yazıyorsanız, kullanıcılarınızın belirli bir alana erişmek isteyip istemediğini asla bilemezsiniz. Emin değilseniz, protecteddaha sonra herkesin daha mutlu olması için işaretleyin . En azından özel alanı kötüye kullanmayın .

Nick'in cevabını çok destekliyorum.


2
Özel alanları kullanmak çoğunlukla kalıtımın bir sınıfın / nesnenin belirli bir davranışını (bilinçli olarak kullanılıyorsa) değiştirmek için yanlış soyutlama olduğunu söyler. API değişmeden davranış değiştiğinden, değiştirme genellikle LSP'yi ihlal edecektir. Harika cevap, +1.
Madara'nın Hayaleti

Çok doğru! Özel Minecraft modları yazmaya çalışırken varlığımın sıkıntısı. Bunu düzeltmek için Yansıma veya ASM kullanmak zorunda olmaktan daha can sıkıcı bir şey yok.
RecursiveExceptionException

Nick'in cevabını anladığım kadarıyla çoğunlukla mülk değil yöntemlerden bahsediyordu. Her seferinde yöntemleri özel olarak ilan etmememiz gerektiğine katılıyorum ve bunların kullanımı düşünceli olmalı, ancak neden sınıfı daha kolay "yeniden yazmak" isteyeceğiniz için özel olarak korunan mülkleri bildirmenizi tavsiye edersiniz. Günlük 3 p ile çalışırken hayal kırıklığınızı tamamen paylaşın, ancak insanları katı mimari oluşturmaya teşvik edelim ve sadece "kod bok haline gelecektir, bu yüzden baştan yeniden koruyalım, böylece kolayca yeniden yazabiliriz" diyelim. Hayal kırıklığını paylaşmama rağmen.
sean662

16

Bir süre önce her sınıfı mümkün olduğunca kilitlemekten bahseden bir makale okudum. Bir yoksa nihai ve gizli yap şeyi hemen dış dünyaya bazı veri veya işlevini göstermek gereğini. Kapsamı daha sonra daha izin verilecek şekilde genişletmek her zaman kolaydır, ancak tam tersi değildir. Öncelikle final, aralarından seçim yapmayı privateve protectedçok daha kolay hale getirecek mümkün olduğunca çok şey yapmayı düşünün .

  1. Hemen alt sınıfa ayırmanız gerekmedikçe tüm sınıfları kesin yapın.
  2. Hemen alt sınıfa ayırmanız ve geçersiz kılmanız gerekmedikçe tüm yöntemleri sonlandırın.
  3. Her ne kadar çoğu zaman garip olan yöntemin gövdesi içinde değiştirmeniz gerekmedikçe tüm yöntem parametrelerini sonlandırın.

Eğer son bir sınıftan ayrılırsanız, o zaman dünya tarafından kesinlikle bir şeye ihtiyaç duyulmadıkça her şeyi özel yapın.

Alt sınıf (lar) içeren bir sınıftan ayrılırsanız, her özelliği ve yöntemi dikkatle inceleyin. Öncelikle söz konusu özelliği / yöntemi alt sınıflara göstermek isteyip istemediğinizi düşünün. Bunu yaparsanız, bir alt sınıfın, geçersiz kılma sürecinde özellik değerini veya yöntem uygulamasını bozduysa nesnenize zarar verip vermeyeceğini düşünün. Mümkünse ve sınıfınızın özelliğini / yöntemini alt sınıflardan bile (ironik, biliyorum) korumak istiyorsanız , o zaman özel yapın. Aksi takdirde korunmasını sağlayın.

Feragatname: Java'da fazla program yapmıyorum :)


2
Açıklığa kavuşturmak için: final classJava'daki A , devralınamayan bir sınıftır. A final methodsanal değildir, yani bir alt sınıf tarafından geçersiz kılınamaz (Java yöntemleri varsayılan olarak sanaldır). A final field/parameter/variabledeğişmez bir değişken referanstır (başlatıldıktan sonra değiştirilememesi anlamında).
M.Stramm

1
Tuhaf bir şekilde, tam tersi tavsiyeyi gördüm, söz konusu şeyi geçersiz kılmanın bir değişmezi kırma potansiyeli olmadığından emin olmadıkça hiçbir şeyin nihai olmaması gerektiğini gördüm. Bu şekilde herkes sınıfınızı gerektiği gibi genişletebilir.
GordonM

Programlama kariyerinizde "final" yöntem parametresini işaretlemenin anlayışınızda bir fark yarattığı bir durumu söyleyebilir misiniz? (derleyici tarafından istenmesi dışında)
user949300

7

Ne zaman privateve ne zaman kullanırsınız protected?

Özel Kalıtım , IS-A ilişkisi yerine ilişki açısından uygulandığı düşünülebilir . Basitçe söylemek gerekirse, devralınan sınıfın dış arabiriminin miras alınan sınıfla hiçbir (görünür) ilişkisi yoktur, Kalıtım yalnızca Base sınıfının sağladığı benzer bir işlevi uygulamak için kullanır .private

Özel Kalıtım'ın aksine, Korumalı kalıtım , IS-A türetme sınıfının Base sınıfından türetildiği ve türetilmiş üyelerin sadece türetilmiş sınıfa erişimini kısıtlamak istediği sınırlı bir Kalıtım biçimidir .


2
"İlişki anlamında uygulanan" bir HAS-A ilişkisidir. Tüm öznitelikler özelse, bunlara erişmenin tek yolu yöntemlerdir. Bu, alt sınıfın süper sınıfın bir nesnesini içermesi ile aynı şeydir. Somut sınıflarınızdaki tüm korunan nitelikleri ortadan kaldırmak, onlardan da tüm mirasları ortadan kaldırmak anlamına gelir.
shawnhcorey

2
İlginç düşünce süreci - Özel Kalıtım . @ shawnhcorey, HAS-A ilişkisi aka derneği (toplanma veya kompozisyon olabilir) işaret ettiğini açıkça belirttiğiniz için teşekkürler . Bu gönderinin de aynı şeyi açıklığa kavuşturmak için güncellenmesi gerektiğini hissediyorum.
RBT

1

Peki, ödeme faturaları ödemelerin faturalandırılmasını idare ederse kapsülleme ile ilgilidir, o zaman ürün sınıfında neden tüm faturalandırma sürecine ihtiyaç duyulur, yani ödeme yöntemi nereye ödeme yapmalı .. öylesine sadece diğer sınıflar ve nesneler için kullanılanlara izin vermek diğer sınıfların da kullanacağı kişiler için halka açık olandan başka bir şey değildir, sadece sınıfları genişletmek için bu sınır için korunur. Madara uchiha gibi özel "limboo"görebildiğiniz gibi (sadece tek sınıf sınıf).

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.