Alıcıları ve ayarlayıcıları / erişimcileri neden kullanmalıyım?


1541

Yalnızca bu değişkenler için ortak alanları kullanmak yerine, yalnızca elde edilen ve ayarlanan alıcıları ve ayarlayıcıları kullanmanın avantajı nedir?

Alıcılar ve ayarlayıcılar basit bir set / setten daha fazlasını yapıyorsa, bunu çok hızlı bir şekilde çözebilirim, ancak nasıl yapılacağı konusunda% 100 net değilim:

public String foo;

daha kötüdür:

private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

Halbuki ilki çok daha az kaynak plakası alır.


6
@Dean J: Diğer birçok soru ile kopyalayın: stackoverflow.com/search?q=getters+setters
Asaph

8
Tabii ki, nesnenin değiştirilmesi için bir özelliğe ihtiyaç duymadığında her ikisi de eşit derecede kötüdür. Her şeyi özel yapmayı tercih ederim ve sonra faydalıysa getters, gerekirse ayarlayıcıları eklerim.
Tordek

26
Google "erişimciler kötü"
OMG Ponies

34
İşlevsel kod veya değişmez nesneler yazıyorsanız "Erişimciler kötülük yapar". Durum bilgisi olan değişken nesneler yazıyorsanız, bunlar oldukça önemlidir.
Christian Hayter

Yanıtlar:


980

Aslında bir sınıfın alanlarını doğrudan ortaya koymak yerine erişimcileri kullanmayı düşünmek için birçok iyi neden vardır - sadece kapsülleme argümanı ve gelecekteki değişiklikleri kolaylaştırmak.

İşte farkında olduğum bazı nedenler:

  • Mülk edinme veya ayarlama ile ilişkili davranışların kapsüllenmesi - bu, daha sonra daha kolay bir şekilde ek işlevin (doğrulama gibi) eklenmesini sağlar.
  • Alternatif bir temsili kullanarak bir özelliği ortaya koyarken mülkün iç temsilini gizleme.
  • Genel arayüzünüzü değişiklikten yalıtmak - uygulama mevcut tüketicileri etkilemeden değişirken genel arayüzün sabit kalmasını sağlar.
  • Mülkün yaşam boyu ve bellek yönetimi (bertaraf) semantiğinin kontrol edilmesi - özellikle yönetilmeyen bellek ortamlarında (C ++ veya Objective-C gibi) önemlidir.
  • Bir özelliğin çalışma zamanında değiştiğinde hata ayıklama önleme noktası sağlama - bir özelliğin belirli bir değere ne zaman ve nerede değiştiğini hata ayıklama, bazı dillerde bu olmadan oldukça zor olabilir.
  • Mülkiyet alıcı / ayarlayıcılara karşı çalışmak üzere tasarlanmış kütüphanelerle birlikte çalışabilirlik geliştirildi - Alay, Serileştirme ve WPF akla geliyor.
  • Mirasçıların, mülkün nasıl davrandığına ve maruz kaldıklarına ilişkin anlambilimlerini değiştirmesine izin veren alıcı / ayarlayıcı yöntemlerini geçersiz kılma.
  • Alıcı / ayarlayıcının değerlerden ziyade lambda ifadeleri olarak iletilmesine izin vermek.
  • Harfler ve ayarlayıcılar farklı erişim düzeylerine izin verebilir - örneğin erişim herkese açık olabilir, ancak küme korunabilir.

59
+1. Sadece eklemek için: Tembel yüklemeye izin veriyor. Yazarken kopyalamaya izin verilmesi.
NewbiZ

6
@bjarkef: Bence publicerişimci yöntemleri kod çoğaltmasına neden olur ve bilgi ortaya koyar . Toplu erişim için erişimciler gerekli değildir (serileştirme). Bazı dillerde (Java), publicget erişimcileri bağımlı sınıfları bozmadan dönüş türünü değiştiremez. Dahası, OO ilkelerine aykırı olduklarına inanıyorum. Ayrıca bakınız: stackoverflow.com/a/1462424/59087
Dave Jarvis

15
@sbi: Bir özellik ayarlayıcı kullanılarak, iyi tasarlanmış bir çerçevede bile satın alınan bir şey, bir özellik değiştiğinde bir nesnenin başka bir nesneyi haberdar etmesidir.
supercat

22
@supercat: Ancak, genel olarak kamuya açık verileri desteklemiyorum. Diğer nesnelerin bildirimi, yalnızca (az ya da çok) genel veri alanlarına sahip bir veri kabı üzerinde önemli bir soyutlama sağlayan gerçek yöntemlerden de yapılabilir. Onun yerine plane.turnTo(dir); plane.setSpeed(spd); plane.setTargetAltitude(alt); plane.getBreaks().release();söylemek istiyorum plane.takeOff(alt). Uçağı takingOffmoda geçirmek için hangi iç veri alanlarının değiştirilmesi gerektiğinden endişe etmiyorum. Ve başka hangi nesneler ( breaks) yöntemi bildirir Ben de bilmek istemiyorum.
sbi

8
@BenLee: Bunu nasıl söyleyeceğimi gerçekten bilmiyorum. "Erişimciler" sadece "getters / setters" kelimesinin eş anlamlısıdır ve ilk iki yorumumda bunların neden "OOP" teriminin kötüye kullanıldığını açıkladım. Eğer ona ulaşmanız ve durumu doğrudan manipüle etmeniz gerekiyorsa, o terimin OOP anlamında bir nesne değildir.
sbi

480

Ayarlayıcının değerin ayarlanmasından daha fazlasını yapması gerektiğini anladığınız andan itibaren 2 hafta (ay, yıl) olduğundan , mülkün doğrudan 238 diğer sınıfta kullanıldığını anlayacaksınız :-)


84
500k hat uygulamasına bakıp hiç ihtiyaç duymadığım yere bakıyorum. Bununla birlikte, bir kez ihtiyaç duyulursa, bir bakım kabusu yaratmaya başlayacağını söyledi. Benim için bir onay işareti için yeterince iyi.
Dean J

