Biri “Neyin değişeceğini kapsüllemek” derken ne anlama geliyor?


25

Karşılaştığım OOP ilkelerinden biri: -Neyin değişebileceğini benimsemek.

Kelimenin tam anlamıyla ne anlama geldiğini anlıyorum, yani neyin değişeceğini gizle. Ancak, daha iyi bir tasarıma nasıl katkıda bulunacağını bilmiyorum. Biri bunu iyi bir örnek kullanarak açıklayabilir mi?


İyi açıklayan en.wikipedia.org/wiki/Encapsulation_(computer_programming) bakınız . Bence "değişken olan" doğru değil çünkü bazen sabitleri de içine almalısınız.
qwerty_so

I don't know how exactly would it contribute to a better designKapsülleyici detaylar, "model" ve uygulama detayları arasında gevşek birleşme ile ilgilidir. Ne kadar az bağlanırsa uygulama ayrıntılarına "model" denir, çözüm de o kadar esnek olur. Ve onu geliştirmeyi kolaylaştırır. "Kendini ayrıntılardan soyutla".
LAIV

@Laiv Yani "değişken", yazılım ömrünüz boyunca ne geliştiğini veya programınızın veya her ikisinin yürütülmesi sırasında neyin değiştiğini belirtir.
Haris Ghauri,

2
@HarisGhauri her ikisi de. Birbirini neyin değiştirdiğini birlikte gruplayın. Bağımsız olarak değişenleri izole edin. Asla değişmeyeceğini düşündüğünüzden şüphelenin.
candied_orange

1
@ laiv düşünme "soyut" iyi bir nokta. Bunu yapmak çok zor olabilir. Herhangi bir nesnede tek bir sorumluluğunuz var. Bununla ilgili güzel bir şey, burada dikkatlice düşünmek zorundasın. Sorunun geri kalanının detayları bir başkası olduğunda, işleri kolaylaştırır.
candied_orange

Yanıtlar:


30

Bu gibi görünen bir kod yazabilirsiniz:

if (pet.type() == dog) {
  pet.bark();
} else if (pet.type() == cat) {
  pet.meow();
} else if (pet.type() == duck) {
  pet.quack()
}

veya şuna benzeyen bir kod yazabilirsiniz:

pet.speak();

Değişen şey kapsüllenmişse, endişelenmenize gerek yoktur. Sadece neye ihtiyaç duyduğunuzu ve neyi kullanıyorsanız onu gerçekten neye ihtiyaç duyduğunuzu neye göre değiştireceğinizi düşünün.

Neyin değişebileceğini enkapsüle edin ve neyin değiştiğine önem veren kodun etrafına yaymanız gerekmez. Sadece evcil hayvanı, bu türden nasıl konuşacağını bilen belirli bir tür olarak ayarladınız ve ondan sonra hangi türü unutabilir ve evcil hayvan gibi davranabilirsiniz. Hangi tipte olduğunu sormana gerek yok.

Türün kapsüllenmiş olduğunu düşünebilirsiniz, çünkü erişmek için bir alıcı gerekir. Yapmıyorum. Getter gerçekten kapsüllemedi. Birisi enkapsülasyonunuzu kırdığında sadece tava atıyorlar. En çok hata ayıklama kodu olarak kullanılan en boy yönelimli kanca gibi güzel bir dekoratör. Nasıl dilimlediğiniz önemli değil, hala türünü ortaya çıkarıyorsunuz.

Bu örneğe bakabilir ve polimorfizm ile enkapsülasyonu karıştırdığımı düşünebilirsiniz. Değilim. "Neyin değiştiği" ve "detayların" birbirine karışması.

Evcil hayvanınızın bir köpek olduğu gerçeği bir ayrıntıdır. Bu sizin için farklı olabilir. Olmayabilir biri. Ama kesinlikle bir kişiden kişiye değişebilen biri. Bu yazılımın yalnızca köpek sevenler tarafından kullanılacağına inanmıyorsak, köpeği bir ayrıntı olarak ele almak ve onu kapsüllemek akıllıcadır. Bu yolla sistemin bazı kısımları köpeğin habersiz bir şekilde farkında değildir ve “papağanlar biziz” ile birleşince etkilenmeyeceklerdir.

