"Sızdıran Soyutlama" terimi ne anlama geliyor? (Lütfen örneklerle açıklayın. Sadece bir teoriyi incelerken sık sık zorlanırım.)
"Sızdıran Soyutlama" terimi ne anlama geliyor? (Lütfen örneklerle açıklayın. Sadece bir teoriyi incelerken sık sık zorlanırım.)
Yanıtlar:
İşte bir et alanı örneği:
Otomobillerin sürücüler için soyutlamaları vardır. En saf haliyle, bir direksiyon simidi, gaz pedalı ve fren var. Bu soyutlama, kaputun altında ne olduğu hakkında birçok ayrıntıyı gizler: motor, kamlar, triger kayışı, bujiler, radyatör vb.
Bu soyutlamanın güzel yanı, kullanıcıyı yeniden eğitmeden uygulamanın parçalarını iyileştirilmiş parçalarla değiştirebilmemizdir. Diyelim ki distribütör kapağını elektronik ateşleme ile değiştiriyoruz ve sabit kamı değişken kam ile değiştiriyoruz. Bu değişiklikler performansı iyileştirir, ancak kullanıcı yine de direksiyonla çalışır ve başlatmak ve durdurmak için pedalları kullanır.
Aslında oldukça dikkat çekici ... 16 yaşında ya da 80 yaşında biri bu karmaşık makineyi, içinde nasıl çalıştığını gerçekten bilmeden çalıştırabilir!
Ancak sızıntılar var. İletim küçük bir sızıntı. Otomatik şanzımanda, vites değiştirirken otomobilin bir an için güç kaybettiğini hissedebilirsiniz, oysa CVT'de tamamen yumuşak tork hissedersiniz.
Daha büyük sızıntılar da var. Motoru çok hızlı devirirseniz, ona zarar verebilirsiniz. Motor bloğu çok soğuksa, araç çalışmayabilir veya performansı düşük olabilir. Radyoyu, farları ve AC'yi aynı anda çalıştırırsanız, gaz kilometrenizin azaldığını göreceksiniz.
Basitçe, soyutlamanızın bazı uygulama ayrıntılarını ortaya koyduğu veya soyutlamayı kullanırken uygulama ayrıntılarının farkında olmanız gerektiği anlamına gelir. Bu terim 2002 dolaylarında Joel Spolsky'ye atfedilmiştir . Daha fazla bilgi için wikipedia makalesine bakın .
Klasik bir örnek, uzak dosyaları yerel olarak değerlendirmenize izin veren ağ kitaplıklarıdır. Bu soyutlamayı kullanan geliştirici, ağ sorunlarının bunun yerel dosyaların yapmadığı şekillerde başarısız olmasına neden olabileceğinin farkında olmalıdır. Daha sonra, ağ kitaplığının sağladığı soyutlamanın dışındaki belirli hataları işlemek için kod geliştirmeniz gerekir.
Vikipedi bir sahiptir oldukça iyi tanımını bunun için
Sızdıran bir soyutlama, karmaşıklığı azaltmayı (veya gizlemeyi) amaçlayan, temeldeki ayrıntıların tamamen gizlenmediği, uygulanan herhangi bir soyutlamayı ifade eder.
Veya başka bir deyişle, yazılım için, programdaki sınırlamalar veya yan etkiler yoluyla bir özelliğin uygulama ayrıntılarını gözlemleyebileceğiniz zamandır.
Hızlı bir örnek, C # / VB.Net kapanışları ve ref / out parametrelerini yakalayamamalarıdır. Yakalanamamalarının nedeni, kaldırma işleminin nasıl gerçekleştiğine dair bir uygulama detayından kaynaklanmaktadır. Bu, bunu yapmanın daha iyi bir yolu olduğu anlamına gelmez.
İşte .NET geliştiricilerine tanıdık bir örnek: ASP.NET'in Page
sınıfı, HTTP işlemlerinin ayrıntılarını, özellikle de form verilerinin yönetimini gizlemeye çalışır, böylece geliştiricilerin gönderilen değerlerle uğraşmasına gerek kalmaz (çünkü form değerlerini sunucuya otomatik olarak eşler) kontroller).
Ancak en temel kullanım senaryolarının ötesine geçerseniz, Page
soyutlama sızmaya başlar ve sınıfın uygulama ayrıntılarını anlamadığınız sürece sayfalarla çalışmak zorlaşır.
Yaygın bir örnek, bir sayfaya dinamik olarak kontroller eklemektir - dinamik olarak eklenen kontrollerin değeri, bunları tam olarak doğru zamanda eklemediğiniz sürece sizin için eşlenmeyecektir: alttaki motor, gelen form değerlerini uygun kontrollerle eşlemeden önce. Bunu öğrenmen gerektiğinde, soyutlama sızdı .
Bir bakıma, önemsiz olmasa da tamamen teorik bir şey.
Bir şeyleri daha kolay kavrayabilmek için soyutlamaları kullanırız. Tek tek öğeler olan sıralı bir karakter kümesiyle uğraştığım gerçeğini gizlemek için bir dilde bir dizgi sınıfı üzerinde çalışabilirim. Sayılarla uğraştığım gerçeğini gizlemek için sıralı bir dizi karakterle uğraşıyorum. 1'ler ve 0'larla uğraştığım gerçeğini gizlemek için sayılarla uğraşıyorum.
Sızdıran bir soyutlama, gizlemesi gereken ayrıntıları gizlemeyen bir soyutlamadır. Java veya .NET'te 5 karakterlik bir dizede string.Length çağrılırsa, bu dillerin karakterleri olarak adlandırdığı uygulama ayrıntıları nedeniyle, bu dillerin 1'i veya 0,5 karakter. Soyutlama sızdırıldı. Yine de sızdırmamak, uzunluğu bulmanın daha fazla depolama alanı gerektireceği (gerçek uzunluğu saklamak için) veya O (1) 'den O (n)' ye (gerçek uzunluğun ne olduğunu bulmak için) değişeceği anlamına gelir. Eğer gerçek yanıtı önemsiyorsam (çoğu zaman gerçekten anlamazsınız) gerçekte neler olup bittiğine dair bilgi üzerinde çalışmanız gerekir.
Bir yöntemin veya özelliğin iç işleyişe girmenize izin verdiği durumlarda, ister soyutlama sızıntıları olsun, isterse daha düşük bir soyutlama düzeyine geçmenin iyi tanımlanmış yolları olsun, bazen insanların üzerinde anlaşamadığı bir konu olabilir.
RPC kullanarak örnek vermeye devam edeceğim.
İdeal RPC dünyasında, uzak prosedür çağrısı yerel bir prosedür çağrısı gibi görünmelidir (ya da hikaye böyle devam eder). Programcıya tamamen şeffaf olmalıdır, öyle ki aradıklarında yerel olarak depolanıp çalıştırılmadığı (veya sadece bu konu için) veya uzaktan saklanıp çalıştırılmadığı hakkında SomeObject.someFunction()
hiçbir fikirleri olmamalıdır . Teori, bunun programlamayı daha basit hale getirdiği yönünde.SomeObject
someFunction
Gerçek farklıdır çünkü yerel bir işlev çağrısı yapmak (dünyanın en yavaş yorumlanan dilini kullanıyor olsanız bile) ile şunlar arasında BÜYÜK bir fark vardır:
Yalnızca zamanda, büyüklük farkının yaklaşık üç mertebesi (veya daha fazlası!). Bu üç + büyüklük sıralaması, performansta büyük bir fark yaratacak ve bu da, bir RPC'yi ilk kez yanlışlıkla gerçek bir işlev çağrısı olarak ele aldığınızda oldukça açık bir şekilde bir prosedür çağrısı sızıntısı soyutlamanıza neden olacak. Ayrıca, kodunuzdaki ciddi sorunları engelleyen gerçek bir işlev çağrısı, uygulama hataları dışında çok az hata noktasına sahip olacaktır. Bir RPC çağrısı, normal bir yerel aramadan beklediğinizden çok, başarısızlık vakaları olarak değerlendirilecek aşağıdaki olası sorunların tümüne sahiptir:
Şimdi, "tıpkı bir yerel işlev çağrısı gibi" olan RPC çağrınız, yerel işlev çağrıları yaparken uğraşmak zorunda kalmayacağınız bir sürü fazladan başarısızlık durumuna sahiptir. Soyutlama daha da zorlaştı.
Sonuçta RPC kötü bir soyutlamadır çünkü her seviyede bir elek gibi sızdırır - başarılı olduğunda ve her ikisinde de başarısız olduğunda.
İçinde bir örnek django birçok çoğa örnek ORM :
Örnek API Kullanımında, çoktan çoğa özniteliğine Yayın nesneleri eklemeden önce temel Makale nesnesini a1 .save () kaydetmeniz gerektiğine dikkat edin. Ve çoktan çoğa özniteliğini güncellemenin temel veritabanına hemen kaydettiğine, tekil bir özniteliğin güncellenmesinin .save () çağrılana kadar veritabanına yansıtılmadığına dikkat edin.
Soyutlama, tek değerli niteliklerin ve çok değerli niteliklerin sadece nitelikler olduğu bir nesne grafiğiyle çalışmamızdır. Ancak RDBS'nin bütünlük sistemi bir nesne arayüzünün ince kaplamasıyla göründüğünden, ilişkisel veritabanı destekli veri deposu olarak uygulama sızıntı yapıyor.
Soyutlama, dünyayı basitleştirmenin bir yoludur. Bu, kaputun altında veya perdenin arkasında gerçekte ne olduğu konusunda endişelenmenize gerek olmadığı anlamına gelir. Bir şeyin aptal kanıtı olduğu anlamına gelir.
Uçaklar çok karmaşık makinelerdir. Jet motorlarınız, oksijen sistemleriniz, elektrik sistemleriniz, iniş takımı sistemleriniz vb. Var ama pilotun jet motorunun karmaşıklığı hakkında endişelenmesine gerek yok ... hepsi "soyutlanmış". Bu, pilotun yalnızca uçağı yönlendirmek için endişelenmesi gerektiği anlamına gelir: sola gitmek için sola ve sağa gitmek için, yükseklik kazanmak için yukarı çekin ve alçalmak için aşağı itin.
Yeterince basit ...... aslında yalan söyledim: direksiyon simidini kontrol etmek biraz daha karmaşık. İdeal bir dünyada, pilotun endişelenmesi gereken tek şey budur . Ancak gerçek hayatta durum böyle değildir: Bir uçağın nasıl çalıştığını veya herhangi bir uygulama detayını bilmeden bir maymun gibi bir uçağı uçurursanız, o zaman büyük olasılıkla çarpışır ve uçaktaki herkesi öldürürsünüz.
Gerçekte, bir pilotun birçok önemli şey için endişelenmesi gerekir - her şey soyutlanmamıştır: pilotlar rüzgar hızı, itme kuvveti, saldırı açıları, yakıt, irtifa, hava sorunları, alçalma açıları ve pilot doğru yöne gidiyor. Bilgisayarlar pilota bu görevlerde yardımcı olabilir, ancak her şey otomatik / basitleştirilmiş değildir.
Örneğin, pilot kolonda çok sert kalkarsa - uçak itaat eder, ancak pilot uçağı stall etme riskiyle karşı karşıya kalır ve bir kez durduğunda, yere düşmeden önce kontrolünü geri kazanmak çok zordur. .
Başka bir deyişle, pilotun başka bir şey bilmeden sadece direksiyonu kontrol etmesi yeterli değildir ......... nooooo ....... uçağın altında yatan riskleri ve sınırlamaları bilmelidir. uçmadan önce ....... uçağın nasıl çalıştığını ve uçağın nasıl uçtuğunu bilmelidir; uygulama ayrıntılarını bilmeli ..... çok sert bir şekilde yukarı çekmenin bir stall'a yol açacağını veya çok dik inişin uçağı yok edeceğini bilmelidir.
Bu şeyler soyutlanmış değil. Pek çok şey soyutlandı, ama her şey değil. Pilotun yalnızca direksiyon kolonu ve belki bir veya iki başka şey için endişelenmesi gerekiyor. Soyutlama "sızdırıyor".
...... kodunuzda aynı şey var. Altta yatan uygulama ayrıntılarını bilmiyorsanız, o zaman çoğu zaman, kendinizi köşeye sıkıştıracaksınız.
İşte kodlamada bir örnek:
ORM'ler, veritabanı sorgularıyla uğraşırken pek çok zorluğu soyutlar, ancak daha önce aşağıdakilere benzer bir şey yaptıysanız
User.all.each do |user|
puts user.name # let's print each user's name
end
O zaman, birkaç milyondan fazla kullanıcınız varsa, uygulamanızı sonlandırmanın güzel bir yolunun farkına varacaksınız. Her şey soyutlanmadı. User.all
25 milyon kullanıcıyla aramanın bellek kullanımınızı artıracağını ve sorunlara yol açacağını bilmelisiniz . Bazı temel ayrıntıları bilmeniz gerekir. Soyutlama sızdırıyor.
Bir noktada , ölçeğiniz ve uygulamanız tarafından yönlendirilecek olan gerçeği, neden bu şekilde davrandığını anlamak için soyutlama çerçevenizin uygulama ayrıntılarına aşina olmanız gerekecek.
Örneğin, şu SQL
sorguyu düşünün :
SELECT id, first_name, last_name, age, subject FROM student_details;
Ve alternatifi:
SELECT * FROM student_details;
Şimdi, mantıksal olarak eşdeğer çözümler gibi görünüyorlar, ancak birincisinin performansı, tek tek sütun adları spesifikasyonu nedeniyle daha iyi.
Önemsiz bir örnek ama sonunda Joel Spolsky'nin alıntısına geri dönüyor:
Önemsiz olmayan tüm soyutlamalar bir dereceye kadar sızdırıyor.
Bir noktada, operasyonunuzda belirli bir ölçeğe ulaşacağınız zaman, DB'nizin (SQL) çalışma şeklini optimize etmek isteyeceksiniz. Bunu yapmak için ilişkisel veritabanlarının çalışma şeklini bilmeniz gerekir. Başlangıçta size soyutlanmıştı ama sızdırıyor. Bir noktada öğrenmen gerekiyor.
Bir kitaplıkta aşağıdaki koda sahip olduğumuzu varsayalım:
Object[] fetchDeviceColorAndModel(String serialNumberOfDevice)
{
//fetch Device Color and Device Model from DB.
//create new Object[] and set 0th field with color and 1st field with model value.
}
Tüketici API'yi çağırdığında, bir Nesne [] alır. Tüketici, nesne dizisinin ilk alanının renk değerine sahip olduğunu ve ikinci alanın model değeri olduğunu anlamalıdır. Burada soyutlama, kütüphaneden tüketici koduna sızmıştır.
Çözümlerden biri, Cihazın Modelini ve Rengini içeren bir nesneyi döndürmektir. Tüketici, modeli ve renk değerini almak için bu nesneyi arayabilir.
DeviceColorAndModel fetchDeviceColorAndModel(String serialNumberOfTheDevice)
{
//fetch Device Color and Device Model from DB.
return new DeviceColorAndModel(color, model);
}
Sızdıran soyutlama, durumu kapsüllemekle ilgilidir. sızdıran soyutlamanın çok basit bir örneği:
$currentTime = new DateTime();
$bankAccount1->setLastRefresh($currentTime);
$bankAccount2->setLastRefresh($currentTime);
$currentTime->setTimestamp($aTimestamp);
class BankAccount {
// ...
public function setLastRefresh(DateTimeImmutable $lastRefresh)
{
$this->lastRefresh = $lastRefresh;
} }
ve doğru yol (sızdıran soyutlama değil):
class BankAccount
{
// ...
public function setLastRefresh(DateTime $lastRefresh)
{
$this->lastRefresh = clone $lastRefresh;
}
}
daha fazla açıklama burada .