Nesneye Yönelik Tuzaklardan Kaçınmak, C'den Göç Etmek, Sizin İçin Ne İşe Yarar?


12

Prosedür dillerinde uzun süredir programlıyorum ve bir soruna ilk tepkim, var olan farklı varlıkları (nesneleri) ve ilişkilerini dikkate almaktan ziyade gerçekleştirmek için görevlere ayırmaya başlamak.

OOP'da bir üniversite kursum var ve kapsülleme, veri soyutlama, polimorfizm, modülerlik ve kalıtımın temellerini anladım.

Okudum /programming/2688910/learning-to-think-in-the-object-oriented-way ve /programming/1157847/learning-object-oriented-thinking , ve bu cevaplarda işaret edilen bazı kitaplara bakacağız.

Orta ve büyük ölçekli projelerimin birçoğunun OOP'un etkili kullanımından faydalanacağını düşünüyorum, ancak bir acemi olarak zaman alıcı, yaygın hatalardan kaçınmak istiyorum.

Deneyimlerinize dayanarak, bu tuzaklar nedir ve etraflarında makul yollar nelerdir? Neden tuzaklar olduklarını ve önerilerinizin bu sorunu ele almada ne kadar etkili olduğunu açıklayabiliyorsanız, takdir edilecektir.

"Adil bir sayıda gözlemci ve değiştirici yöntemlere sahip olmak ve özel değişkenleri kullanmak yaygın mıdır, yoksa bunları birleştirmek / azaltmak için teknikler var mı?"

Yöntemleri karıştırmak için iyi nedenler varsa, saf bir OO dili olarak C ++ kullanma konusunda endişelenmiyorum. (GOTO'ları az da olsa kullanmanın nedenlerini hatırlatır.)

Teşekkür ederim!


2
tam bir cevaba layık değil, ama beni kabul etmek için uzun zaman alan önemli bir cevap (yani çok fazla zaman okudum ama fanboy konuşması olarak ele aldım): üye işlevleri yerine ücretsiz işlevler tercih edin. Bu iyi bir şey olan sınıflarınızı minimum tutar.
stijn

@stijn Aslında sınıfta olması gerekmiyorsa, oraya koymayın diyorsunuz. Örneğin, şimdiye kadar okuduğum kodda serbest fonksiyonlar olabilecek yardımcı program işlevlerinin birçoğunu görüyorum.
Stephen

Evet, bu o. 'Üye olmayan arkadaşını tercih et' için arama yaparsanız, bununla ilgili birçok bilgi bulabilirsiniz. Sonunda Tek Sorumluluk İlkesine bağlı
kalmaya

Yanıtlar:


10

Öğrendiğim en büyük şey, dışarıdan sınıflar tasarlamak. Uygulamayı düşünmeye başlamadan önce arayüzü tasarlayın. Bu, sınıfı, temel algoritmaları yazmak ve sınıfı oluşturmak ve gerektiğinde yeni genel üye işlevlerini yazmaktan çok, kullanıcılarınız için (sınıfı kullanan kişiler) çok daha sezgisel hale getirecektir.


7
Buna ek olarak, 'niyetle programlayabiliriz', yani yeni sınıfı kullanacak bazı örnek kodlar yazabiliriz, hangi yöntem ve politikaların kullanımını rahatlattığını görebiliriz. Sonra bu bilgileri uygulamalarda temel alırken kullanın.

2
Teoride harika - tasarım arayüzleri ve temelde bitti. Ancak pratikte bu kadar basit çalışmaz. Arayüz tasarımı ve uygulaması genellikle, son arayüz kristalleşene kadar ardışık yinelemelerde el ele gider. O zaman muhtemelen nihai uygulamaya sahip olursunuz.
Gene Bushuyev

9

Birincisi, çok fazla bilgi ortaya koymanın tuzağı. Varsayılan private, olmamalıdır public.

Bundan sonra çok fazla alıcı / ayarlayıcı geliyor. Diyelim ki veri üyem var. Ben musunuz gerçekten bir başka sınıfta bu veri gerek? Tamam, alıcı ol. Ben mi gerçekten, reaally nesnenin ömrü boyunca bu verileri değiştirmek gerekir? Sonra bir ayarlayıcı yapın.

Çoğu acemi programcı, her veri üyesi için bir alıcı / ayarlayıcı yapmak için varsayılana sahiptir. Bu, arabirimleri keser ve genellikle kötü tasarım seçimleridir.


2
Evet, verileri özel hale getirmek ve sonra alıcılar ve ayarlayıcılar ile kapsüllemeyi üflemek için kapsüllemeyi yüzeysel olarak anlayanlar arasında oldukça popüler.
Gene Bushuyev

Java hala get / set yöntemlerine ihtiyaç duyan tek popüler dil hakkındadır. Diğer her dil özellikleri destekler, bu nedenle genel veri üyeleri ile mülk arasında sözdizimsel bir fark yoktur. Java'da bile, sınıfın tüm kullanıcılarını da kontrol ediyorsanız herkese açık veri üyelerine sahip olmak günah değildir.
kevin cline

Herkese açık veri üyelerinin bakımı daha zordur. Alıcıları / ayarlayıcıları (veya özellikleri) kullanarak, dahili veri sunumunu değiştirirken dış kodu değiştirmeden arayüzü koruyabilirsiniz. Özelliklerin, tüketicinin bakış açısından üye değişkenlerle sözdizimsel olarak özdeş olduğu dillerde, bu nokta yine de geçerli değildir.
tdammers

Şimdiye kadar özel üyelere bir fonksiyonda yerel değişkenler olarak "çeşit" olarak baktığımı düşünüyorum ... dünyanın geri kalanının fn'nin çalışması için onlar hakkında hiçbir şey bilmesine gerek yok ... ve fn yalnızca arayüzün tutarlı olması gerekir. Ben doğru yolda olabilir, bu yüzden aslında yazdım bir tampon sınıf bakarak, hiç kamu veri üyeleri vardır spam alıcı / ayarlayıcı ile gitmek eğilimindedir. Teşekkürler!
Stephen

2

Bu uçurumdan geçtiğimde aşağıdaki yaklaşıma karar verdim:

0) Yavaş başladım, küçük / orta büyüklükteki prosedürel uygulamalarla ve iş için kritik öneme sahip hiçbir şey yok.

