Alıcıların ve ayarlayıcıların içinde nelere izin verilmeli?


45

Alıcı ve ayarlayıcı yöntemleri ve kapsülleme hakkında ilginç bir internet tartışmasına girdim. Birileri yapmaları gereken tek şey, onları "saf" tutmak ve enkapsülasyonu sağlamak için bir değişken (atamalar) veya değişken bir erişim (alıcılar) olduğunu söyledi.

  • Bunun, alıcıların ve belirleyicilerin ilk sırada yer almasının amacını tamamen ortadan kaldıracağı doğru muyum ve doğrulama ve diğer mantıklara (elbette garip yan etkiler olmadan) izin verilmeli mi?
  • Doğrulama ne zaman yapılmalı?
    • Değeri ayarlarken, ayarlayıcı içinde (nesneyi geçersiz bir duruma girmekten korumak için - bence)
    • Değeri ayarlamadan önce, ayarlayıcı dışında
    • Nesnenin içinde, değer her kullanılmadan önce
  • Bir ayarlayıcının değeri değiştirmesine izin verilir mi (belki de geçerli bir değeri bir kanonik içsel gösterime dönüştürebilirsiniz)?

18
Alıcı ve ayarlayıcılarla ilgili en iyi şey, bunlara sahip olmama , yani ayarlayıcıdan ayrılma ve salt okunur bir özelliğe sahip olmanız, alıcıdan ayrılma ve geçerli değeri kimsenin işi olmayan yapılandırma seçeneğine sahip olmanızdır.
Michael Borgwardt

7
@MichaelBorgwardt Temiz bir “anlat, sorma” arayüzüne sahip olmak için her ikisini de bırakın. Özellikler = potansiyel kod kokusu.
Konrad Rudolph


2
Setörler ayrıca bir Etkinlik düzenlemek için de kullanılabilir .
John Isaiah Carmona

@KonradRudolph İfadenizi kabul ediyorum, ancak "potansiyel" kelimesini gerçekten vurgulamak istiyorum.
Phil

Yanıtlar:


37

Üniversitede C ++ öğrenirken öğretim görevlimle de benzer bir tartışmayı hatırlıyorum. Bir değişkeni halka açık hale getirdiğimde alıcıları ve ayarlayıcıları kullanma noktasını göremedim. Şimdi yılların tecrübesiyle daha iyi anlıyorum ve sadece “kapsüllemeyi sürdürmek” demekten daha iyi bir sebep öğrendim.

Alıcıları ve ayarlayıcıları tanımlayarak tutarlı bir arayüz sağlarsınız, böylece uygulamanızı değiştirmek isterseniz bağımlı kodları kırmanız daha az olasıdır. Bu, özellikle sınıflar bir API ile maruz kaldığınızda ve diğer uygulamalarda veya 3. şahıslar tarafından kullanıldığında önemlidir. Peki ya alıcı ya da düzenleyiciye giren şeyler?

Alıcılar genellikle bir değere erişim için basit ve salak bir geçiş yolu olarak uygulandığından daha iyidir, çünkü bu davranışlarını tahmin edilebilir kılar. Genel olarak söylüyorum, çünkü hesaplamaların manipüle ettiği değerlere ve hatta koşullu kodlara erişmek için alıcıların kullanıldığı durumlar gördüm. Tasarım zamanında kullanmak için görsel bileşenler yaratıyorsanız, ancak çalışma zamanında görünüşte kullanışlı görünüyorsa, genellikle çok iyi değil. Ancak bunun arasında basit bir yöntem kullanmak arasında hiçbir fark yoktur, ancak bir yöntem kullandığınızda, bir yöntemi daha uygun şekilde adlandırmanız daha olasıdır; bu nedenle, "alıcı" işlevinin kodu okurken daha belirgin olması gerekir.

Aşağıdakileri karşılaştırın:

int aValue = MyClass.Value;

ve

int aValue = MyClass.CalculateValue();

İkinci seçenek, değerin hesaplandığını açıkça belirtirken, ilk örnek, değerin kendisi hakkında hiçbir şey bilmeden, yalnızca bir değer döndürdüğünüzü söyler.

Belki aşağıdakilerin daha net olacağını iddia edebilirsiniz:

int aValue = MyClass.CalculatedValue;

