Uygulamada açık-kapalı ilkesine nasıl uyulur


15

Açık-kapalı prensibinin amacını anlıyorum. Değiştirmeden genişletmeyi denemenizi söyleyerek, değiştirirken zaten çalışan bir şeyi kırma riskini azaltmak içindir.

Ancak, bu prensibin uygulamada nasıl uygulandığını anlamakta zorlandım. Anladığım kadarıyla bunu uygulamanın iki yolu var. Olası bir değişiklikten önce ve sonra:

  1. Önce: soyutlamalara programlayın ve 'geleceği tahmin edin' olabildiğince. Örneğin , gelecekte sisteme s eklenecekse bir yöntemin drive(Car car)değiştirilmesi Motorcyclegerekecektir, bu nedenle muhtemelen OCP'yi ihlal eder. Ancak yöntemin drive(MotorVehicle vehicle)gelecekte değişmesi daha az olasıdır, bu nedenle OCP'ye uyar.

    Ancak, geleceği tahmin etmek ve sistemde hangi değişikliklerin yapılacağını önceden bilmek oldukça zordur.

  2. Sonra: bir değişiklik gerektiğinde, geçerli kodunu değiştirmek yerine bir sınıfı genişletin.

1 numaralı uygulamayı anlamak zor değil. Ancak nasıl uygulanacağını anlamada sorun yaşıyorum.