Kodun geri kalanından ayrıştırın, ayırın ve ayrıntıları gizleyin. Ayrıntıların bilginin sisteminize yayılmasına izin vermeyin ve “neyin değişeceğini kapsülleyin” ifadesini takip edin.


3
Bu gerçekten garip. "Neyin değişebileceğini kapsama" bana göre durum değişimlerini gizlemek anlamına geliyor, örneğin hiçbir zaman küresel değişkenlere sahip değilsiniz. Fakat siz de cevabı çok anlamlı, hatta kapsüllemeden çok polimorfizme bir cevap olsa bile, :)
David Arno

2
@DavidArno Polimorfizm bu işi yapmanın bir yoludur. Yapının evcil hayvana yapılıp yapılmadığına bakabilirdim ve evcil hayvanın kapsüllenmesiyle burada işler güzel görünürdü. Ama bu sadece temizlemek yerine karışıklığı hareket ettiriyor olacaktı.
candied_orange

1
"Değişen şeyi kapsama" bana göre devlet değişikliklerini gizlemek anlamına geliyor . Hayır, hayır CO'nun yorumunu beğendim. Derick Elkin'in cevabı daha da derinleşir, birden fazla okur. @JacquesB'in dediği gibi "Bu ilke aslında oldukça derin"
radarbob

16

"Burada değişir", burada "değişen gereksinimler nedeniyle zaman içinde değişebilir" anlamına gelir. Bu temel bir tasarım ilkesidir: Gelecekte ayrı olarak değiştirilmesi gerekebilecek kod veya veri parçalarını ayırmak ve izole etmek. Tek bir gereksinim değişirse, ideal olarak sadece ilgili kodu tek bir yerde değiştirmemizi gerektirir. Ancak, kod tabanı kötü bir şekilde tasarlanırsa, yani birçok yerde yayılmış olan gereksinim için yüksek düzeyde birbirine bağlı ve mantık varsa, değişiklik zor olacaktır ve beklenmeyen etkilere neden olma riski yüksek olacaktır.

Diyelim ki birçok yerde satış vergisi hesaplaması kullanan bir uygulamanız var. Satış vergisi oranı değişirse ne tercih edersiniz:

  • Satış vergisi, satış vergisinin hesaplandığı uygulamada her yerde kodlanmış bir bilgi kaynağıdır.

  • satış vergisi oranı, satış vergisinin hesaplandığı uygulamada her yerde kullanılan küresel bir sabittir.

  • calculateSalesTax(product)Satış vergisi oranının kullanıldığı tek yer olan tek bir yöntem vardır .

  • satış vergisi oranı bir yapılandırma dosyasında veya veritabanı alanında belirtilir.

Satış vergisi oranı, diğer gereksinimlerden bağımsız bir siyasi karar nedeniyle değişebileceğinden, bir konfigürasyonda izole edilmesini tercih ediyoruz, böylece herhangi bir kodu etkilemeden değiştirilebilir. Ancak, satış vergisinin hesaplanmasındaki mantığın, örneğin farklı ürün için farklı oranların değişebileceği de düşünülebilir, bu nedenle hesaplama mantığını kapsüllendirmek isteriz. Küresel sabit, iyi bir fikir gibi görünebilir, ancak aslında satışlar vergisini programda tek bir yerde kullanmak yerine farklı yerlerde kullanmayı teşvik edebileceğinden, aslında kötüdür.

Şimdi başka bir sabiti düşünün, Pi, aynı zamanda koddaki birçok yerde de kullanılır. Aynı tasarım ilkesi geçerli midir? Hayır, çünkü Pi değişmeyecek. Bir yapılandırma dosyasına veya veritabanı alanına çıkarmak, sadece gereksiz karmaşıklığa neden olur (ve her şey eşit olmak için en basit kodu tercih ederiz). Tutarsızlıkları önlemek ve okunabilirliği artırmak için birden fazla yerde sabit kodlamak yerine, onu global bir sabit haline getirmek mantıklı geliyor.

Mesele şu ki, sadece programın şu an nasıl çalıştığına bakarsak , satış vergisi oranı ve Pi eşdeğerdir, ikisi de sabittir. Sadece gelecekte neyin değişebileceğini düşündüğümüz zaman , tasarımda onlara farklı davranmamız gerektiğini anlıyoruz.