20
Ben sadece sizi ve uygulamanızı kıskanabilirsiniz :-) Bu, gerçekten de yazılım yığını bağlıdır. Delphi, örneğin (ve C # - sanırım?) Özellikleri ilk sınıfta doğrudan bir alanı okuyabilen / yazabildikleri 1. sınıf vatandaşları olarak tanımlamanıza izin verir, ancak - ihtiyacınız olduğunda - bunu getter / setter yöntemleriyle de yapabilirsiniz. Mucho rahat. Java, ne yazık ki, siz de alıcıları / ayarlayıcıları kullanmaya zorlayan javabeans standardından bahsetmiyor.
ChssPly76

14
Bu, erişimcilerin kullanılması için gerçekten iyi bir neden olsa da, birçok programlama ortamı ve editör, sorunun etkisini biraz azaltan yeniden düzenleme (IDE'de veya ücretsiz eklentiler olarak) için destek sunmaktadır.
LBushkin

62
olgunlaşma öncesi optimizasyon gibi geliyor
cmcginty

41
Alıcıların ve ayarlayıcıların uygulanıp uygulanmayacağını merak ettiğinizde sormanız gereken soru şudur: Bir sınıftaki kullanıcıların neden sınıfın içlerine erişmesi gerekir? Doğrudan yapmaları veya ince bir sözde katman tarafından korunmaları gerçekten önemli değildir - kullanıcıların uygulama ayrıntılarına erişmeleri gerekiyorsa, bu sınıfın yeterince soyutlama sunmadığının bir işaretidir. Ayrıca bu yoruma bakınız .
sbi

357

Ortak alan, alanı döndürmek ve alana atamak dışında hiçbir şey yapmayan bir alıcı / ayarlayıcı çiftinden daha kötü değildir. Birincisi, (çoğu dilde) işlevsel bir fark olmadığı açıktır. Herhangi bir fark, sürdürülebilirlik veya okunabilirlik gibi diğer faktörlerde olmalıdır.

Getter / setter çiftlerinin sıkça bahsedilen bir avantajı değil. Uygulamayı değiştirebileceğiniz ve müşterilerinizin yeniden derlenmesi gerekmediği yönünde bir iddia var. Sözde, ayarlayıcılar daha sonra doğrulama gibi işlevler eklemenize izin verir ve müşterilerinizin bunu bilmesine bile gerek yoktur. Bununla birlikte, bir ayarlayıcıya doğrulama eklemek, ön koşullarında bir değişikliktir , önceki sözleşmenin ihlali , oldukça basit bir şekilde, "buraya bir şey koyabilirsiniz ve aynı şeyi daha sonra alıcıdan alabilirsiniz".

Şimdi, sözleşmeyi kırdığınıza göre, kod tabanındaki her dosyayı değiştirmek, yapmak istemediğiniz bir şeydir, kaçınmak değil. Bundan kaçınırsanız, tüm kodların bu yöntemlere ilişkin sözleşmenin farklı olduğunu varsayarsınız.

Bu sözleşme olmasaydı, arabirim istemcilerin nesneyi geçersiz durumlara koymasına izin veriyordu. Bu, kapsüllemenin tam tersidir Eğer bu alan başlangıçtan itibaren hiçbir şeye ayarlanamazsa, neden başlangıçtan itibaren doğrulama yapılmadı?

Aynı argüman, bu doğrudan alıcı / ayarlayıcı çiftlerinin sözde diğer avantajları için de geçerlidir: daha sonra ayarlanan değeri değiştirmeye karar verirseniz, sözleşmeyi ihlal edersiniz. Türetilmiş bir sınıftaki varsayılan işlevselliği, zararsız birkaç değişikliğin (günlüğe kaydetme veya gözlemlenemeyen diğer davranışlar gibi) ötesinde geçersiz kılarsanız, temel sınıfın sözleşmesini bozarsınız. Bu, OO'nun ilkelerinden biri olarak görülen Liskov Değiştirilebilirlik Prensibinin ihlalidir.

Bir sınıfın her alan için bu aptal alıcıları ve ayarlayıcıları varsa, o zaman herhangi bir değişmezi olmayan, sözleşmesi olmayan bir sınıftır . Bu gerçekten nesne yönelimli bir tasarım mı? Sınıfın tamamı bu alıcılar ve ayarlayıcılarsa, bu sadece aptal bir veri sahibi ve aptal veri sahipleri aptal veri sahipleri gibi görünmelidir:

class Foo {
public:
    int DaysLeft;
    int ContestantNumber;
};

Böyle bir sınıfa doğrudan geçişli alıcı / ayarlayıcı çifti eklemek bir değer katmaz. Diğer sınıflar, yalnızca alanların sağladığı işlemleri değil, anlamlı işlemleri sağlamalıdır. Yararlı değişmezleri bu şekilde tanımlayabilir ve koruyabilirsiniz.

Müşteri : "Bu sınıfın bir nesnesiyle ne yapabilirim?"
Tasarımcı : "Birkaç değişkeni okuyabilir ve yazabilirsiniz."
Müşteri : "Ah ... güzel, sanırım?"

Alıcıları ve ayarlayıcıları kullanmanın nedenleri vardır, ancak bu nedenler mevcut değilse, yanlış kapsülleme tanrıları adına alıcı / ayarlayıcı çiftleri yapmak iyi bir şey değildir. Alıcıları veya belirleyicileri yapmak için geçerli nedenler, daha sonra doğrulama veya farklı dahili temsiller gibi daha sonra yapabileceğiniz potansiyel değişiklikler olarak belirtilenleri içerir. Ya da değer müşteriler tarafından okunabilir ancak yazılabilir olmamalıdır (örneğin, bir sözlüğün boyutunu okumak), bu yüzden basit bir alıcı iyi bir seçimdir. Ancak bu nedenleri, yalnızca daha sonra isteyebileceğiniz potansiyel bir şey olarak değil, seçim yaptığınızda orada olmalıdır. Bu bir YAGNI örneğidir ( Buna ihtiyacınız olmayacak ).


12
Harika cevap (+1). Benim tek eleştirim, son paragraftaki "validasyon" un ilk birkaçdaki "validasyon" dan nasıl farklı olduğunu anlamak için birkaç okuma yapmam gerektiğidir (ki bu ikinci durumda atmışsınız, ancak ilkinde terfi etmişsinizdir); ifadelerin ayarlanması bu konuda yardımcı olabilir.
Orbit'te Hafiflik Yarışları

14
Bu harika bir cevap ama ne yazık ki mevcut çağ "bilgi gizlemenin" ne olduğunu ya da ne için olduğunu unuttu. Asla değişmezlik hakkında okumazlar ve en çevik arayışlarında asla bir nesnenin yasal durumlarının ne olduğunu ve dolayısıyla ne olmadığını tanımlayan devlet-geçiş şemasını çizmediler.
Darrell Teague

2
Önemli bir gözlem, alıcıların ayarlayıcılardan çok daha yararlı olduklarıdır. Alıcı, hesaplanan değeri veya önbelleğe alınmış hesaplanan değeri döndürebilir. Bir ayarlayıcının yapabileceği tek şey bir doğrulama ve privatealanı değiştirmek . Bir sınıfın ayarlayıcıları yoksa, onu değişmez yapmak kolaydır.
Raedwald

7
Ee, sanırım bir sınıf değişmezinin ne olduğunu bilmiyorsun. Önemsiz bir yapı ise, hemen hemen destekleyecek değişmezleri olduğu ve akılda olmadan tasarım yapamayacağı anlamına gelir. "Bir hata oluştu, bir üye yanlış güncellendi." ayrıca "Üye güncellemesi sınıf değişmezlerini ihlal etti" gibi oldukça fazla şey okur. İyi tasarlanmış bir sınıf, istemci kodunun değişmezlerini ihlal etmesine izin vermez.
R. Martinho Fernandes

5
'Aptal veri tutucular için +1 aptal veri tutucular gibi görünmelidir' Ne yazık ki, bu günlerde herkes aptal veri tutucular etrafında her şeyi tasarlıyor gibi görünüyor ve birçok çerçeve bile gerektiriyor ...
Joeri Hendrickx

92

Birçok insan, alıcıların ve pasörlerin avantajlarından bahsediyor, ama şeytanın avukatını oynamak istiyorum. Şu anda programcıların her şeyi hazırlayan ve ayarlayıcı yapmaya karar verdikleri çok büyük bir programda hata ayıklama yapıyorum. Bu hoş görünebilir, ama tersine mühendislik kabusu.

Diyelim ki yüzlerce kod satırına bakıyorsunuz ve bununla karşılaşıyorsunuz:

person.name = "Joe";

Bir ayarlayıcı olduğunu anlayana kadar güzel bir kod parçası. Şimdi, bu ayarlayıcıyı takip edersiniz ve aynı zamanda person.firstName, person.lastName, person.isHuman, person.hasReallyCommonFirstName öğesini ayarlar ve veritabanına bir sorgu gönderen person.update () öğesini çağırırsınız. Oh, bu Hafıza sızıntınızın olduğu yer.

İlk bakışta yerel bir kod parçasını anlamak, alıcıların ve ayarlayıcıların kırılma eğilimi gösteren iyi okunabilirliğin önemli bir özelliğidir. Bu yüzden yapabildiğim zaman onlardan kaçınmaya ve kullandığımda yaptıklarını en aza indirmeye çalışıyorum.


8
Evet. Şu anda büyük bir kod temeli yeniden düzenleme ve bu bir kabus oldu. Alıcılar ve ayarlayıcılar, çok meşgul diğer alıcıları ve ayarlayıcıları çağırmak da dahil olmak üzere çok fazla şey yaparlar. Doğrulama, erişimcileri kullanmak için harika bir nedendir, ancak bunları daha fazlasını yapmak için kullanmak, herhangi bir potansiyel yararı ortadan kaldırıyor gibi görünmektedir.
Fadecomic

31
Bu sözdizimsel şekere karşı bir argüman, genel olarak ayarlayıcılara karşı değil.
Phil

6
Doğru ve doğru ancak dahası, bireysel mülkiyet belirleyicilerinin, soyutlamanın neden sızmasına izin veren herhangi bir nesnenin bütünlüğünü sağlamak için kesinlikle hiçbir çaresi olmadan geçersiz durumlar için kapıyı nasıl açtığını gösterir.
Darrell Teague

"Onun bir ayarlayıcı fark kadar güzel bir basitçe kod parçası" - hey, Java veya C # hakkında orijinal yazı oldu? :)
Honza Zidek

Okunabilirlik konusunda tamamen katılıyorum. Ne zaman person.name = "Joe";denir tamamen açıktır . Bilginin yolunda bir sarsıntı yoktur, sözdizimi vurgulaması bir alan olduğunu gösterir ve yeniden düzenleme daha basittir (iki yöntemi ve bir alanı yeniden düzenlemeye gerek yoktur). İkincisi, "getIsValid ()" ya da "isValid ()" vb. Düşünerek boşa harcanan tüm beyin döngüleri unutulabilir. Bir alıcı / ayarlayıcı tarafından bir hatadan kurtulduğum tek bir zamanı düşünemiyorum.
Will

53

Saf bir nesne yönelimli dünyada, alıcılar ve pasifler korkunç bir anti-modeldir . Bu makaleyi okuyun: Getters / Setters. Kötülük. Dönem . Özetle, programcıları veri yapıları olarak nesneler hakkında düşünmeye teşvik ederler ve bu tür düşünme saf prosedüreldir (COBOL veya C'deki gibi). Nesneye yönelik bir dilde veri yapısı yoktur, sadece davranışı gösteren nesneler vardır (nitelikler / özellikler değil!)

Bunlarla ilgili daha fazla bilgiyi Elegant Objects Bölüm 3.5'te (nesne yönelimli programlama hakkındaki kitabım) bulabilirsiniz.


7
Harfler ve ayarlayıcılar bir anemik etki alanı modeli önerir.
Raedwald

7
İlginç bir bakış açısı. Fakat çoğu programlama bağlamında ihtiyacımız olan şey veri yapılarıdır. Bağlantılı makalenin "Köpek" örneğini almak. Evet, bir özellik belirleyerek gerçek dünyadaki bir köpeğin ağırlığını değiştiremezsiniz ... ama a new Dog()bir köpek değildir. Bir köpek hakkında bilgi tutan bir nesnedir. Ve bu kullanım için, yanlış kaydedilmiş bir ağırlığı düzeltmek doğaldır.
Stephen C

3
Size, en faydalı programların gerçek dünya nesnelerini modellemesine / simüle etmesine gerek olmadığını söylüyorum. IMO, bu aslında programlama dilleri ile ilgili değil. Ne için programlar yazdığımızla ilgili.
Stephen C

3
Gerçek dünya ya da değil, yegor tamamen haklıdır. Sahip olduğunuz şey gerçekten bir "Struct" ise ve buna adıyla başvuran herhangi bir kod yazmanız gerekmiyorsa, bunu karma veya başka bir veri yapısına koyun. Bunun için kod yazmanız gerekiyorsa, onu bir sınıfın üyesi olarak koyun ve bu değişkeni işleyen kodu aynı sınıfa koyun ve setter & getter'ı atlayın. PS. Her ne kadar yegor'in bakış açısını paylaşsam da, kodsuz açıklamalı fasulyelerin biraz yararlı veri yapıları olduğuna inanıyorum - ayrıca alıcılar bazen gerekli, ayarlayıcılar hiç var olmamalı.
Bill K

1
Ben taşındım - bu yanıtın tamamı doğru ve alakalı olmasına rağmen doğrudan soruyu ele almıyor. Belki de "Her iki ayarlayıcı / alıcı ve genel değişkenler yanlış" demelidir ... Kesinlikle spesifik olmak için, Ayarlayıcılar ve yazılabilir genel değişkenler asla kullanılmamalıdır, ancak alıcılar genel nihai değişkenlerle hemen hemen aynıdır ve bazen gerekli bir kötülüktür ama ikisi de diğerinden daha iyi değil.
Bill K

52

Bir çok neden var. En sevdiğim, davranışı değiştirmeniz veya bir değişken üzerinde ayarlayabileceğiniz şeyleri düzenlemeniz gerektiğidir. Örneğin, bir setSpeed ​​(int speed) yönteminiz olduğunu varsayalım. Ancak, yalnızca maksimum 100 hız ayarlayabilmenizi istiyorsunuz.

public void setSpeed(int speed) {
  if ( speed > 100 ) {
    this.speed = 100;
  } else {
    this.speed = speed;
  }
}

Kodunuzda HER YERDE herkese açık alanı kullanıyorduysanız ve yukarıdaki gereksinime ihtiyacınız olduğunu fark ettiyseniz ne olacak? Sadece setterinizi değiştirmek yerine kamusal alanın her kullanımını avlayın.

Benim 2 sent :)


61
Kamusal alanın her kullanımını avlamak o kadar zor olmamalı. Özelleştirin ve derleyicinin onları bulmasına izin verin.
Nathan Fellman

12
bu elbette doğrudur, ama neden tasarlandığından daha zor hale getirirsiniz. Get / set yaklaşımı hala daha iyi cevaptır.
Hardryv

28
@Nathan: Başka kullanımları bulmak sorun değil. Hepsini değiştirmek .
Graeme Perrow

22
@GraemePerrow hepsini değiştirmek zorunda kalmak bir avantaj değil, bir avantajdır :( Ya hızın 100'den yüksek olabileceğini düşündüğünüz bir kodunuz olsaydı (çünkü, sözleşmeyi imzalamadan önce, yapabilir!) ( while(speed < 200) { do_something(); accelerate(); })
R. Martinho Fernandes

29
ÇOK kötü bir örnek! Birisi aramalı: myCar.setSpeed(157);ve birkaç satırdan sonra speed = myCar.getSpeed();Ve şimdi ... Neden speed==100olması gerektiğini anlamaya çalışırken mutlu hata ayıklama dilerim157
Piotr Aleksander Chmielowski

38

Erişimcilerin ve mutasyon yapanların bir avantajı, doğrulama gerçekleştirebilmenizdir.

Örneğin foo, herkese açık olsaydı , bunu kolayca ayarlayabilirdim nullve sonra başka biri nesne üzerinde bir yöntemi çağırmayı deneyebilirdi. Ama artık orada değil! Bir setFooyöntemle, bunun fooasla ayarlanmadığından emin olabilirdim null.

Erişimciler ve mutasyoncular da kapsüllemeye izin verir - eğer değeri bir kez ayarlandığında (belki de yapıcıda ayarlanır ve daha sonra yöntemlerle kullanılır, ancak asla değiştirilmemesi gerekiyordu) görülmezseniz, asla kimse tarafından görülmez. Ancak, diğer sınıfların görmesine veya değiştirmesine izin verebiliyorsanız, uygun erişimci ve / veya mutatör sağlayabilirsiniz.


28

Dilinize bağlıdır. "Java" yerine "nesne yönelimli" olarak etiketlediniz, bu yüzden ChssPly76'nın cevabının dile bağlı olduğunu belirtmek isterim. Örneğin Python'da alıcıları ve ayarlayıcıları kullanmak için hiçbir neden yoktur. Davranışı değiştirmeniz gerekirse, temel özellik erişiminin çevresine alıcı ve ayarlayıcıyı saran bir özellik kullanabilirsiniz. Bunun gibi bir şey:

class Simple(object):
   def _get_value(self):
       return self._value -1

   def _set_value(self, new_value):
       self._value = new_value + 1

   def _del_value(self):
       self.old_values.append(self._value)
       del self._value

   value = property(_get_value, _set_value, _del_value)

2
Evet, cevabımın altındaki bir yorumda çok şey söyledim. Java, getty / setters'ı koltuk değneği olarak kullanan tek dil değil, tıpkı Python'un özellikleri tanımlayabilen tek dil olmadığı gibi. Ancak asıl mesele hala devam etmektedir - "mülkiyet" aynı "kamusal alan" değildir.
ChssPly76

1
@jcd - hiç de değil. Herkese açık alanlarınızı göstererek "arayüzünüzü" (herkese açık API burada daha iyi bir terim olacaktır) tanımlarsınız. Bir kez bittiğinde, geri dönüş yok. Özellikler alanlar DEĞİLDİR çünkü alanlara giriş denemelerini durdurmak için bir mekanizma sağlarlar (bunlar tanımlanmışsa yöntemlere yönlendirerek); ancak, alıcı / ayarlayıcı yöntemleri üzerinde sözdizimi şekerinden başka bir şey değildir. Son derece elverişlidir, ancak altta yatan paradigmayı değiştirmez - alanlara erişim üzerinde herhangi bir kontrolü olmayan alanlar, kapsülleme ilkesini ihlal eder.
ChssPly76

14
@ ChssPly76 — Kabul etmiyorum. Onlar gibi özellikleri gibi çok kontrol var, çünkü ne zaman ihtiyacım olursa onları özellikleri yapabilirsiniz. Boilerplate alıcıları ve ayarlayıcıları kullanan bir özellik ile ham öznitelik arasında hiçbir fark yoktur, ancak ham öznitelik daha hızlıdır, çünkü çağrı yöntemlerinden ziyade temel dili kullanır. İşlevsel olarak aynıdırlar. Kapsüllemenin ihlal edilmesinin tek yolu, parantezlerin ( obj.set_attr('foo')) doğal olarak eşittir işaretlerine ( obj.attr = 'foo') üstün olduğunu düşünüyorsanız . Genel erişim, genel erişimdir.
jcdyer

1
@jcdyer evet kadar kontrol evet, ama okunabilirlik kadar değil, diğerleri genellikle yanlış bir şekilde obj.attr = 'foo'başka bir şey olmadan değişkeni ayarladığını varsayar
Timo Huovinen

9
@TimoHuovinen Java'daki bir kullanıcıdan obj.setAttr('foo')" farklı bir şey olmadan değişkeni ayarladığı" varsayımı nasıl farklıdır ? Herkese açık bir yöntemse, genel bir yöntemdir. Bir yan etki elde etmek için kullanırsanız ve herkese açıksa, o zaman sadece amaçlanan yan etki olmuş gibi çalışan her şeye güvenebilmeniz daha iyi olurdu ( diğer tüm uygulama detayları ve diğer yan etkiler, kaynak kullanımı, ne olursa olsun) , kullanıcının endişelerinden gizlenir). Bu Python ile kesinlikle farklı değil. Python'un efekti elde etmek için sözdizimi sadece daha basittir.
ely

25

Eh, sadece değişkenlerinizin / nesnelerin kapsüllenmesi ve güvenliği için gerekli olsalar bile, gerçek bir Nesne Odaklı Programı kodlamak istiyorsak, o zaman AKSESUARLARI AŞMAK DURMALIYIM , çünkü bazen çok bağımlıyız gerçekten gerekli olmadığında ve bu, değişkenleri herkese açık hale getirmemizle hemen hemen aynıdır.


24

Teşekkürler, bu düşüncemi gerçekten açıklığa kavuşturdu. Şimdi, alıcıları ve ayarlayıcıları kullanmamak için (neredeyse) 10 (neredeyse) iyi neden:

  1. Değeri ayarlamak ve almaktan daha fazlasını yapmanız gerektiğini fark ettiğinizde, alanı özel hale getirebilirsiniz, bu da size doğrudan nereden eriştiğinizi anında söyleyecektir.
  2. Orada gerçekleştirdiğiniz herhangi bir doğrulama yalnızca bağlamdan bağımsız olabilir; bu, nadiren uygulamada olur.
  3. Ayarlanan değeri değiştirebilirsiniz - bu, arayan kişi size [şok korkusu] OLDUĞU GİBİ saklamanızı istediği bir değeri ilettiğinde mutlak bir kabus olur.
  4. Dahili temsili gizleyebilirsiniz - harika, böylece tüm bu işlemlerin simetrik olduğundan emin olursunuz değil mi?
  5. Genel arayüzünüzü sayfaların altındaki değişikliklerden yalıttınız - bir arayüz tasarlıyorsanız ve bir şeye doğrudan erişimin uygun olup olmadığından emin değilseniz, tasarım yapmaya devam etmeliydiniz.
  6. Bazı kütüphaneler bunu bekler, ancak çok fazla değil - yansıma, serileştirme, sahte nesneler, ortak alanlarla iyi çalışır.
  7. Bu sınıfı devralarak, varsayılan işlevselliği geçersiz kılabilirsiniz - başka bir deyişle, yalnızca uygulamayı gizlemekle kalmayıp tutarsız hale getirerek arayanları GERÇEKTEN şaşırtabilirsiniz.

Sadece son gittiğim (N / A veya D / C) ...


11
Bence en önemli argüman şu: "Eğer bir arayüz tasarlıyorsanız ve bir şeye doğrudan erişimin iyi olup olmadığından emin değilseniz, o zaman tasarım yapmaya devam etmeliydiniz." Alıcılar / ayarlayıcılar ile ilgili en önemli sorun budur: Bir sınıfı yalnızca (az ya da çok) ortak alanlardan oluşan bir konteynere indirirler. Ancak gerçek OOP'de, bir nesne bir veri alanı konteynerinden daha fazlasıdır. Bu durumu manipüle etmek için durumu ve algoritmaları içerir. Bu ifade ile ilgili önemli olan, devletin kapsüllenmesi ve yalnızca nesne tarafından sağlanan algoritmalar tarafından manipüle edilmesi gerektiğidir.
sbi

22

Biraz geç olduğunu biliyorum, ancak performansla ilgilenen bazı insanlar olduğunu düşünüyorum.

Küçük bir performans testi yaptım. Ben bir tamsayı tutan bir "NumberHolder" sınıfı yazdım. Bu Tamsayı'yı getter yöntemini kullanarak anInstance.getNumber()veya numarayı kullanarak doğrudan erişerek okuyabilirsiniz anInstance.number. Programım her iki yolla da 1.000.000.000 kez okur. Bu işlem beş kez tekrarlanır ve saat yazdırılır. Aşağıdaki sonucu aldım:

Time 1: 953ms, Time 2: 741ms
Time 1: 655ms, Time 2: 743ms
Time 1: 656ms, Time 2: 634ms
Time 1: 637ms, Time 2: 629ms
Time 1: 633ms, Time 2: 625ms

(Zaman 1 doğrudan yoldur, Zaman 2 alıcıdır)

Görüyorsunuz, yakalayıcı (neredeyse) her zaman biraz daha hızlıdır. Sonra farklı sayılarla denedim. 1 milyon yerine 10 milyon ve 0,1 milyon kullandım. Sonuçlar:

10 milyon döngü:

Time 1: 6382ms, Time 2: 6351ms
Time 1: 6363ms, Time 2: 6351ms
Time 1: 6350ms, Time 2: 6363ms
Time 1: 6353ms, Time 2: 6357ms
Time 1: 6348ms, Time 2: 6354ms

10 milyon döngü ile süreler neredeyse aynı. İşte 100 bin (0,1 milyon) döngü:

Time 1: 77ms, Time 2: 73ms
Time 1: 94ms, Time 2: 65ms
Time 1: 67ms, Time 2: 63ms
Time 1: 65ms, Time 2: 65ms
Time 1: 66ms, Time 2: 63ms

Ayrıca farklı miktarlardaki döngülerde, alıcı normal yoldan biraz daha hızlıdır. Umarım bu sana yardımcı olmuştur.


3
Bir nesnenin adresini yüklemek ve üyelere erişmek için bir uzaklık eklemek yerine belleğe erişmek için bir işlev çağrısına sahip "fark edilir" bir ek yük vardır. Muhtemelen VM, getter'inizin zaten optimize edilmiş olmasıdır. Ne olursa olsun, bahsedilen yük, alıcıların / pasiflerin tüm faydalarını kaybetmeye değmez.
Alex

16

Mevcut teslimatınız için gerekli olmadıkça alıcı ayarlayıcılarını kullanmayın Gelecekte ne olacağı hakkında çok fazla düşünmeyin, değiştirilecek herhangi bir şey varsa, çoğu üretim uygulamasında, sistemde bir değişiklik talebidir.

Basit, kolay düşünün, gerektiğinde karmaşıklık katın.

Derin teknik bilginin işletme sahiplerinin bilgisizliğinden faydalanmam, çünkü doğru olduğunu düşündüğüm için ya da yaklaşımı sevdim.

Sadece erişim değiştiricileri ve n mantık işlemini doğrulamak için bazı yöntemler ile alıcılar ayarlayıcıları olmadan yazılmış büyük bir sistemim var. Kesinlikle ihtiyacınız varsa. Herhangi bir şey kullanın.


16

Alıcıları ve ayarlayıcıları kullanıyoruz:

  • yeniden kullanılabilirlik için
  • programlamanın ilerleyen aşamalarında doğrulama yapmak

Alıcı ve ayarlayıcı yöntemleri, özel sınıf üyelerine erişmek için ortak arabirimlerdir.


Kapsülleme mantra

Kapsülleme mantrası alanları özel ve yöntemleri herkese açık hale getirmektir.

Alıcı Yöntemleri: Özel değişkenlere erişebiliriz.

Setter Yöntemleri: Özel alanları değiştirebiliriz.

Alıcı ve ayarlayıcı yöntemleri yeni işlevler eklemese de, daha sonra bu yöntemi yapmak için fikrimizi değiştirebiliriz

  • daha iyi;
  • daha güvenli; ve
  • Daha hızlı.

Bir değer kullanılabilecek her yerde, bu değeri döndüren bir yöntem eklenebilir. Onun yerine:

int x = 1000 - 500

kullanım

int x = 1000 - class_name.getValue();

Layman'ın terimleriyle

"Kişi" sınıfının temsili

Bunun ayrıntılarını saklamamız gerektiğini varsayalım Person. Bu Personalanlar vardır name, ageve sex. Bunu yapmak name, ageve için yöntemler oluşturmayı içerir sex. Biz başka bir kişiyi oluşturmak gerekirse Şimdi, için yöntemler oluşturmak için gerekli olur name, age, sexbaştan.

Bunu yapmak yerine, class(Person)alıcı ve ayarlayıcı yöntemleriyle bir fasulye oluşturabiliriz . Yani yarın class(Person class)yeni bir kişi eklememiz gerektiğinde bu fasulyenin nesnelerini yaratabiliriz (şekle bakınız). Böylece fasulye sınıfının alanlarını ve yöntemlerini yeniden kullanıyoruz, ki bu çok daha iyi.


15

Java davası için bunu düşünerek biraz zaman geçirdim ve asıl nedenlerin olduğuna inanıyorum:

  1. Uygulamaya değil, arayüze kod
  2. Arabirimler alanları değil, yalnızca yöntemleri belirtir

Başka bir deyişle, arabirimdeki bir alanı belirtmenin tek yolu, yeni bir değer yazmak için bir yöntem ve geçerli değeri okumak için bir yöntem sağlamaktır.

Bu yöntemler rezil alıcı ve ayarlayıcı ....


1
Tamam, ikinci soru; kaynağı kimseye ihraç etmediğiniz ve kaynak üzerinde tam kontrole sahip olduğunuz bir proje ... alıcılar ve pasiflerle bir şey kazanıyor musunuz?
Dean J

2
Önemsiz bir Java projesinde, işleri yönetilebilir ve test edilebilir hale getirmek için arayüzlere kod yazmanız gerekir (maketleri ve proxy nesnelerini düşünün). Arayüz kullanıyorsanız, alıcılara ve ayarlayıcılara ihtiyacınız vardır.
Thorbjørn Ravn Andersen

15

Tembel yükleme için yararlı olabilir. Söz konusu nesnenin bir veritabanında saklandığını ve ihtiyacınız olmadığı sürece onu almak istemediğinizi varsayalım. Nesne bir alıcı tarafından alınırsa, iç nesne birisi isteyinceye kadar boş olabilir, o zaman onu alıcıya ilk çağrıda alabilirsiniz.

Birkaç farklı web servis çağrısından bazı verileri yükleyen bir projede bana verilen bir temel sayfa sınıfım vardı, ancak bu web servis çağrılarındaki veriler her zaman tüm alt sayfalarda kullanılmadı. Web hizmetleri, tüm faydaları için, yeni "yavaş" tanımlarına öncülük eder, böylece gerekmiyorsa bir web hizmeti çağrısı yapmak istemezsiniz.

Genel alanlardan alıcılara geçtim ve şimdi alıcılar önbelleği kontrol ediyor ve eğer yoksa web servisini arayın. Biraz sargı ile çok sayıda web hizmeti çağrısı engellendi.

Böylece alıcı beni her alt sayfada neye ihtiyacım olacağını anlamaya çalışmaktan kurtarıyor. İhtiyacım olursa, alıcıyı ararım ve eğer zaten yoksa benim için bulmaya gider.

    protected YourType _yourName = null;
    public YourType YourName{
      get
      {
        if (_yourName == null)
        {
          _yourName = new YourType();
          return _yourName;
        }
      }
    }

Öyleyse, alıcı ayarlayıcıyı çağırıyor mu?
icc97

Geçmişte nasıl yaptığımın bir kod örneğini ekledim - esas olarak, gerçek sınıfı korumalı bir üyede saklarsınız, sonra korumalı üyeyi get erişimcisinde döndürür ve başlatılmamışsa başlatırsınız.
quillbreaker


13

Şimdiye kadar cevaplarda kaçırdığım bir yön, erişim belirtimi:

  • üyeler için hem ayarlama hem de alma için yalnızca bir erişim belirtiminiz vardır
  • ayarlayıcılar ve alıcılar için ince ayar yapabilir ve ayrı olarak tanımlayabilirsiniz

13

DÜZENLEME: Bu soruyu cevapladım, çünkü bunu soran programlamayı öğrenen bir sürü insan var ve cevapların çoğu teknik olarak yetkin, ancak bir acemi iseniz anlamak kolay değil. Hepimiz yeniydik, bu yüzden elimi daha acemi dostu bir cevapta deneyeceğim diye düşündüm.

İki ana polimorfizm ve validasyon. Sadece aptal bir veri yapısı olsa bile.

Diyelim ki bu basit sınıfımız var:

public class Bottle {
  public int amountOfWaterMl;
  public int capacityMl;
}

İçinde ne kadar sıvı olduğunu ve kapasitesinin ne olduğunu tutan çok basit bir sınıf (mililitre cinsinden).

Yaptığımda ne olur:

Bottle bot = new Bottle();
bot.amountOfWaterMl = 1500;
bot.capacityMl = 1000;

Bunun çalışmasını beklemezdin, değil mi? Bir tür akıl sağlığı kontrolünün olmasını istiyorsunuz. Daha da kötüsü, maksimum kapasiteyi hiç belirtmezsem ne olur? Ah canım, bir sorunumuz var.

Ama başka bir sorun daha var. Şişeler sadece bir çeşit konteyner olsaydı ne olurdu? Ya kapasiteleri ve miktarları sıvı dolu olan birkaç kabımız olsaydı? Sadece bir arayüz oluşturabilirsek, programımızın geri kalanının bu arayüzü kabul etmesine izin verebilirdik ve şişeler, bidonlar ve her türlü şey birbirinin yerine kullanılabilirdi. Daha iyi olmaz mıydı? Arayüzler yöntem gerektirdiğinden, bu da iyi bir şeydir.

Sonunda şöyle bir şey buluruz:

public interface LiquidContainer {
  public int getAmountMl();
  public void setAmountMl(int amountMl);
  public int getCapacityMl();
}

Harika! Ve şimdi Şişeyi buna değiştiriyoruz:

public class Bottle extends LiquidContainer {
  private int capacityMl;
  private int amountFilledMl;

  public Bottle(int capacityMl, int amountFilledMl) {
    this.capacityMl = capacityMl;
    this.amountFilledMl = amountFilledMl;
    checkNotOverFlow();
  }

  public int getAmountMl() {
    return amountFilledMl;
  }

  public void setAmountMl(int amountMl) {
     this.amountFilled = amountMl;
     checkNotOverFlow();
  }
  public int getCapacityMl() {
    return capacityMl;
  }

  private void checkNotOverFlow() {
    if(amountOfWaterMl > capacityMl) {
      throw new BottleOverflowException();
    }
}

BottleOverflowException tanımını okuyucuya alıştırma olarak bırakacağım.

Şimdi bunun ne kadar daha sağlam olduğuna dikkat edin. Bottle yerine LiquidContainer'ı kabul ederek kodumuzdaki her türlü konteyneri ele alabiliriz. Ve bu şişelerin bu tür şeylerle nasıl başa çıktığı farklı olabilir. Değiştiğinde durumlarını diske yazan şişeleriniz olabilir ya da SQL veritabanlarına veya GNU'ya kaydedilen şişelerin başka neleri bildiğini görebilirsiniz.

Ve tüm bunların çeşitli boğmacalarla başa çıkmanın farklı yolları olabilir. Şişe sadece kontrol eder ve taşarsa bir RuntimeException kurar. Ama bu yanlış bir şey olabilir. (Hata işleme konusunda yararlı bir tartışma var, ancak burada bunu çok basit bir şekilde saklıyorum. Yorumdaki insanlar muhtemelen bu basit yaklaşımın kusurlarını gösterecektir.))

Ve evet, çok basit bir fikirden hızlıca daha iyi cevaplar almaya gittik.

Bir şişenin kapasitesini değiştiremeyeceğinizi lütfen unutmayın. Şimdi taşa konmuş. Bunu son olarak ilan ederek bir int ile yapabilirsiniz. Ancak bu bir liste olsaydı, listeyi boşaltabilir, listeye yeni şeyler ekleyebilir ve böyle devam edebilirsiniz. İç kısımlara dokunmaya erişimi sınırlandıramazsınız.

Herkesin ele almadığı üçüncü şey de var: alıcılar ve ayarlayıcılar yöntem çağrılarını kullanıyor. Bu, diğer her yerde normal yöntemlere benzedikleri anlamına gelir. DTO'lar ve şeyler için garip bir sözdizimine sahip olmak yerine, her yerde aynı şeye sahipsiniz.


2
Okuduğum arayüzlerin ilk yarı iyi açıklaması için teşekkürler, "uzaktan kumandalar" veya "arabalar" ile ilgili referansları
sanslar

1
"Değiştiğinde durumlarını diske yazan şişelere veya SQL veritabanlarına kaydedilen şişelere sahip olabilirsiniz." Ama neyse, sunmak için güzel bir fikir!
Xerus

10

"Özellikleri" (C ++, Java) desteklemeyen veya alanları özelliklere (C #) değiştirirken istemcilerin yeniden derlenmesini gerektiren dillerde, get / set yöntemlerini kullanmak daha kolaydır. Örneğin, bir setFoo yöntemine doğrulama mantığı eklemek, bir sınıfın genel arabiriminin değiştirilmesini gerektirmez.

"Gerçek" özellikleri destekleyen dillerde (Python, Ruby, belki Smalltalk?) Yöntem almanın / ayarlamanın anlamı yoktur.


1
Re: C #. Bir get / set'e işlevsellik eklerseniz, bu yine de yeniden derleme gerektirmez mi?
steamer25

@ steamer25: üzgünüm, yanlış yazılmış. Demek istediğim, sınıfın müşterilerinin yeniden derlenmesi gerekecek.
John Millikin

5
Bir setFoo yöntemine doğrulama mantığı eklemek, bir sınıfın arabirimini dil düzeyinde değiştirmeyi gerektirmez , ancak ön koşulları değiştirdiği için gerçek arabirimi , yani sözleşmeyi değiştirir. Neden kimse bir kırılma değişiklik olarak değil tedavi etmek derleyici isteyeyim öyle ?
R. Martinho Fernandes

@ R.MartinhoFernandes biri bu "kırık" sorunu nasıl düzeltir? Derleyici kırılıp kırılmadığını söyleyemez. Bu sadece başkaları için kütüphaneler yazarken bir endişe kaynağıdır, ama burada evrensel zOMG olarak ejderhalar yapıyorsunuz!
Phil

3
Yanıtta belirtildiği gibi, yeniden derleme gerekli, derleyicinin sizi olası bir kırılma değişikliğinden haberdar etmesinin bir yoludur. Ve yazdığım neredeyse her şey etkili bir şekilde "başkaları için bir kütüphane" dir, çünkü yalnız çalışmıyorum. Projedeki diğer kişilerin kullanacağı arayüzlere sahip kod yazıyorum. Fark ne? Cehennem, bu arayüzlerin kullanıcısı olsam bile, neden kodumu daha düşük kalite standartlarında tutmalıyım? Onları yazan kişi ben olsam bile, zahmetli arayüzlerle çalışmayı sevmiyorum.
R. Martinho Fernandes

6

OO tasarımının temel prensiplerinden biri: Kapsülleme!

Size birçok avantaj sağlar, bunlardan biri sahne arkasındaki alıcı / ayarlayıcı uygulamasını değiştirebilmenizdir, ancak bu değerdeki herhangi bir tüketici, veri türü aynı kaldığı sürece çalışmaya devam edecektir.


23
Kapsül alıcılar ve ayarlayıcılar gülünç derecede incedir. Buraya bakın .
sbi

Genel arayüzünüz 'foo'nun' T 'türünde olduğunu ve herhangi bir şeye ayarlanabileceğini belirtiyorsa, bunu asla değiştiremezsiniz. İkincisini 'Y' türünde yapmaya karar veremez ya da boyut kısıtlamaları gibi kurallar koyamazsınız. Bu nedenle, çok fazla ayarlanamayan / yapmayan hiçbir şey elde edemiyorsanız, bir kamusal alanın sunmayacağı hiçbir şey kazanamazsınız ve kullanımı daha hantal hale getirirsiniz. Nesne null olarak ayarlanabilir veya değerin bir aralık dahilinde olması gibi bir kısıtlamanız varsa, evet, herkese açık bir küme yöntemi gerekir, ancak yine de bu değeri ' t değişiklik
thecoshman

Bir sözleşme neden değişemez?
Phil

5
Sözleşmeler değişirse neden işler derlenmeye devam etsin?
R. Martinho Fernandes

5

Aşağıdaki durumlarda alıcıları ve ayarlayıcıları kullanmalısınız:

  • Kavramsal bir özellik olan bir şeyle uğraşıyorsunuz, ancak:
    • Dilinizde özellikler (veya Tcl'nin değişken izleri gibi benzer bir mekanizma) yok veya
    • Dilinizin özellik desteği bu kullanım durumu için yeterli değil veya
    • Dilinizin (veya bazen çerçevenizin) deyimsel kuralları, bu kullanım durumu için alıcıları veya ayarlayıcıları teşvik eder.

Bu çok nadiren genel bir OO sorusudur; farklı diller (ve farklı kullanım örnekleri) için farklı cevapları olan dile özgü bir sorudur.


Bir OO teorisi açısından, alıcılar ve ayarlayıcılar işe yaramaz. Sınıfınızın arayüzü, ne olduğu, ne olduğu değil. (Değilse, yanlış sınıfı yazdınız.) Çok basit durumlarda, örneğin bir sınıfın sadece dikdörtgen koordinatlarda bir noktayı temsil ettiği durumlarda, * özellikler arayüzün bir parçasıdır; alıcılar ve ayarlayıcılar sadece bulut. Ancak çok basit durumlarda, ne nitelikler ne de alıcılar ve ayarlayıcılar arayüzün bir parçası değildir.

Başka bir deyişle: Sınıfınızın tüketicilerinin bir spamözelliğiniz olduğunu bile bilmemesi gerektiğine inanıyorsanız, bunu willy-nilly'yi değiştiremezsiniz, ardından onlaraset_spam yöntem vermek yapmak istediğiniz son şeydir.

* Bu basit sınıf için bile, xve ydeğerlerinin ayarlanmasına izin vermek istemeyebilirsiniz . Bu gerçekten bir sınıf ise, böyle yöntemler olmamalıdır translate, rotatevb? Eğer dilinizde kayıtlar / yapılar / adlandırılmış tuples olmadığı için bu sadece bir sınıfsa, bu gerçekten OO meselesi değildir…


Ama hiç kimse genel OO tasarımı yapmıyor. Belirli bir dilde tasarım ve uygulama yapıyorlar. Ve bazı dillerde, alıcılar ve ayarlayıcılar işe yaramaz olmaktan uzaktır.

Dilinizde özellikler yoksa, kavramsal olarak bir nitelik olan, ancak aslında hesaplanan veya doğrulanan vb. Bir şeyi temsil etmenin tek yolu alıcılar ve ayarlayıcılardır.

Dilinizde özellikler olsa bile, bunların yetersiz veya uygunsuz olduğu durumlar olabilir. Örneğin, alt sınıfların bir özniteliğin anlambilimini denetlemesine izin vermek istiyorsanız, dinamik erişime sahip olmayan dillerde, bir alt sınıf bir öznitelik için hesaplanmış bir özelliğin yerini alamaz.

"Ya daha sonra uygulamamı değiştirmek istersem?" soru (hem OP'nin sorusu hem de kabul edilen cevapta farklı ifadelerde birden çok kez tekrarlanır): Bu gerçekten saf bir uygulama değişikliğiyse ve bir öznitelikle başladıysanız, arayüzü etkilemeden bir özelliğe değiştirebilirsiniz. Tabii ki, diliniz bunu desteklemiyorsa. Yani bu yine aynı durum.

Ayrıca, kullandığınız dilin (veya çerçevenin) deyimlerini takip etmek de önemlidir. C # 'da güzel Ruby tarzı kod yazarsanız, sizden başka deneyimli C # geliştiricisi onu okumakta zorlanır ve bu kötüdür. Bazı diller, sözleşmeleri etrafında diğerlerinden daha güçlü kültürlere sahiptir. - ve deyimsel alıcıların nasıl olduğu spektrumunun karşı uçlarında olan Java ve Python'un en güçlü iki kültüre sahip olması tesadüf olmayabilir.

İnsan okuyucuların ötesinde, sözleşmeleri izlemenizi ve istemezseniz hayatınızı zorlaştıracak kütüphaneler ve araçlar olacaktır. Hoj Interface Builder widget'larını ObjC özelliklerinden başka bir şeye ya da belirli Java alay kitaplıklarını alıcılar olmadan kullanmak, hayatınızı daha da zorlaştırıyor. Araçlar sizin için önemliyse, onlarla savaşmayın.


4

Nesne yönelimi tasarım açısından, her iki alternatif de sınıfların kapsüllenmesini zayıflatarak kodun korunmasına zarar verebilir. Bir tartışma için bu mükemmel makaleyi inceleyebilirsiniz: http://typicalprogrammer.com/?p=23


3

Getter ve setter yöntemleri erişimci yöntemleridir, yani özel sınıf üyelerini değiştirmek için genellikle genel bir arabirimdir. Bir özelliği tanımlamak için getter ve setter yöntemlerini kullanırsınız. Sınıf içinde yöntemler olarak tanımlasanız bile, alıcı ve ayarlayıcı yöntemlerine sınıf dışındaki özellikler olarak erişirsiniz. Sınıf dışındaki bu özelliklerin, sınıftaki özellik adından farklı bir adı olabilir.

Alıcı ve ayarlayıcı yöntemlerini kullanmanın, benzer özelliklere erişebileceğiniz karmaşık işlevlere sahip üyeler oluşturmanıza izin verme gibi bazı avantajları vardır. Ayrıca salt okunur ve salt yazılır özellikler oluşturmanıza da olanak tanır.

Alıcı ve ayarlayıcı yöntemleri yararlı olsa da, bunları aşırı kullanmamaya dikkat etmelisiniz, çünkü diğer sorunların yanı sıra, kod bakımını belirli durumlarda daha zor hale getirebilirler. Ayrıca, genel üyeler gibi sınıf uygulamanıza erişim sağlarlar. OOP uygulaması, bir sınıf içindeki özelliklere doğrudan erişimi engeller.

Sınıf yazarken, her zaman örnek değişkenlerinizi mümkün olduğunca özel hale getirmeniz ve buna göre alıcı ve ayarlayıcı yöntemleri eklemeniz önerilir. Bunun nedeni, kullanıcıların sınıflarınızdaki belirli değişkenleri değiştirmesine izin vermek istemeyebileceğiniz birkaç kez olmasıdır. Örneğin, belirli bir sınıf için oluşturulan örnek sayısını izleyen özel bir statik yönteminiz varsa, kullanıcının bu sayacı kod kullanarak değiştirmesini istemezsiniz. Yalnızca yapıcı ifadesi çağrıldığında bu değişkeni artırmalıdır. Bu durumda, özel bir örnek değişkeni oluşturabilir ve yalnızca sayaç değişkeni için bir alıcı yöntemine izin verebilirsiniz; bu, kullanıcıların geçerli değeri yalnızca alıcı yöntemini kullanarak alabilecekleri ve yeni değerler ayarlayamayacakları anlamına gelir. setter yöntemini kullanarak.


3

Kod gelişir . veri üyesi korumasına ihtiyacınızprivate olduğunda mükemmeldir . Sonunda tüm sınıflar iyi tanımlanmış bir arayüze sahip bir çeşit "miniprogram" olmalıdır . .

Bununla birlikte, yazılım geliştirme , sınıfın son versiyonunu, ilk denemede bir miktar dökme demir heykeline basıyormuş gibi ayarlamakla ilgili değildir. Onunla çalışırken, kod daha çok kil gibidir. Siz geliştirdikçe ve çözdüğünüz problem alanı hakkında daha fazla bilgi edindikçe gelişir . Gelişim sırasında sınıflar birbirleriyle etkileşime girmeleri gerekenden (çarpanlarına ayırmayı planladığınız bağımlılık), birleşerek veya bölünebilirler. Bence bu tartışma dini olarak yazmak istemeyen insanlara dayanıyor

int getVar() const { return var ; }

Yani:

doSomething( obj->getVar() ) ;

Onun yerine

doSomething( obj->var ) ;

Sadece getVar()görsel olarak gürültülü olmakla kalmaz, gettingVar()bir şekilde gerçekte olduğundan daha karmaşık bir süreçtir. (Sınıf yazarı olarak) kutsallığı varnasıl değerlendirdiğinizi, bir passthru ayarlayıcı varsa, sınıfınızın bir kullanıcısı için özellikle kafa karıştırıcıdır - o zaman bu kapıları değerli ısrar etmek için "korumak" gibi görünüyor, (ancak kutsallığını koruduğunuzda varbile) var, kimsenin set varne yaptıklarına bakmadan, içeri girme ve istedikleri değere sahip olma yeteneğiyle çok fazla değmez .

Bu yüzden aşağıdaki gibi program (bir "çevik" tipi yaklaşım varsayalım - yani tam olarak ne yapacağını bilmeden kod yazarken / ayrıntılı bir şelale tarzı arayüz seti planlamak için zaman veya deneyimim yok):

1) Veri ve davranışa sahip temel nesneler için tüm kamu üyeleri ile başlayın. Bu yüzden tüm C ++ "örnek" kodumda beni her yerde kullanmak structyerine fark edeceksiniz class.

2) Bir nesnenin bir veri üyesi için iç davranışı yeterince karmaşık hale geldiğinde (örneğin, bir iç std::listdüzeni bir tür düzende tutmayı sever ), erişimci türü işlevleri yazılır. Ben kendim programlama ediyorum çünkü ben her zaman üye set yok privatehemen, ama sınıf üyesi evrimi aşağı bir yere "terfi" olacak ya protectedyaprivate .

3) tamamen fleshed ve donanımları hakkında katı kurallar (yani sahiptir Sınıflar onlar yaptıklarını tam olarak bilmek ve "lanet" (teknik terim için değildir) verilmiştir onun iç elemanlar) classtanımı, özel üyelerini varsayılan, ve yalnızca seçili birkaç üyenin olmasına izin verilir public.

Bu yaklaşımın, bir sınıfın evriminin ilk aşamalarında, çok sayıda veri üyesi göç ettiğinde, etrafında değiştiğinde vb.


1
"... sadece içlerine vidalayamayacağınız iyi tanımlanmış bir arayüz" ve ayarlayıcılarda doğrulama.
Agi Hammerthief

3

Erişimcileri kullanmayı düşünmek için iyi bir sebep var, mülk mirası yok. Sonraki örneğe bakın:

public class TestPropertyOverride {
    public static class A {
        public int i = 0;

        public void add() {
            i++;
        }

        public int getI() {
            return i;
        }
    }

    public static class B extends A {
        public int i = 2;

        @Override
        public void add() {
            i = i + 2;
        }

        @Override
        public int getI() {
            return i;
        }
    }

    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.i);
        a.add();
        System.out.println(a.i);
        System.out.println(a.getI());
    }
}

Çıktı:

0
0
4

3

Harfler ve ayarlayıcılar , Nesneye Yönelik Programlamanın temel yönlerinden ikisini uygulamak için kullanılır:

  1. Soyutlama
  2. kapsülleme

Bir Çalışan sınıfımız olduğunu varsayalım:

package com.highmark.productConfig.types;

public class Employee {

    private String firstName;
    private String middleName;
    private String lastName;

    public String getFirstName() {
      return firstName;
    }
    public void setFirstName(String firstName) {
       this.firstName = firstName;
    }
    public String getMiddleName() {
        return middleName;
    }
    public void setMiddleName(String middleName) {
         this.middleName = middleName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFullName(){
        return this.getFirstName() + this.getMiddleName() +  this.getLastName();
    }
 }

Burada Tam Ad'ın uygulama ayrıntıları kullanıcıdan gizlenir ve genel bir öznitelikten farklı olarak doğrudan kullanıcı tarafından erişilebilir değildir.


1
Bana göre benzersiz bir şey yapmayan tonlarca alıcı ve ayarlayıcıya sahip olmak işe yaramaz. getFullName bir istisnadır çünkü başka bir şey yapar. Sadece üç değişkenin herkese açık olması, getFullName'in tutulması, programın okunmasını kolaylaştıracak, ancak yine de tam adı olan şeyi gizleyecektir. Genellikle ben a alıcılar ve ayarlayıcılar ile tamamen iyiyim. benzersiz bir şey yaparlar ve / veya b. sadece bir tane var, evet herkese açık bir finali olabilirdi ve nah hariç
FacelessTiger

1
Bunun yararı, arabirimi değiştirmeden sınıfın içini değiştirebilmenizdir. Diyelim ki üç özellik yerine, bir tane dizeniz vardı. Alıcıları ve ayarlayıcıları kullanıyorsanız, bu değişikliği yapabilir ve daha sonra alıcıları / ayarlayıcıları güncelleyerek adların [0] adının, adlarının [1] orta olduğunu vb. , kullandıkları firstName özelliği artık mevcut olmadığından, Çalışanlara erişilen her sınıfı da değiştirmeniz gerekir.
Andrew Hows

1
Gerçek hayatta gördüklerimden, insanlar sınıfın
içini

2

Diğer bir kullanım (özellikleri destekleyen dillerde) ayarlayıcıların ve alıcıların bir işlemin önemsiz olduğunu ima edebilmeleridir. Genellikle, bir mülkte hesaplama açısından pahalı olan herhangi bir şey yapmaktan kaçınmak istersiniz.


Bir alıcı veya ayarlayıcının pahalı bir işlem olmasını asla beklemezdim. Bu gibi durumlarda bir fabrikayı daha iyi kullanın: İhtiyacınız olan tüm ayarlayıcıları kullanın ve sonra pahalı executeveya buildyöntemi çağırın .
Hubert Grzeskowiak

2

Alıcıların / ayarlayıcıların nispeten modern bir avantajı, etiketli (dizinlenmiş) kod editörlerinde koda göz atmayı kolaylaştırmasıdır. Örneğin, bir üyeyi kimin ayarladığını görmek istiyorsanız, ayarlayıcının arama hiyerarşisini açabilirsiniz.

Öte yandan, üye herkese açıksa, araçlar üyeye okuma / yazma erişimini filtrelemeyi mümkün kılmaz. Bu yüzden üyenin tüm kullanımları olsa trudge gerekir.


doğru klişe yapabilirsiniz> bir üye / ayarlayıcıda olduğu gibi bir üye üzerinde kullanım bulabilirsiniz
Eildosa

2

Nesneye yönelik bir dilde yöntemler ve bunların erişim değiştiricileri, o nesnenin arabirimini bildirir. Yapıcı ile erişimci ve mutasyona uğratıcı yöntemler arasında geliştiricinin bir nesnenin iç durumuna erişimi kontrol etmesi mümkündür. Değişkenler yalnızca kamuya açıklanırsa, bu erişimi düzenlemenin bir yolu yoktur. Ayarlayıcıları kullanırken, kullanıcıyı ihtiyacımız olan girdi için kısıtlayabiliriz. Bu çok değişkenin beslemesinin uygun bir kanaldan geleceği ve kanal bizim tarafımızdan önceden tanımlandığı anlamına gelir. Bu yüzden ayarlayıcıları kullanmak daha güvenlidir.


1

Ayrıca bu, sınıfınızı "geleceğe dönük" hale getirmektir. Özellikle, bir alandan bir mülke geçmek bir ABI molasıdır, bu yüzden daha sonra sadece "alanı ayarla / al" dan daha fazla mantığa ihtiyacınız olduğuna karar verirseniz, elbette herhangi bir şey için sorun yaratan ABI'yi kırmanız gerekir. sınıfınıza karşı derlenmiş başka.


2
Sanırım alıcının veya ayarlayıcının davranışını değiştirmenin o zaman kırıcı bir değişiklik olmadığını düşünüyorum. </sarcasm>
R. Martinho Fernandes

@ R.MartinhoFernandes her zaman olmak zorunda değildir. TBYS
Phil

1

Sadece ek açıklama fikrini atmak istiyorum: @getter ve @setter. @Getter ile obj = class.field = class.field = obj komutunu kullanamamanız gerekir. @Setter ile, tam tersi. @Getter ve @setter ile her ikisini de yapabilmeniz gerekir. Bu, çalışma zamanında önemsiz yöntemleri çağırmayarak kapsüllemeyi koruyacak ve zamanı azaltacaktır.


1
Çalışma zamanında "önemsiz yöntemler" ile uygulanacaktı. Aslında, muhtemelen önemsiz değil.
Phil

Bugün bu bir ek açıklama ön işlemcisi ile yapılabilir.
Thorbjørn Ravn Andersen
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.