Örneğin (YouTube'da bir videodan aldı): en biz kabul eden bir sınıfta bir yöntemi var diyelim CreditCardnesneleri: makePayment(CraditCard card). VoucherSisteme bir gün s eklenir. Bu yöntem onları desteklemediğinden değiştirilmelidir.

Yöntemi ilk etapta uygularken geleceği ve programı daha soyut terimlerle tahmin edemedik (örneğin makePayment(Payment pay), şimdi mevcut kodu değiştirmemiz gerekiyor).

Uygulama # 2, işlevselliği değiştirmek yerine genişleterek eklememiz gerektiğini söylüyor. Bu ne anlama geliyor? Sadece varolan kodunu değiştirmek yerine varolan sınıfı alt sınıflara mı ayırmalıyım? Kodu yeniden yazmaktan kaçınmak için etrafına bir tür sarıcı yapmalı mıyım?

Ya da prensip 'işlevselliği doğru bir şekilde nasıl değiştirir / ekler' anlamına gelmez, daha ziyade 'ilk etapta değişiklik yapmaktan (yani soyutlamalara program yapmaktan) kaçınmak anlamına gelir mi?



1
Açık / Kapalı Prensibi kullandığınız mekanizmayı gerektirmez. Kalıtım genellikle yanlış seçimdir. Ayrıca, gelecekteki tüm değişikliklere karşı korunmak imkansızdır. Geleceği tahmin etmeye çalışmak en iyisidir, ancak bir kez değişiklik gerektiğinde, aynı türden gelecekteki değişikliklerin karşılanabilmesi için tasarımı değiştirin.
Doval

Yanıtlar:


14

Tasarım ilkeleri her zaman birbiriyle dengelenmelidir. Geleceği tahmin edemezsiniz ve çoğu programcı bunu denediğinde korkunç bir şekilde yapar. Bu nedenle , öncelikle çoğaltma ile ilgili olan, ancak diğer tasarım ilkeleri için de yeniden düzenleme için geçerli olan üç kuralı var .

Bir arayüzün yalnızca bir uygulamasına sahip olduğunuzda, herhangi bir uzantının nerede olacağı çok net olmadığı sürece OCP'yi fazla önemsemeniz gerekmez. Aslında, bu durumda aşırı tasarım yapmaya çalışırken genellikle netliği kaybedersiniz . Eğer bir kez uzatmak yaptığımızda, bunu dost OCP yapmak refactor eğer bunu yapmak için en kolay ve en net yoludur. Üçüncü bir uygulamaya genişlettiğinizde, biraz daha fazla çaba gerektirse bile OCP'yi dikkate alarak onu yeniden düzenlediğinizden emin olun.

Pratikte, yalnızca iki uygulamanız olduğunda, üçte birini eklediğinizde yeniden düzenleme yapmak genellikle çok zor değildir. O zamandan sonra büyümesine izin verdiğinizde, bakımı zahmetli hale gelir.


1
Cevabın için teşekkür ederim. Bakın ne dediğimi anlayabiliyor muyum: Söylediğiniz şey, çoğunlukla bir sınıfta değişiklik yapmak zorunda kaldıktan sonra OCP'yi önemsemem . Anlamı: Bir sınıfı ilk kez uygularken, OCP hakkında fazla endişelenmemeliyim, çünkü geleceği zaten tahmin etmek zor. İlk kez genişletmem / değiştirmem gerektiğinde, belki biraz daha esnek olmak için gelecekte daha esnek olmak (daha fazla OCP) iyi bir fikirdir. Ve üçüncü kez sınıfı genişletmem / değiştirmem gerekiyor, OCP'ye daha yapışmasını sağlamak için biraz yeniden düzenleme yapmanın zamanı geldi. Demek istediğin bu mu?
Aviv Cohn

1
Fikir bu.
Karl Bielefeldt

2

Bence geleceğe çok fazla bakıyorsun. Mevcut problemi açık / kapalı yapabilecek esnek bir şekilde çözün.

Diyelim ki bir drive(Car car)yöntem uygulamanız gerekiyor . Dilinize bağlı olarak birkaç seçeneğiniz vardır.

  • Aşırı yüklemeyi (C ++) destekleyen diller için, drive(const Car& car)

    Bir noktada daha sonra ihtiyacınız olabilir drive(const Motorcycle& motorcycle), ancak buna müdahale etmez drive(const Car& car). Sorun değil!

  • Aşırı yüklemeyi desteklemeyen diller için (Hedef C), yönteme tür adını ekleyin -driveCar:(Car *)car.

    Bir noktada daha sonra ihtiyacınız olabilir -driveMotorcycle:(Motorcycle *)motorcycle, ama yine de, müdahale etmez.

Bu, drive(Car car)modifikasyona kapatılmasına izin verir , ancak diğer araç tiplerine uzanmaya açıktır. Bugün iş yapmanıza izin veren ancak gelecekte kendinizi engellemenizi önleyen bu minimalist gelecek planlaması.

İhtiyacınız olan en temel türleri hayal etmeye çalışmak sonsuz gerilemeye yol açabilir. Segue, bisiklet veya Jumbo jet kullanmak istediğinizde ne olur? İnsanların içine girdiği ve hareketlilik için kullandığı tüm cihazları açıklayabilecek tek bir genel soyut türü nasıl oluşturuyorsunuz?


Yeni yönteminizi eklemek için bir sınıfı değiştirmek Açık-Kapalı Prensibi'ni ihlal eder. Öneriniz ayrıca Liskov-İkame Prensibi'ni OO'nun en güçlü parçalarından birini ortadan kaldıran araç kullanabilen tüm araçlara uygulama yeteneğini de ortadan kaldırıyor.
Dunk

@ Dunk Cevabımı katı Meyer açık / kapalı prensibine değil, polimorfik açık / kapalı prensibine dayandırdım. Yeni arayüzleri desteklemek için sınıfların güncellenmesine izin verilir. Bu örnekte, araba arayüzü motosiklet arayüzünden ayrı tutulur. Bunlar, uygulayıcı sınıfın destekleyebileceği otomobil ve motosiklet için ayrı sürüş soyut sınıfları olarak resmileştirilebilir.
Jeffery Thomas

@ Dunk Liskov-İkame Prensibi yararlıdır, ancak ücretsiz değildir. Orijinal şartname sadece bir araba gerektiriyorsa, daha genel bir araç oluşturmak ekstra para, zaman ve karmaşıklık maliyetine değmeyebilir. Ayrıca, daha genel bir aracın planlanmamış alt sınıfları işlemek için mükemmel bir şekilde uygun olması pek olası değildir. Ya bir motosikletin ara yüzünün araç arayüzüne ayakkabı takılması gerekir (sadece arabayı idare etmek için tasarlanmıştır) veya motosikleti kullanmak için aracı değiştirmeniz gerekir (açık / kapalı gerçek bir ihlal).
Jeffery Thomas

Liskov-İkame ilkesi ücretsiz değil, aynı zamanda çok fazla maliyetle de gelmiyor. Ve ana uygulamada başka bir alt sınıf asla miras alınmasa bile, genellikle her zamankinden daha fazla geri ödüyor. LSP uygulamak otomatik testi çok daha kolay hale getirir, ki bu zaten bir kazançtır. Ayrıca, kesinlikle çılgınca gitmemelisiniz ve her şeyin LSP'ye ihtiyacı olacağını varsayarsanız, bir uygulama oluşturuyorsanız ve gelecekteki bir devirde neye ihtiyaç duyabileceğine dair iyi bir his yoksa uygulamanız veya alanınız hakkında yeterli bilgiye sahip olacaksınız.
Dunk

1
OCP'nin tanımı ile ilgili olarak. Çalıştığım, sıradan bir ticari şirketten daha yüksek doğrulama seviyeleri gerektiren endüstriler olabilir, ancak genellikle bir dosya / sınıf değişirse sadece dosyayı / sınıfı değil, her şeyi yeniden test etmeniz gerekir. regresyon testinizde bu dosyayı / sınıfı kullanır. Bu nedenle, birisinin polimorfik açık / kapalı iyi olduğunu söylemesi önemli değildir, arayüzün değiştirilmesi çok çeşitli sonuçlara sahiptir, bu yüzden hepsi bu kadar iyi değildir.
Dunk

2

Açık-kapalı prensibinin amacını anlıyorum. Değiştirmeden genişletmeyi denemenizi söyleyerek, değiştirirken zaten çalışan bir şeyi kırma riskini azaltmak içindir.

Aynı zamanda, zaten var olan nesnelerin davranışını değiştirmeyerek bu yönteme güvenen tüm nesneleri kırmamakla da ilgilidir. Bir nesne davranış değiştirmenin reklamını yaptıktan sonra, diğer nesnelerin tam olarak ne beklediğini bilmeden, nesnenin bilinen ve beklenen davranışını değiştirdiğiniz için risklidir.

Bu ne anlama geliyor? Sadece varolan kodunu değiştirmek yerine varolan sınıfı alt sınıflara mı ayırmam gerekir?

Evet.

"Yalnızca kredi kartlarını kabul eder", genel arayüzü aracılığıyla söz konusu sınıfın davranışının bir parçası olarak tanımlanır. Programcı dünyaya bu nesnenin yönteminin sadece kredi kartı aldığını beyan etti. Bunu çok açık olmayan bir yöntem adı kullanarak yaptı, ama bitti. Sistemin geri kalanı buna güveniyor.

Bu o zaman mantıklı olabilirdi, ama şimdi değişmesi gerekiyorsa, kredi kartları dışında bir şeyleri kabul eden yeni bir sınıf yapmalısınız.

Yeni davranış = yeni sınıf

Bir yana - Geleceği tahmin etmenin iyi bir yolu, bir yöntem verdiğiniz adı düşünmektir. Tam olarak hangi ödemeyi yapabileceği konusunda yöntemde belirli kurallara sahip bir yönteme makePayment gibi gerçekten genel bir sondaj yöntemi adı verdiniz mi? Bu bir kod kokusu. Belirli kurallarınız varsa, bunlar yöntem adından açıkça belirtilmelidir - makePayment makeCreditCardPayment olmalıdır. Nesneyi ilk kez yazarken bunu yapın ve diğer programcılar bunun için size teşekkür edecektir.

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.