1) OO tarzında programı sıfırdan nasıl yazacağımı gösteren basit bir 1.-geçiş haritalaması - en önemlisi benim için ve bu IS özneldir - tüm temel sınıfları bulmaktı. Benim amacım, temel sınıflarda olabildiğince kapsüllemekti. Temel sınıflarda mümkün olan her şey için saf sanal yöntemler.

2) Daha sonra bir sonraki adım türevler oluşturmaktı.

3) son adım - orijinal prosedür kodunda, veri yapılarını gözlemci / değiştirici kodundan ayırmaktır. Daha sonra veri gizlemeyi kullanın ve tüm ortak verileri temel sınıflara eşleyin ve alt sınıflarda programda ortak olmayan veriler gitti. Prosedürel gözlemci / değiştirici kodu için de aynı tedavi - tüm 'her yerde kullanılan' mantığı temel sınıflara girdi. Ve sadece verilerin bir alt kümesine etki eden mantığı görüntüleme / değiştirme türetilmiş sınıflara girdi.

Bu sübjektif ama FAST ise eğer iyi prosedürel kod ve veri yapılarını biliyorum. Kod incelemesindeki bir FAIL, temel sınıflarda biraz veri veya mantık görünmediği, ancak her yerde kullanıldığı zamandır.


2

İyi bir stil duygusu elde etmek için OOP kullanan diğer başarılı projelere bir göz atın. Kendi tasarım kararlarımı verirken hep aradığım Qt projesine bakmanızı tavsiye ederim .


Evet! Bir kişi bir şeyin ustası olmadan önce, kendisinden önce çalışan büyük ustaları taklit etmeyi öğrenir. Başkalarının uyguladığı iyi kodu incelemek, öğrenmenin iyi bir yoludur. Artırmaya bakmayı, basit tasarımlardan başlamayı ve anlayışı geliştikçe daha derine inmeyi öneririm.
Gene Bushuyev

1

Kiminle konuştuğunuza bağlı olarak, OOP'deki tüm veriler özel veya korumalı olmalı ve yalnızca erişimciler ve mutasyon uzmanları aracılığıyla erişilebilir olmalıdır. Genel olarak, bunun iyi bir uygulama olduğunu düşünüyorum, ancak bu normdan saptığım durumlar var. Örneğin, bir sınıfınız varsa (Java'da diyelim ki) tek amacı bazı veri parçalarını mantıksal bir üniteye bölmektir, alanları herkese açık bırakmak mantıklıdır. Değişmez bir kapsülse, onları nihai olarak işaretleyin ve yapıcıda başlatın. Bu, sınıfı (bu örnekte) bir yapıdan biraz daha fazla azaltır (aslında, C ++ kullanarak, bunu aslında bir yapı olarak adlandırmalısınız. Tıpkı bir sınıf gibi çalışır, ancak varsayılan görünürlük herkese açıktır ve niyetiniz daha açıktır), ancak Bu durumlarda kullanmanın çok daha rahat olduğunu düşünüyorum.