Ancak sorun, değerin zaten başka bir yerde manipüle edildiğini varsayıyor olmanızdır. Bu nedenle, bir alıcı durumunda, bir değeri döndürürken başka bir şeyin devam edebileceğini varsayırken, bir mülk bağlamında böyle şeyleri açıklığa kavuşturmak zordur ve mülk adları asla fiil içermemelidir Aksi takdirde, bir bakışta kullanılan ismin erişildiğinde parantez ile süslenmesi gerekip gerekmediğini anlamak zorlaştırır.

Ancak ayarlayıcılar biraz farklıdır. Bir pasif sahibinin, bir mülke gönderilen verileri doğrulamak için bazı ek işlemler sağlaması, bir değerin ayarlanması mülkün tanımlanmış sınırlarını ihlal ederse bir istisna atması tamamen uygundur. Bununla birlikte, bazı geliştiricilerin ayarlayıcılara işleme eklemesiyle ilgili sorun, ayarlayıcıyı biraz daha fazla yapmak için, her zaman bir şekilde verinin hesaplanması ya da manipülasyonu yapmak gibi bir cazibenin olmasıdır. Burası, bazı durumlarda tahmin edilemeyen veya istenmeyen olabilecek yan etkileri bulabileceğiniz yerdir.

Belirleyiciler durumunda, her zaman verilere olabildiğince az yapmak için basit bir kural uygularım. Örneğin, genellikle sınır testlerine ve yuvarlama işlemlerine izin vereceğim, böylece gerektiğinde istisnaları ortaya çıkarabilirim veya makul bir şekilde önlenebilecekleri gereksiz istisnalardan kaçınabilirim. Kayan nokta özellikleri, bir istisna oluşturmaktan kaçınmak için aşırı ondalık basamakları yuvarlamak isteyebileceğiniz, buna karşın aralık değerlerinde birkaç ek ondalık basamakla girilmesine izin vermek isteyebileceğiniz iyi bir örnektir.

Setter girişinde bir çeşit manipülasyon uygularsanız, alıcı ile aynı problemi yaşarsınız, diğerlerini setterin ne yaptığını bilmesini sağlamak, sadece adlandırarak zorlaşır. Örneğin:

MyClass.Value = 12345;

Bu, ayarlayıcıya verildiğinde değere ne olacağı hakkında bir şey söylüyor mu?

Peki ya:

MyClass.RoundValueToNearestThousand(12345);

İkinci örnek, verilerinize tam olarak ne olacağını size söylerken, birincisi, değerinizin keyfi olarak değiştirilip değiştirilmeyeceğini size bildirmeyecektir. Kod okurken, ikinci örnek amaç ve işlev açısından çok daha net olacaktır.

Bunun, alıcıların ve belirleyicilerin ilk sırada yer almasının amacını tamamen ortadan kaldıracağı doğru muyum ve doğrulama ve diğer mantıklara (elbette garip yan etkiler olmadan) izin verilmeli mi?

Alıcı ve ayarlayıcılara sahip olmak, "saflık" uğruna kapsülleme değil, sınıfın çağrı koduyla uyumluluğunu bozacak sınıfın arayüzünde bir değişiklik riski olmadan kodun kolayca yeniden yapılandırılmasına izin vermek için kapsülleme ile ilgilidir. Doğrulama, bir ayarlayıcıda tamamen uygun olmakla birlikte, küçük bir risk vardır; arama kodu belirli bir şekilde gerçekleşen doğrulamaya dayanırsa, doğrulamadaki bir değişikliğin arama koduyla uyumluluğu bozması riski vardır. Bu, genellikle nadir görülen ve nispeten düşük riskli bir durumdur, ancak tamamlanma uğruna not edilmelidir.

Doğrulama ne zaman yapılmalı?

Doğrulama, değeri gerçekten ayarlamadan önce ayarlayıcı bağlamında yapılmalıdır. Bu, bir istisna atıldığında, nesnenizin durumunun değişmemesini ve potansiyel olarak verilerini geçersiz kılmamasını sağlar. Genel olarak, doğrulama kodunu ayarlayıcı kodunu nispeten derli toplu bir şekilde tutmak için ayarlayıcı içinde adı verilen ilk şey olacak ayrı bir yönteme devretmek daha iyi olur.