Bu ilke aslında oldukça derin, çünkü bugün kod üssünün bugün yapması gerekenlerin ötesine bakmanız ve aynı zamanda gereklilikleri değiştirebilecek ve hatta farklı paydaşları anlayabilen dış güçleri göz önünde bulundurmanız gerektiği anlamına geliyor .


2
Vergiler güzel bir örnek. Kanunlar ve Vergiler hesaplamaları bir günden diğerine değişebilir. Bir vergi beyan sistemi uyguluyorsanız, bu tür değişikliklere kesinlikle bağlı olursunuz. Ayrıca bir Yerel ayardan diğerine (ülkeler, iller, ...) değişir
Laiv

"Pi değişmeyecek" beni güldürdü. Doğru, Pi'nin değişmesi pek mümkün değil, ama artık kullanmana izin verilmediğini varsayalım. Bazı insanlar kendi yöntemleriyle Pi'yi kullanmayacaklarsa. Diyelim ki bu bir gereklilik mi? Umarım Tau günün kutlu olsun . Güzel cevap btw. Gerçekten derin.
candied_orange

14

Mevcut cevapların her ikisi de sadece kısmen işarete çarpıyor gibi görünüyor ve temel fikri bulanıklaştıran örneklere odaklanıyorlar. Bu aynı zamanda (yalnızca) bir OOP ilkesi değil, genel olarak bir yazılım tasarım ilkesidir.

Bu cümle içinde "değişken" olan şey koddur. Christophe bir şey genellikle olduğunu söyleyerek noktada olduğunu olabilir sık sık olduğu değişir tahmin bu. Amaç, koddaki gelecekteki değişikliklerden kendinizi korumaktır. Bu, bir arayüze karşı programlama ile yakından ilgilidir . Ancak, Christophe bunu "uygulama detayları" ile sınırlandırmak konusunda yanlış. Aslında, bu tavsiyenin değeri genellikle gereksinimlerdeki değişikliklerden kaynaklanmaktadır .

Bu sadece dolaylı olarak kapsülleyici devletle ilgilidir, ki David Arno'nun düşündüğü şey bu. Bu tavsiye her zaman (ancak genellikle) kapsülleme durumunu önermez ve bu tavsiye de değişmez nesneler için de geçerlidir. Aslında, sadece sabitleri isimlendirmek, değişeni enkapsüle etmenin (çok temel) bir şeklidir.

CandiedOrange, "neyin değiştiğini" "ayrıntılarıyla" açıkça birleştirir. Bu sadece kısmen doğrudur. Değişen herhangi bir kodun bir anlamda "detaylar" olduğunu kabul ediyorum, fakat "detay" değişmeyebilir (bu tautolojik yapmak için "detaylar" tanımlanmadıkça). Değişmeyen detayların kapsüllenmesi için sebepler olabilir, ancak bu karar bir değildir. Kabaca söylemek gerekirse, "köpek", "kedi" ve "ördek" ile uğraşmanız gereken tek tip olacağından çok eminseniz, bu belirti CandiedOrange'in yeniden yapılanmasını önermez.

CandiedOrange'in örneğini farklı bir bağlamda yayınlamak, C gibi bir prosedür diline sahip olduğumuzu varsayalım:

if (pet.type() == dog) {
  pet.bark();
} else if (pet.type() == cat) {
  pet.meow();
} else if (pet.type() == duck) {
  pet.quack()
}

Bu kod parçasının gelecekte değişeceğini makul bir şekilde bekleyebilirim. Yeni bir prosedür tanımlayarak onu "kapsülleyebilirim":

void speak(pet) {
  if (pet.type() == dog) {
    pet.bark();
  } else if (pet.type() == cat) {
    pet.meow();
  } else if (pet.type() == duck) {
    pet.quack()
  }
}

ve kod bloğu yerine bu yeni prosedürün kullanılması (yani bir "özütleme yöntemi" yeniden düzenleme). Bu noktada bir "inek" tipi veya sadece speakprosedürün güncellenmesini gerektiren herhangi bir şey eklemek . Tabii ki, bir OO dilinde bunun yerine CandiedOrange'in cevabının gerektirdiği şekilde dinamik gönderimden yararlanabilirsiniz. petBir arayüz üzerinden erişirseniz , bu doğal olarak gerçekleşecektir . Koşullu mantığın dinamik gönderim yoluyla ortadan kaldırılması, bu prosedürsel yorumlamayı neden yaptığımın bir parçası olan ortogonal bir kaygıdır. Ayrıca bunun OOP'a özgü özellikler gerektirmediğini vurgulamak istiyorum. Bir OO dilinde bile, neyin değişebileceğini kapsama almak mutlaka yeni bir sınıf veya arayüzün oluşturulması gerektiği anlamına gelmez.