Kesinlikle yapmak istemediğiniz bir şey , mutatörde tutarlılık açısından kontrol edilmesi ve herkese açık bırakılması gereken bir alana sahip olmaktır.


3
Ayrıca, herkese açık verileri tutan bu tür sınıflarınız varsa, bunları structs olarak bildirmelisiniz - anlamsal bir fark yaratmaz, ancak niyeti açıklar ve kullanımlarını, özellikle C programcıları için daha doğal hale getirir.

1
Evet, burada C ++ (bu sorunun bahsettiği soru) iseniz, katılıyorum, ancak işimin çoğunu Java'da yapıyorum ve maalesef hiçbir yapımız yok.

toplu sınıflar (seyrek vaka) ve işlevselliği olan sınıflar (genel durum) arasında açıkça ayrım yapmanız gerekir. İkincisi kapsülleme uygulamalı ve üyelerine sadece genel arayüz aracılığıyla erişmelidir, hiçbir veri kamuya açıklanmamalıdır.
Gene Bushuyev

1

Kent Beck'in Uygulama Desenleri, yanlış kullanım değil, nesne yönelimli mekanizmaların nasıl kullanılacağı konusunda mükemmel bir temeldir.


1

Yöntemleri karıştırmak için iyi nedenler varsa, saf bir OO dili olarak C ++ kullanma konusunda endişelenmiyorum. (GOTO'ları az da olsa kullanmanın nedenlerini hatırlatır.)

Bu biti görene kadar gerçekten konuşacak çok şeyim olduğunu düşünmemiştim. Duyguya katılmıyorum. OOP, C ++ 'da kullanılabilen ve kullanılması gereken paradigmalardan sadece biridir. Açıkçası, bence en güçlü özelliklerinden biri değil.

OO açısından, C ++ aslında biraz kısadır düşünüyorum. Örneğin sanal olmayan fonksiyonlara sahip olma fikri bu açıdan ona karşı bir kene. Benimle aynı fikirde olmayanlarla tartıştım, ancak sanal olmayan üyeler benim endişelerime göre paradigmaya uymuyor. Polimorfizm OO için anahtar bir bileşendir ve sanal olmayan fonksiyonları olan sınıflar OO anlamında polimorfik değildir. Yani bir OO dili olarak C ++'ın Java veya Objective-C gibi dillerle karşılaştırıldığında aslında oldukça zayıf olduğunu düşünüyorum.

Diğer yandan, genel programlama, C ++ bu oldukça iyidir. Bunun için daha iyi diller olduğunu söylediğini duydum, ancak nesnelerin ve genel işlevlerin kombinasyonu oldukça güçlü ve etkileyici bir şey. Ayrıca programlama süresi ve işlem süresi de çok hızlı olabilir. Gerçekten bu alanda C ++ parlıyor olsa da itiraf daha iyi olabilir düşünüyorum (örneğin kavramlar için dil desteği). OO paradigmasına sadık kalmaları ve diğerlerine ahlaksızlık düzeylerinde goto ifadesinin sırasına göre davranmaları gerektiğini düşünen biri, bu paradigmaya bakmamakla gerçekten kayboluyor.

Şablonların meta programlama kapasitesi de oldukça etkileyicidir. Örneğin Boost.Units kitaplığına bakın. Bu kütüphane boyutsal büyüklükler için tip desteği sağlar. Şu anda çalıştığım mühendislik firmasında bu kütüphaneyi kapsamlı bir şekilde kullandım. Olası programcıların bir yönü ve hatta spesifikasyon hatası için çok daha hızlı bir geri bildirim sağlar. '=' Operatörünün her iki tarafının da açık döküm olmadan boyutsal olarak eşdeğer olmadığı bir formül kullanan bir program derlemek imkansızdır. Ben şahsen bunun mümkün olduğu başka bir dil ile hiçbir deneyimim yok ve kesinlikle C ++ gücü ve hızına sahip bir dil ile değil.

Metaprogramlama tamamen işlevsel bir paradigmadır.

Gerçekten, bazı talihsiz yanılgılarla C ++ 'a adım attığınızı düşünüyorum. OO dışındaki diğer paradigmalardan kaçınılmalı, LEVERAGED uygulanmalıdır. Üzerinde çalıştığınız sorunun yönü için doğal olan paradigmayı kullanın. Nesneleri, aslında nesneye eğilimli bir sorun olmayan şeylere zorlamayın. Benim düşünceme göre, OO, C ++ için hikayenin yarısı bile değil.


Sanal anahtar kelime C ++ içinde sanal sınıf işlevselliği sağlamaz mı? Git yorumuna gelince, sürdüğüm şey, akıl yürütmeyi anladığım sürece ampirik kuralları çiğnemekten endişe etmememdi. Bununla birlikte, OO lehine gerekenden daha fazla zorunluluk / usule ilişkin daha fazla çaba sarf ediyorum. Teşekkürler.
Stephen

sanal yöntemler polimorfizm için bir önkoşul değildir, bunu yapmanın yaygın bir yoludur. aslında, her şey eşittir, daha sonra sanal bir yöntem yapmak aslında kapsüllemeyi zayıflatır, çünkü sınıfın api'sinin boyutunu artırıyorsunuz ve liskov'u izlediğinizden emin olmanızı zorlaştırıyorsunuz. sadece bir dikişe ihtiyacınız varsa sanal bir şey yapın, kalıtım yoluyla yeni davranışlar enjekte etmek için bir yer (kalıtım da OOP'de dikkatli olunması gereken bir şey olsa da). sanal için sanal uğruna bir sınıf "daha fazla OOP" yapmaz
sara

1

Bu soruya bir cevap kabul etmek istedim, ancak onay işaretini vermek için bir cevaba karar veremedim. Bu nedenle orijinal yazarları iptal ettim ve bunu özet bir cevap olarak oluşturdum. Birkaç dakika süren herkese teşekkürler, sağladığınız içgörünün bana iyi bir yön verdiğini ve raylardan çıkmadığım için biraz güven verdiğini buldum.

@nightcracker

Birincisi, çok fazla bilgi ortaya koymanın tuzağı. Varsayılan, herkese açık değil, gizli olmalıdır. Bundan sonra çok fazla alıcı / ayarlayıcı geliyor.

Bu problemi geçmişte çalışırken gözlemlediğimi hissettim. Yorumlarınız, altta yatan değişkenleri ve bunların uygulanmasını gizleyerek, onlara bağlı hiçbir şeyi yok etmeden uygulamalarını değiştirmekte özgür olduğumu da hatırlattı.

Dominic Gurto

Uygulamayı düşünmeye başlamadan önce arayüzü tasarlayın. Gene Bushuyev Arayüzü tasarımı ve uygulaması genellikle son arayüz kristalleşene kadar ardışık yinelemelerde el ele gider.

Dominic'in yorumunun arzulamak için harika bir ideal olduğunu düşündüm, ancak Gene'in yorumunun gerçekten durumun gerçekliğine çarptığını düşünüyorum. Şimdiye kadar bunu eylemde gördüm ... ve nadir olmadığından biraz daha iyi hissediyorum. Bir programcı olarak olgunlaştığımda daha eksiksiz tasarımlara yaslanacağımı düşünüyorum, ama şu anda hala atlamaktan ve bazı kodları yazılı olarak almak.

wantTheBest

Yavaş, küçük / orta ölçekli prosedürel uygulamalarla başladım ve işte kritik görevler yoktu. orijinal prosedür kodunda, veri yapılarını gözlemci / değiştirici kodundan ayırın

Bu çok mantıklı ... İşleri iş yerinde tutma fikrini sevdim, ancak bazı kritik olmayan şeyleri sınıflarla yeniden düzenleme fikrini sevdim.

jpm

Kesinlikle yapmak istemediğiniz bir şey, mutatörde tutarlılık açısından kontrol edilmesi ve herkese açık bırakılması gereken bir alana sahip olmaktır.

Bir süredir bunun verilerin kapsüllenmesinin güçlü yönlerinden biri olduğunu biliyorum ... tutarlılığı ve bu koşulların / aralıkların / vb.

Çılgın Eddie

OO paradigmasına sadık kalmaları ve diğerlerine ahlaksızlık düzeylerinde goto ifadesinin sırasına göre davranmaları gerektiğini düşünen biri, bu paradigmaya bakmamakla gerçekten kayboluyor. Şablonların meta programlama kapasitesi de oldukça etkileyicidir.

Başlangıçta Crazy Eddie'nin cevabında çok şey kaçırdım, sanırım çünkü bahsettiğim bazı konuları okumadım ... metaprogramlama gibi. CE'nin gönderisindeki en büyük mesaj, C ++ 'ın her birinin en iyi potansiyeline kullanılması gereken yeteneklerin ve stillerin bir karışımı olmasıydı.

Tekrar cevap veren herkese teşekkürler!


0

En büyük tuzak, OOP'un gümüş bir kurşun veya "tek bir mükemmel paradigma" olduğu inancıdır.

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.