Kütüphanelerdeki görünürlüğü ele almanın genel yolu nedir?


12

Sınıflarda ne zaman özel ve ne zaman korumalı kullanılacağına dair bu soru beni düşündürdü. (İlgili olduğu için bu soruyu son sınıflara ve yöntemlere de genişleteceğim. Java ile programlama yapıyorum, ancak bunun her OOP dili ile ilgili olduğunu düşünüyorum)

Kabul edilen cevap:

İyi bir kural: her şeyi olabildiğince özel yapmak.

Ve bir tane daha:

  1. Hemen alt sınıfa ayırmanız gerekmedikçe tüm sınıfları final 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.

Bu oldukça basit ve açıktır, ancak uygulamalar yerine çoğunlukla kütüphaneler (GitHub'da Açık Kaynak) yazıyorsam ne olur?

Bir çok kütüphane ve durumu adlandırabilirim,

  • Bir kütüphane, geliştiricilerin asla düşünmeyeceği şekilde genişletildi
  • Bu, görünürlük kısıtlamaları nedeniyle "sınıf yükleyici büyüsü" ve diğer hack'lerle yapılmalıdır.
  • Kütüphaneler, geliştirilmedikleri bir şekilde kullanıldı ve gerekli işlevler "
  • Görünürlüğün azalması nedeniyle değiştirilemeyen küçük bir sorun (hata, işlevsellik, "yanlış" davranış) nedeniyle kitaplıklar kullanılamadı
  • Düzeltilemeyen bir sorun, basit bir işlevi (özel veya son) geçersiz kılmanın yardımcı olabileceği büyük, çirkin ve buggy geçici çözümlere yol açtı.

Ve aslında soru çok uzayıncaya kadar bunları adlandırmaya başladım ve bunları kaldırmaya karar verdim.

Ben gerekenden daha fazla kod, gerekenden daha fazla görünürlük, gerekenden daha fazla soyutlama fikrini seviyorum. Bu, kodun yalnızca yazanlar tarafından kullanıldığı son kullanıcı için bir uygulama yazarken işe yarayabilir. Ancak, kodun diğer geliştiriciler tarafından kullanılması gerekiyorsa, orijinal geliştiricinin olası her kullanım durumunu önceden düşünmesinin ve değişikliklerin / refaktörlerin yapmasının zor / imkansız olması nasıl mümkün olur?

Büyük açık kaynak kütüphaneleri yeni bir şey olmadığından, nesne yönelimli dilde bu tür projelerde görünürlüğü ele almanın en yaygın yolu nedir?



Açık kaynak hakkında soru sormanız durumunda, listelediğiniz sorunları kapalı kaynaktan daha iyi ele almak için uygun kodlama ilkelerini bükmek daha az mantıklıdır, çünkü sadece gerekli düzeltmeleri doğrudan kütüphane koduna ekleyebilir veya çatallayabilir ve kendi versiyonunu yapabilir istedikleri düzeltmeler
gnat

2
Demek istediğim bu değil, bu bağlamda hiçbir anlam ifade etmeyen açık kaynak referansınız. Pragmatik ihtiyaçların bazı durumlarda ( teknik borç tahakkuk olarak da bilinir) katı ilkelerden sapmayı nasıl haklı çıkarabildiğini hayal edebiliyorum ama bu açıdan kodun kapalı veya açık kaynak olması önemli değil. Ya da daha doğrusu, burada hayal ettiğinizden ters yönde önemlidir, çünkü kod açık kaynak olması, bu ihtiyaçları kapalı olanlardan daha az presleme yapabilir, çünkü bunları ele almak için ek seçenekler sunar
gnat

1
@piegames: Ben burada sivriltmeyi tamamen kabul ediyorum, çizdiğin problemlerin kapalı kaynak kütüphanelerinde ortaya çıkma olasılığı daha yüksek - izin verilen lisansa sahip bir OS lib ise, koruyucular bir değişiklik isteğini göz ardı ederse, lib ve gerekirse görünürlüğü kendiniz değiştirin.
Doc Brown

1
@piegames: Sorunu anlamıyorum. "Java" bir dildir, bir lib değildir. Ve "küçük açık kaynak kütüphaneniz" çok katı bir görünürlüke sahipse, daha sonra görünürlüğü artırmak normalde geriye dönük uyumluluğu bozmaz. Sadece tam tersi.
Doc Brown

Yanıtlar:


15

Talihsiz gerçek şu ki, pek çok kütüphane tasarlanmamış , yazılıyor . Bu üzücüdür, çünkü önceden düşünülmüş bir miktar düşünce yolda birçok sorunu önleyebilir.

Bir kütüphane tasarlamaya başlarsak, bazı beklenen kullanım durumları olacaktır. Kütüphane tüm kullanım örneklerini doğrudan karşılamayabilir, ancak bir çözümün parçası olabilir. Bu yüzden kütüphanenin uyum sağlayabilecek kadar esnek olması gerekir.

Kısıtlama, kütüphanenin kaynak kodunu alıp yeni kullanım durumunu ele alacak şekilde değiştirmenin genellikle iyi bir fikir olmamasıdır. Özel kütüphaneler için kaynak mevcut olmayabilir ve açık kaynak kütüphaneler için çatallı bir versiyonun korunması istenmeyebilir. Son derece spesifik uyarlamaları, yukarı akış projesine birleştirmek mümkün olmayabilir.

Burada açık-kapalı prensibi devreye girer: kütüphane kaynak kodunu değiştirmeden uzantıya açık olmalıdır. Bu doğal olarak gelmiyor. Bu kasıtlı bir tasarım hedefi olmalıdır. Burada yardımcı olabilecek çok sayıda teknik var, klasik OOP tasarım kalıpları bunlardan bazıları. Genel olarak, kullanıcı kodunun kütüphaneye güvenli bir şekilde takılabileceği ve işlevsellik ekleyebileceği kancaları belirtiriz.

Her yöntemi herkese açık hale getirmek veya her sınıfın alt sınıflara ayrılmasına izin vermek, genişletilebilirliği sağlamak için yeterli değildir. Öncelikle, kullanıcının kütüphaneye nereden bağlanabileceği açık değilse, kütüphaneyi genişletmek gerçekten zordur. Örneğin, çoğu sınıfın geçersiz kılınması güvenli değildir, çünkü temel sınıf yöntemi örtük varsayımlarla yazılmıştır. Gerçekten genişletilebilirlik için tasarım yapmanız gerekiyor.

Daha da önemlisi, bir şey herkese açık API'nın bir parçası olduğunda onu geri alamazsınız. Aşağı akış kodunu kırmadan yeniden düzenleyemezsiniz. Erken açıklık kütüphaneyi yetersiz bir tasarımla sınırlar. Buna karşılık, iç eşyaları özel yapmak, ancak daha sonra bunlara ihtiyaç varsa kanca eklemek daha güvenli bir yaklaşımdır. Bu, bir kütüphanenin uzun vadeli evrimiyle başa çıkmak için akılcı bir yol olsa da, şu anda kütüphaneyi kullanması gereken kullanıcılar için bu tatmin edici değildir .

Peki bunun yerine ne olacak? Kütüphanenin mevcut durumu ile önemli bir acı varsa, geliştiriciler zamanla biriken gerçek kullanım durumları hakkındaki tüm bilgileri alabilir ve kütüphanenin Sürüm 2'sini yazabilir. İyi olacak! Tüm bu tasarım hatalarını düzeltir! Ayrıca beklenenden daha uzun sürecek, çoğu durumda fışkırıyor. Ve yeni sürüm eski sürüme çok benzemiyorsa, kullanıcıları taşımaya teşvik etmek zor olabilir. Daha sonra iki uyumsuz sürümü kullanmaya devam edersiniz.


Bu yüzden sadece kamu / geçersiz kılmak yeterli değil çünkü uzantısı için kanca eklemeniz gerekir. Ayrıca, geriye dönük uyumluluk nedeniyle değişiklikleri / yeni API'yi ne zaman yayınlayacağımı da düşünmem gerekiyor. Peki ya özel yöntemlerin görünürlüğü?
Piegames

@piegames Görünürlükle hangi bölümlerin herkese açık olduğuna (kararlı API'nizin bir parçası) ve hangi bölümlerin gizli olduğuna (değişebilir) karar verirsiniz. Birisi bunu yansıma ile atlarsa, bu özellik gelecekte bozulduğunda bu sorun olur. Bu arada, uzatma noktaları genellikle geçersiz kılınabilecek bir yöntem biçimindedir. Ancak bir yönteme arasında bir fark var olabilir geçersiz kılınmış olabilir ve bir yönteme yöneliktir (aynı zamanda Kalıp yönteminin bakınız) geçersiz edilmesi.
amon

8

Her genel ve genişletilebilir sınıf / yöntem, API'nızın desteklenmesi gereken bir parçasıdır. Bu ayarı kütüphanenin makul bir alt kümesiyle sınırlamak en fazla kararlılığa izin verir ve yanlış gidebilecek şeylerin sayısını sınırlar. Bu, makul bir şekilde neleri destekleyebileceğinize bağlı olarak bir yönetim kararıdır (ve hatta OSS projeleri bir dereceye kadar yönetilir).

OSS ve kapalı kaynak arasındaki fark, çoğu insanın kod etrafında bir topluluk oluşturmaya ve büyütmeye çalışmasıdır, böylece kütüphaneyi koruyan birden fazla kişi olur. Bununla birlikte, mevcut çeşitli yönetim araçları vardır:

  • Posta listeleri kullanıcı ihtiyaçlarını ve bir şeylerin nasıl uygulanacağını tartışır
  • Sorun izleme sistemleri (JIRA veya Git sorunları vb.) Hataları ve özellik isteklerini izler
  • Sürüm kontrolü kaynak kodunu yönetir.

Olgun projelerde, göreceğiniz şey bu çizgiler boyunca bir şeydir:

  1. Birisi, başlangıçta yapmak için tasarlanmadığı kütüphaneyle bir şeyler yapmak istiyor
  2. Sorun izlemeye bir bilet ekliyorlar
  3. Ekip sorunu posta listesinde veya yorumlarda tartışabilir ve istekte bulunan kişi her zaman tartışmaya katılmaya davet edilir
  4. API değişikliği bir nedenle kabul edilir ve önceliklendirilir veya reddedilir

Bu noktada, değişiklik kabul edildi, ancak kullanıcı düzeltilmesini hızlandırmak istiyorsa, işi yapabilir ve bir çekme isteği veya bir yama gönderebilir (sürüm kontrol aracına bağlı olarak).

Hiçbir API statik değildir. Ancak büyümesinin bir şekilde şekillenmesi gerekiyor. Bir şeyleri açmak için kanıtlanmış bir ihtiyaç olana kadar her şeyi kapalı tutarak, buggy veya kararsız bir kütüphanenin itibarını almaktan kaçınırsınız.


1
Tamamen katılıyorum ve başarılı bir şekilde 3. taraf kapalı kaynak kütüphaneleri ve açık kaynak kütüphaneleri için ortaya koyduğunuz değişiklik talebi sürecini uyguladım.
Doc Brown

Deneyimlerime göre, küçük kütüphanelerdeki küçük değişiklikler bile çok fazla iş (sadece diğerlerini ikna etmek olsa bile) ve biraz zaman alabilir (o zamana kadar bir anlık görüntü kullanmayı göze alamazsanız bir sonraki sürümü beklemek için). Yani bu benim için açıkça bir seçenek değil. Yine de ilgileniyorum: Bu konsepti gerçekten kullanan daha büyük kütüphaneler (GitHub'da) var mı?
Piegames

Her zaman çok iştir. Katkıda bulunduğum hemen hemen her projenin buna benzer bir süreci var. Apache günlerimde günlerce bir şeyler tartışabiliriz, çünkü yarattıklarımız hakkında tutkuluyduk. Birçok insanın kütüphaneleri kullanacağını biliyorduk, bu yüzden önerilen değişikliğin API'yi kıracak mı diye tartışmak zorunda kaldık, önerilen özellik buna değer mi, ne zaman yapmalıyız? vb.
Berin Loritsch

0

Birkaç kişiyle sinire çarptığı göründüğünden cevabımı yeniden yazacağım.

sınıf özellik / yöntem görünürlüğünün güvenlik veya kaynak açıklığı ile ilgisi yoktur.

Görünürlüğün var olmasının nedeni, nesnelerin 4 spesifik soruna kırılgan olmasıdır:

  1. eşzamanlılık

Modülünüzü kapsüllenmemiş olarak oluşturursanız, kullanıcılarınız modül durumunu doğrudan değiştirmeye alışacaktır. Bu, tek iş parçacıklı bir ortamda iyi çalışır, ancak bir kez iş parçacığı eklemeyi düşündüğünüzde; devleti özel yapmak zorunda kalacaksınız ve diğer iş parçacıkları için yarışmak yerine kaynakları bekleten alıcılar ve ayarlayıcılarla birlikte kilitleri / monitörleri kullanmak zorunda kalacaksınız. Bu, özel değişkenlere geleneksel bir şekilde erişilemediği için kullanıcı programlarınızın artık çalışmadığı anlamına gelir. Bu, birçok yeniden yazmaya ihtiyacınız olduğu anlamına gelebilir.

Gerçek şu ki, tek bir iş parçacığı çalışma zamanını düşünerek kodlamak çok daha kolay ve özel anahtar kelime, senkronize edilen anahtar kelimeyi veya birkaç kilidi eklemenize izin veriyor ve başlangıçta kapsülleme yaparsanız kullanıcılarınızın kodu kırılmayacak .

  1. Kullanıcıların kendilerini ayağa vurmasını engellemeye yardımcı olun / arayüzün kullanımını düzene sokun. Özünde, nesnenin değişmezlerini kontrol etmenize yardımcı olur.

Her nesnenin tutarlı bir durumda olması için gerçek olması gereken bir sürü şey vardır. Ne yazık ki, bu şeyler müşterinin görünür alanında yaşar, çünkü her nesneyi kendi sürecine taşımak ve mesajlarla konuşmak pahalıdır. Bu, kullanıcının tam görünürlüğe sahip olması durumunda bir nesnenin tüm programı çökmesinin çok kolay olduğu anlamına gelir.

Bu kaçınılmazdır, ancak yalnızca kullanıcının programı çok daha sağlam hale getiren dikkatlice hazırlanmış bir arabirim aracılığıyla nesnenin durumuyla etkileşime girmesine izin vererek yanlışlıkla çökmeleri önleyen bir arabirim kapatması yaparak, bir nesneyi hizmetlerinin üzerinde bir arabirim kapatması yaparak yanlışlıkla bir nesneyi tutarsız hale getirebilirsiniz. . Bu, kullanıcının değişmezleri kasıtlı olarak bozmayacağı anlamına gelmez, ancak eğer yaparsa, çöken istemcileri, tek yapmaları gereken programı yeniden başlatmaktır (korumak istediğiniz veriler istemci tarafında saklanmamalıdır) ).

Modüllerinizin kullanılabilirliğini artırabileceğiniz bir diğer güzel örnek, yapıcıyı özel yapmaktır; çünkü kurucu bir istisna atarsa, programı öldürecektir. Bunu çözmek için tembel bir yaklaşım, yapıcı bir try / catch bloğunda olmadığı sürece inşa edemeyeceğiniz bir derleme zamanı hatası atmasını sağlamaktır. Yapıcıyı özel hale getirerek ve genel bir statik oluşturma yöntemi ekleyerek, oluşturma yöntemini oluşturmazsa null döndürme yöntemini döndürebilir veya programı daha kullanıcı dostu hale getirmek için hatayı işlemek üzere bir geri arama işlevi alabilirsiniz.

  1. Kapsam kirliliği

Birçok sınıfın çok fazla durumu ve yöntemi vardır ve bunlar arasında gezinmeye çalışırken bunalmak kolaydır; Bu yöntemlerin çoğu yardımcı fonksiyonlar, durum gibi görsel gürültüdür. değişkenleri ve yöntemleri özel yapmak, kapsam kirliliğini azaltmaya ve kullanıcının aradığı hizmetleri bulmasını kolaylaştırmaya yardımcı olur.

Özünde, sınıfın dışında değil, sınıf içinde yardımcı işlevlere sahip olmanızı sağlar; görünürlük kontrolü olmadan, kullanıcının asla kullanmaması gereken bir grup hizmetle dikkatini dağıtmadan, yöntemleri bir grup yardımcı yöntem haline getirerek (yine de kapsamınızı kirletecek, ancak kullanıcıyı değil).

  1. bağımlılıklara bağlı olmak

İyi hazırlanmış bir arayüz, işini yapmaya bağlı olduğu dahili veritabanlarını / pencerelerini / görüntülemesini gizleyebilir ve başka bir veritabanına / başka bir pencereleme sistemine / başka bir görüntüleme kütüphanesine geçmek isterseniz, arayüzü aynı ve kullanıcılar arasında tutabilirsiniz. farketmez.

Öte yandan, bunu yapmazsanız, bağımlılıklarınızı değiştirmeyi imkansız hale getirmek için kolayca düşebilirsiniz, çünkü bunlar açığa çıkar ve kod buna dayanır. Yeterince büyük bir sistemle, taşıma maliyeti karşılanamaz hale gelirken, bir kapsülleme, iyi davranan müşteri kullanıcılarını bağımlılıkları değiştirmek için gelecekteki kararlardan koruyabilir.


1
"Hiçbir şeyi gizlemenin bir anlamı yok" - o zaman neden kapsüllemeyi düşünelim? Birçok bağlamda düşünmek özel ayrıcalıklar gerektirir.
Frank Hileman

Kapsüllemeyi düşünüyorsunuz çünkü modülünüzü geliştirirken size nefes alan ve yanlış kullanım olasılığını azaltıyor. Örneğin, bir sınıfın iç durumunu doğrudan değiştiren 4 iş parçacığınız varsa, bu kolayca sorunlara neden olurken, değişkeni özel yapmak, kullanıcıyı dünya durumunu değiştirmek için genel yöntemleri kullanmaya teşvik eder, bu da sorunları önlemek için monitörleri / kilitleri kullanabilir . Bu, kapsüllemenin tek gerçek yararıdır.
Dmitry

Güvenlik uğruna bir şeyleri saklamak, API'nizde deliklere sahip olmak zorunda olduğunuz bir tasarım yapmak için kolay bir yoldur. Bunun iyi bir örneği, birçok araç kutunuzun ve alt pencereli birçok pencerenin bulunduğu çok belgeli uygulamalardır. Kapsülleme üzerine fındık girerseniz, bir belgeye bir şey çizmek için bir duruma sahip olursunuz, pencereden iç belgeden iç belgeden iç belgeden bir şey çizme talebinde bulunmasını istemesini istemelisiniz. ve bağlamını geçersiz kılar. Müşteri tarafı müşteri tarafı ile oynamak istiyorsa, onları engelleyemezsiniz.
Dmitry

Tamam, daha mantıklı, ancak ortam bunu destekliyorsa güvenlik erişim kontrolü ile sağlanabilir ve OO dil tasarımındaki orijinal hedeflerden biriydi. Ayrıca kapsüllemeyi teşvik ediyor ve aynı anda kullanmadığını söylüyorsunuz; biraz kafa karıştırıcı.
Frank Hileman

Hiç kullanmamayı istemedim; Bunu güvenlik uğruna kullanmama niyetindeydim; kullanıcılarınızın deneyimini geliştirmek ve kendinize daha sorunsuz bir geliştirme ortamı sağlamak için stratejik olarak kullanın. Demek istediğim, güvenlik ya da kaynak açıklığı ile hiçbir ilgisi yok. İstemci tarafı nesneleri tanım gereği içgözleme karşı savunmasızdır ve bunları kullanıcı işlem alanından çıkarmak, kapsüllenmemiş şeyleri kapsüllenmiş olarak eşit şekilde erişilemez hale getirir.
Dmitry
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.