Daha iyi bir örnek olarak (OO'ya daha yakın fakat tamamen OO değil), kopyaları bir listeden silmek istediğimizi söylüyoruz. Diyelim ki, başka bir listede gördüğüm öğeleri takip ederek ve gördüğümüz tüm öğeleri kaldırarak listeyi değiştirerek uygularız. En azından performans nedenleriyle, görülen öğeleri takip etme biçimimizi değiştirmek istediğimizi değiştirmek isteyebileceğimizi varsaymak mantıklıdır. Neyin değişebileceğini kapsülleme diktumu, görülen öğeler kümesini temsil etmek için soyut bir veri türü oluşturmamız gerektiğini öne sürmektedir. Algoritmamız şimdi bu soyut Set veri türüne göre tanımlanmıştır ve bir ikili arama ağacına geçmeye karar verirsek, algoritmamızın değişmesi veya bakımı gerekmez. Bir OO dilinde, bu soyut veri tipini yakalamak için bir sınıf veya arayüz kullanabiliriz. SML / O 'gibi bir dilde

Gereksinim odaklı bir örnek için, bazı iş mantıklarıyla ilgili bazı alanları doğrulamanız gerektiğini söyleyin. Şu anda özel gereksinimleriniz olsa da, onların değişeceğinden şüphelisiniz. Mevcut mantığı kendi yordamını / işlevini / kuralını / sınıfını içine alabilirsiniz.

Bu, "değişken olanı kapsüllemek" in bir parçası olmayan ortogonal bir kaygı olmasına rağmen, şimdi kapsüllenmiş mantığı ile parametreleştiren soyutlamak doğaldır. Bu tipik olarak daha esnek bir koda yol açar ve kapsüllenmiş mantığı değiştirmek yerine alternatif bir uygulamada yer değiştirerek mantığın değiştirilmesine izin verir.


Acı tatlı ironi. Evet, bu yalnızca bir OOP konusu değil. Bir dil paradigması detayının cevabımı kirletmesine izin verdin ve paradigmayı "değiştirerek" haklı olarak cezalandırdın.
candied_orange

"Bir OO dilinde bile, değişkenleri kapsayan, zorunlu olarak yeni bir sınıf veya arayüz oluşturulması gerekmediği anlamına gelmez" - yeni bir sınıf veya arayüz oluşturmanın
SRP'yi

11

"Neyin değişebileceğini kapsama" ifadesi, değişen ve gelişen uygulama detaylarının gizlenmesini ifade eder.

Örnek:

Örneğin, sınıfın register () Courseöğesinin kaydını tuttuğunu varsayalım Students. Üzerinde a uygulayabilir LinkedListve üzerinde yinelemeye izin vermek için kabı açığa çıkarabilirsiniz:

class Course { 
    public LinkedList<Student> Atendees; 
    public bool register (Student s);  
    ...
}

Ancak bu iyi bir fikir değil:

  • Birincisi, insanlar iyi davranıştan yoksun kalabilir ve onu register-) yöntemini kullanmadan doğrudan listeye ekleyen, self servis olarak kullanabilirler.
  • Ancak daha da can sıkıcı: bu, "kullanılan kodun" kullanılmış sınıfın iç uygulama ayrıntılarına bağımlılığı yaratır. Bu, örneğin bir dizi, vektör, koltuk numarası olan bir harita veya kendi kalıcı veri yapınızı kullanmayı tercih ederseniz, sınıfın gelecekteki evrimlerini engelleyebilir.

Neyin değişebileceğini (veya daha doğrusu söylenen, neyin değişebileceğini söyleyen) kapsüllenirseniz, hem kullanım kodunun hem de kapsüllenmiş sınıfın birbirlerini kendi başlarına geliştirmeleri için özgürlüğünüzü korursunuz. Bu nedenle OOP'de önemli bir ilkedir.

Ek okuma:

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.