Bir ayarlayıcının değeri değiştirmesine izin verilir mi (belki de geçerli bir değeri bir kanonik içsel gösterime dönüştürebilirsiniz)?

Çok nadir durumlarda, belki. Genel olarak, muhtemelen değil daha iyidir. Bu, başka bir yönteme kalan en iyi şeydir.


re: değeri değiştirirseniz, ayarlayıcı geçersiz bir değerden geçerse -1 değerini ya da bazı NULL bayrak belirtecini ayarlamanız makul olabilir
Martin Beckett

1
Bununla ilgili birkaç sorun var. Bir değeri keyfi ayarlamak, kasıtlı ve net olmayan bir yan etki yaratır. Ayrıca, arama kodunun yasadışı verilerle daha iyi başa çıkmak için kullanılabilecek geri bildirim almasına izin vermez. Bu, UI seviyesindeki değerlerle özellikle önemlidir. Yine de adil olmak gerekirse, düşündüğüm bir istisna, tarihi standart bir tarih / saat biçiminde saklarken, girdi olarak birden fazla tarih biçimine izin verip vermeme olabilir. Bu belirli bir "yan etki" nin, girdi verilerinin yasal olması şartıyla validasyondaki verilerin normalleştirilmesi olduğunu ileri sürebilir.
S.Robins

Evet, belirleyicinin gerekçelerinden biri, eğer değer ayarlanabiliyorsa true / false döndürmesi gerektiğidir.
Martin Beckett

1
"Kayan nokta özellikleri, bir istisna oluşturmaktan kaçınmak için aşırı ondalık basamakları yuvarlamak isteyebileceğiniz iyi bir örnektir" Hangi durumlarda çok fazla ondalık basamağa sahip olmak bir istisna yaratır?
Mark Byers

2
Değeri, bir onaylama biçimi olarak kanonik bir gösterime değiştirirken görüyorum. Eksik değerleri (örneğin, bir zaman damgası yalnızca zaman içeriyorsa geçerli tarihi) varsayılan değere sahip olma veya bir değeri ölçeklendirme (.93275 ->% 93.28), iç tutarlılığı sağlamak için tamam olmalıdır, ancak bu tür manipülasyonlar açıkça API belgelerinde belirtilmelidir. , özellikle de API içinde nadir bir deyim ise.
TMN

20

Eğer alıcı / ayarlayıcı sadece değeri yansıtırsa, o zaman onlara sahip olmanın veya değeri özel yapmanın bir anlamı yoktur. İyi bir nedeniniz varsa, bazı üye değişkenleri halka açıklamakta yanlış bir şey yoktur. Eğer bir 3B puan sınıfı yazıyorsanız .x, .y, .z public komutunu kullanmak çok mantıklı.

Ralph Waldo Emerson'ın dediği gibi, "Aptalca bir tutarlılık, küçük devlet adamları ve filozofları ve Java tasarımcıları tarafından sevilen küçük zihinlerin hobgobinidir."

Alıcılar / ayarlayıcılar, yan etkilerin olabileceği yerlerde, diğer dahili değişkenleri güncellemeniz, önbellekteki değerleri yeniden hesaplamanız ve sınıfı geçersiz girdilerden korumanız gereken yerlerde kullanışlıdır.

Onlar için iç yapıyı gizlemelerinin normal gerekçesi genellikle en az işe yarar. Örneğin. Bu noktaları 3 değişken olarak saklı tutuyorum, uzak bir veritabanında dizeleri olarak depolamaya karar verebilirim, bu yüzden arayanın kodu üzerinde başka bir etkiye sahip olmadan da bunları gizlemek için alıcı / ayarlayıcılar yapacağım.


1
Vay, Java tasarımcıları ilahi? </jk>

@delnan - açıkçası değil - ya da daha iyi kafiyerlerdi ;-)
Martin Beckett

X, y, z'nin tüm Float.NAN olduğu bir 3d noktası oluşturmak geçerli olur mu?
Andrew T Finnell

4
"Her zamanki gerekçelendirme" nin (iç yapıyı gizleme) alıcıların ve belirleyicilerin en az faydalı özelliği olduğu fikrine katılmıyorum. C # 'da, özelliği, altta yatan yapısı değiştirilebilecek bir arabirim yapmak kesinlikle faydalıdır - örneğin, yalnızca bir numaralandırma için kullanılabilir bir özellik istiyorsanız, bunu söylemek IEnumerable<T>gibi bir şeyi zorlamaktan daha iyidir List<T>. Söyleyeceğim veritabanı erişimi örneğiniz, tek bir sorumluluğu ihlal ediyor - modelin temsilinin nasıl devam edeceği ile karıştırılıyor.
Brandon Linton

@AndrewFinnell - imkansız / geçerli değil / silinmemiş gibi işaretleme noktalarının iyi bir yolu olabilir
Martin Beckett

8

Meyer'in Üniformalı Erişim Prensibi: "Bir modül tarafından sunulan tüm servisler, depolama veya hesaplama yoluyla uygulanıp uygulanmadıklarına ihanet etmeyen, tek tip bir gösterim yoluyla sağlanmalıdır." Özellikler: alıcıların / ayarlayıcıların arkasındaki ana sebep, aka Özellikler.

Bir sınıfın bir alanını önbelleğe almaya veya tembel olarak hesaplamaya karar verirseniz, somut verileri değil, yalnızca maruz kalan özellik erişimcileriniz varsa, bunu istediğiniz zaman değiştirebilirsiniz.

Değerli nesneler, basit yapılar bu soyutlamaya ihtiyaç duymazlar ama bence tam teşekküllü bir sınıf.


2

Eyfel dili ile tanıtılan sınıfların tasarlanması için ortak bir strateji Komut-Sorgu Ayırma'dır . Fikir bir yöntemdir gerektiğidir ya size nesne hakkında bir şey söylemek ya da bir şey yapmak için nesneyi söyler, ancak her iki yapmamaya.

Bu, içsel temsil değil, sadece sınıfın ortak arayüzü ile ilgilidir. Veritabanındaki bir satırla desteklenen bir veri modeli nesnesini göz önünde bulundurun. Veriyi yüklemeden nesneyi yaratabilir, sonra bir alıcıyı ilk kez çağırdığınızda gerçekte yaptığı gibi olur SELECT. Sorun değil, nesnenin nasıl temsil edildiğiyle ilgili bazı içsel ayrıntıları değiştiriyor olabilirsiniz, ancak nesnenin istemcileri için görünüşünü değiştirmiyorsunuzdur. Alıcıları birden çok kez arayabilmeli ve bu sonuçları iade etmek için farklı çalışmalar yapsalar bile yine de aynı sonuçları elde edebilmelisiniz.

Benzer şekilde, bir pasif, sözleşmeye bağlı olarak, yalnızca nesnenin durumunu değiştirdiği gibi görünüyor. Bunu bir kıvrımlı şekilde yapabilir UPDATE- bir veritabanına yazma , ya da parametreyi bir iç nesneye geçirme. Sorun değil, ancak devletin kurulmasıyla ilgili olmayan bir şey yapmak şaşırtıcı olacaktır.

Meyer (Eyfel'in yaratıcısı) da doğrulama hakkında söylenecek şeyler yaptı. Temel olarak, ne zaman bir nesne sakinse, geçerli bir durumda olmalıdır. Dolayısıyla, kurucu tamamlandıktan hemen sonra, her harici yöntem çağrısından önce ve sonra (ancak zorunlu olarak değil), nesnenin durumu tutarlı olmalıdır.

Bu dilde, bir prosedür çağırmak ve açık bir özniteliği okumak için sözdiziminin her ikisinin de aynı şekilde göründüğünü not etmek ilginçtir. Başka bir deyişle, arayan bir kişi bir yöntem kullanıp kullanmadıklarını veya doğrudan bir örnek değişkeniyle çalışıp çalışmadıklarını söyleyemez. Sadece sorunun ortaya çıktığı yerde bu uygulama detayını gizlemeyen dillerdedir - eğer arayanlar söyleyemezse, bu değişikliği müşteri koduna sızdırmadan halka açık bir ivar ile erişimciler arasında geçiş yapabilirsiniz.


1

Yapılacak bir başka kabul edilebilir şey de klonlamadır. Bazı durumlarda, birinin sınıfınızı vermesinden sonra örneğin: bir şeyin listesini sınıfında değiştiremez (ya da listedeki nesneyi değiştiremez). Bu nedenle, ayarlayıcıda parametrenin derin kopyasını alırsınız ve alıcıda derin kopyayı döndürürsünüz. (Değişken türleri parametre olarak kullanmak çok seçenek değildir, ancak yukarıda bunun mümkün olmadığı varsayılmaktadır) Ancak gerekmediğinde erişimcilerde klonlama yapmayın. Tedarikçiler / alıcılar ve belirleyiciler hakkında sürekli maliyet işlemleri olarak düşünmesi (ancak uygun şekilde olmaması) kolaydır, bu nedenle api kullanıcısı için bekleyen performans mayınlarıdır.


1

Özellik erişimciler ve verileri depolayan ivarlar arasında her zaman 1-1 eşlemesi yoktur.

Örneğin, bir görünüm sınıfı center, görüntünün merkezini depolayan ivar olmasa da bir özellik sağlayabilir ; ayarı centergibi diğer Ivars için nedenler değişiklikleri originveya transformya da her türlü fakat sınıfın müşterileri tanımak veya umurumda değil nasıl center doğru bir şekilde çalıştığından şartıyla saklanır. Gerekenler değil gerçekleşmesi, ancak, bu ayar centerşeyler en yapmış olduğu ancak, yeni değeri kaydetmek için ne gerekiyorsa ötesinde gerçekleşmesi neden olur.


0

Ayarlayıcılar ve alıcılarla ilgili en iyi bölüm, API'yi değiştirmeden API kurallarını değiştirmeyi kolaylaştırmalarıdır. Bir hatayı tespit ederseniz, hatayı kütüphanede düzeltebilmeniz ve her tüketicinin kod tabanını güncellememesi daha olasıdır.


-4

Setçilerin ve alıcıların kötü olduğuna ve yalnızca bir çerçeve / konteyner tarafından yönetilen sınıflarda kullanılması gerektiğine inanma eğilimindeyim. Uygun bir sınıf tasarım alıcılar ve ayarlayıcılar vermemelidir.

Düzenleme: Bu konuda iyi yazılmış bir makale .

Düzen2: kamu alanları bir OOP yaklaşımında saçmalıktır; Alıcıların ve belirleyicilerin kötü olduğunu söyleyerek onların ortak alanlarla değiştirilmeleri gerektiğini kastetmiyorum.


1
Sanırım alıcıların / ayarlayıcıların dikkatli bir şekilde kullanılmasını ve gerekli olmadıkça sınıf verilerinin ortaya çıkmasını önlemeyi öneren makalenin noktasını kaçırdığınızı düşünüyorum. Bu IMHO, geliştirici tarafından kasıtlı olarak uygulanması gereken mantıklı bir tasarım prensibidir. Bir sınıfın özelliklerini yaratma açısından, olabilir , potansiyel olarak sadece bir değişken açığa ama bu esasında encapsulation ihlal zor değişken ayarlandığında doğrulama sağlamak, ya da "var" zaman alternatif bir kaynaktan gelen değer çizmek yapar ve Arayüz tasarımını korumak için sizi zorlaştırıyor.
S.Robins

@ S.Robins Bence kamu alıcıları ve belirleyicileri yanlıştır; ortak alanlar daha yanlış (eğer karşılaştırılabilirse).
m3th0dman

@ methodman Evet, ortak bir alanın yanlış olduğunu kabul ediyorum, ancak ortak mülk faydalı olabilir. Bu özellik, zamanın özel gereksinimine bağlı olarak doğrulama veya verilerin ayarlanması veya iade edilmesi ile ilgili olaylar için bir yer sağlamak için kullanılabilir. Alıcı ve ayarlayıcıların kendileri yanlış değildir. Öte yandan bunların nasıl ve ne zaman kötüye kullanıldıklarına, koşullara bağlı olarak tasarım ve bakım açısından zayıf olarak görülebilir. :)
S.Robins

@ S.Robins OOP ve modellemenin temelleri hakkında düşünün; nesnelerin gerçek hayattaki varlıklarını kopyalaması gerekiyordu. Alıcı / belirleyici gibi bu tür işlem / özelliklere sahip gerçek yaşamdaki varlıklar var mı?
m3th0dman

Burada çok fazla yorum yapmaktan kaçınmak için, bu sohbeti bu sohbete ekleyeceğim ve buradaki yorumunuzu ele alacağım .
S.Robins
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.