Kaçınılacak tasarım desenleri [kapalı]


105

Pek çok insan, Singleton modelinin bir takım dezavantajları olduğu konusunda hemfikir görünüyor ve hatta bazıları modelden tamamen kaçınmayı öneriyor. Burada mükemmel bir tartışma var . Lütfen Singleton modeliyle ilgili her türlü yorumu bu soruya yöneltin.

Sorum : Kaçınılması veya çok dikkatli kullanılması gereken başka tasarım modelleri var mı?


Tasarım Antipatterns'in harika bir listesini fark etmem gerekiyor deviq.com/antipatterns
Geliştirici

@casperOne soruyu neden kapattınız? Soru meşruydu.
bleepzter

Yanıtlar:


149

Desenler karmaşıktır

Tüm tasarım desenleri dikkatli kullanılmalıdır. Bence , hemen bir model uygulamak yerine, bunu yapmak için geçerli bir neden olduğunda kalıpları yeniden gözden geçirmelisiniz . Kalıpları kullanmanın genel sorunu, karmaşıklık katmalarıdır. Modellerin aşırı kullanımı, belirli bir uygulamayı veya sistemi daha da geliştirmek ve sürdürmek için zahmetli hale getirir.

Çoğu zaman basit bir çözüm vardır ve herhangi bir özel kalıp uygulamanıza gerek kalmaz. İyi bir temel kural, kod parçalarının değiştirilmesi veya sık sık değiştirilmesi gerektiğinde bir model kullanmak ve bir model kullanırken karmaşık kod uyarısını almaya hazır olmaktır.

Kodunuzda değişikliği desteklemek için pratik bir ihtiyaç görürseniz , amacınızın basitlik olması ve bir kalıp kullanmanız gerektiğini unutmayın .

Kalıplar üzerindeki ilkeler

Aşırı tasarlanmış ve karmaşık çözümlere yol açabileceklerse, kalıpları kullanmak bir tartışma gibi görünebilir. Bununla birlikte, bir programcı için modellerin çoğunun temelini oluşturan tasarım tekniklerini ve ilkelerini okumak çok daha ilginçtir . Aslında , 'tasarım kalıpları' üzerine en sevdiğim kitaplardan biri, söz konusu modele hangi ilkelerin uygulanabilir olduğunu yineleyerek bunu vurguluyor . Alaka düzeyi açısından modellerden faydalı olacak kadar basittirler. Kodunuzun modülleri oluşturabildiğiniz sürece , ilkelerden bazıları, Liskov İkame İlkesi gibi nesne yönelimli programlamadan (OOP) daha fazlasını kapsayacak kadar geneldir .

Çok sayıda tasarım ilkesi vardır, ancak GoF kitabının ilk bölümünde açıklananlar başlamak için oldukça yararlıdır.

  • Bir 'uygulama' değil, bir 'arayüz' için programlayın. (Dörtlü Çete 1995: 18)
  • "Nesne bileşimini" "sınıf kalıtımına" tercih edin. (Dörtlü Çete 1995: 20)

Bir süreliğine bunların içine kapılmasına izin ver. GoF yazıldığında, arayüzün Java veya C # tür olarak arayüzle karıştırılmaması gereken, soyutlama olan (süper sınıflar anlamına gelen) herhangi bir şey anlamına geldiği unutulmamalıdır . İkinci ilke, maalesef bugün hala yaygın olan, gözlemlenen aşırı miras kullanımından kaynaklanmaktadır .

Oradan , Robert Cecil Martin (aka Bob Amca) tarafından bilinen SOLID ilkelerini okuyabilirsiniz . Scott Hanselman, Bob Amca ile bu ilkeler hakkında bir podcast'te röportaj yaptı :

  • Tek Sorumluluk İlkesi
  • O pen Kapalı Prensibi
  • L iskov İkame Prensibi
  • Ben ayrılığı prensibi nterface
  • D ependency Reversiyon Prensibi

Bu ilkeler, meslektaşlarınızla okumak ve tartışmak için iyi bir başlangıçtır. İlkelerin birbiriyle ve kaygıların ayrılması ve bağımlılık aşılama gibi diğer süreçlerle iç içe geçtiğini görebilirsiniz . Bir süre TDD yaptıktan sonra, izole ve tekrarlanabilir birim testleri oluşturmak için bunları bir dereceye kadar takip etmeniz gerektiğinden, bu ilkelerin doğal olarak pratikte geldiğini görebilirsiniz .


7
+1 Çok güzel cevap. Görünüşe göre bugün her (acemi) programcı kendi tasarım modellerini biliyor ya da en azından var olduklarını biliyor. Ancak pek çok kişi, kodlarının karmaşıklığını yönetmek için Tek Sorumluluk gibi kesinlikle temel ilkelerden bazılarını, uygulamayı bırakın, hiç duymadı.
eljenso

21

Design Patterns yazarlarının kendilerinin en çok endişelendiği şey "Ziyaretçi" modeliydi.

Bu "gerekli bir kötülüktür" - ancak genellikle aşırı kullanılır ve buna duyulan ihtiyaç genellikle tasarımınızda daha temel bir kusuru ortaya çıkarır.

"Ziyaretçi" kalıbı için alternatif bir ad "Çoklu gönderim" dir, çünkü Ziyaretçi kalıbı, iki türüne göre kullanılacak kodu seçmek için tek tip bir gönderim OO dili kullanmak istediğinizde elde ettiğiniz şeydir. (veya daha fazla) farklı nesneler.

Klasik örnek, iki şekil arasında kesişme noktasına sahip olmanızdır, ancak genellikle gözden kaçırılan daha da basit bir durum vardır: iki heterojen nesnenin eşitliğini karşılaştırmak.

Her neyse, genellikle böyle bir şeyle sonuçlanırsınız:

interface IShape
{
    double intersectWith(Triangle t);
    double intersectWith(Rectangle r);
    double intersectWith(Circle c);
}

Bununla ilgili sorun, tüm "IShape" uygulamalarınızı bir araya getirmiş olmanızdır. Hiyerarşiye yeni bir şekil eklemek istediğinizde, diğer tüm "Shape" uygulamalarını da değiştirmeniz gerekeceğini ima ettiniz.

Bazen bu doğru minimal tasarımdır - ama iyice düşünün. Tasarımınız gerçekten iki türde göndermeniz gerektiğini şart koşuyor mu? Çoklu yöntemlerin her bir kombinasyon patlamasını yazmaya istekli misiniz?

Genellikle, başka bir kavramı tanıtarak aslında yazmak zorunda kalacağınız kombinasyonların sayısını azaltabilirsiniz:

interface IShape
{
    Area getArea();
}

class Area
{
    public double intersectWith(Area otherArea);
    ...
}

Elbette buna bağlıdır - bazen tüm bu farklı durumların üstesinden gelmek için gerçekten kod yazmanız gerekir - ancak dalmaya başlamadan ve Ziyaretçi'yi kullanmadan önce durup düşünmeye değer. Daha sonra sizi çok fazla acıdan kurtarabilir.


2
Ziyaretçilerden bahseden Bob Amca bunu "her zaman" kullanıyor butunclebob.com/ArticleS.UncleBob.IuseVisitor
Spoike

3
@Paul Hollingsworth Design Patterns yazarlarının endişelendiğini (ve neden endişelendiklerini) söylediği yere referans verebilir misiniz?
m3th0dman

16

Singletons - singleton X kullanan bir sınıfın ona bağımlılığı görülmesi ve test için izole edilmesi zor.

Çok sık kullanılırlar çünkü uygun ve anlaşılması kolaydır, ancak testi gerçekten karmaşık hale getirebilirler.

Bakın Singletons Patolojik Yalancılardır .


1
Ayrıca, bir Mock nesnesini enjekte etmek için size tek bir nokta verebilecekleri için testi basitçe doğrulayabilirler. Her şey doğru dengeyi sağlamaya bağlıdır.
Martin Brown

1
@Martin: Tabii eğer test için bir singeltonu değiştirmek mümkünse (standart singelton uygulamasını kullanmıyorsanız), ancak bu yapıcıda test uygulamasını geçmekten nasıl daha kolay?
orip

14

Şablon Yöntemi modelinin genellikle çok tehlikeli bir model olduğuna inanıyorum.

  • Çoğu zaman "yanlış nedenlerle" miras hiyerarşinizi kullanır.
  • Temel sınıflar, her türlü ilişkisiz kodla kirlenme eğilimindedir.
  • Sizi, genellikle geliştirme sürecinin oldukça erken safhalarında tasarımı kilitlemeye zorlar. (Çoğu durumda erken kilitlenme)
  • Bunu daha sonraki bir aşamada değiştirmek daha da zorlaşır.

2
Şablon Yöntemini her kullandığınızda muhtemelen Strateji kullanmakta daha iyi olduğunuzu ekleyeceğim. TemplateMethod ile ilgili sorun, temel sınıf ile türetilmiş sınıf arasında genellikle aşırı bağlanmış olan yeniden girişin olmasıdır.
Paul Hollingsworth

5
@Paul: Şablon yöntemi doğru kullanıldığında harikadır, yani değişen parçaların, olmayan parçalar hakkında çok şey bilmesi gerektiğinde. Benim fikrim, stratejinin temel kod yalnızca özel kodu çağırdığında kullanılması gerektiğidir ve özel kodun doğası gereği temel kod hakkında bilgi sahibi olması gerektiğinde şablon yöntemi kullanılmalıdır.
dsimcha

evet dsimcha, katılıyorum ... sınıf tasarımcısı bunun farkında olduğu sürece.
Paul Hollingsworth

9

Tasarım Modellerinden (DP) kaçınmanız gerektiğini düşünmüyorum ve mimarinizi planlarken kendinizi DP'leri kullanmaya zorlamanız gerektiğini düşünmüyorum. DP'leri yalnızca planlamamızdan doğal çıktıklarında kullanmalıyız.

Baştan belirli bir DP'yi kullanmak istediğimizi tanımlarsak, gelecekteki tasarım kararlarımızın çoğu, seçtiğimiz DP'nin ihtiyaçlarımıza uygun olduğuna dair hiçbir garanti olmaksızın bu seçimden etkilenecektir.

Ayrıca yapmamamız gereken bir şey de bir DP'yi değişmez bir varlık olarak ele almaktır, modeli ihtiyaçlarımıza göre uyarlamalıyız.

Yani özetlemek gerekirse, DP'lerden kaçınmamız gerektiğini düşünmüyorum, mimarimizde zaten şekillenirken onları kucaklamalıyız.


7

Bence Active Record, iş mantığını kalıcı kodla karıştırmayı teşvik eden aşırı kullanılan bir model. Depolama uygulamasını model katmanından gizlemek için çok iyi bir iş yapmaz ve modelleri bir veritabanına bağlar. Tablo Veri Ağ Geçidi, Satır Veri Geçidi ve Veri Eşleştiricisi gibi, genellikle daha iyi bir çözüm sunan ve kesinlikle depolamaya daha iyi bir soyutlama sağlamaya yardımcı olan birçok alternatif (PoEAA'da açıklanmıştır) vardır. Ayrıca, modelinizin bir veritabanında saklanmasına gerek yoktur ; XML olarak depolamaya veya web hizmetlerini kullanarak bunlara erişmeye ne dersiniz? Modellerinizin depolama mekanizmasını değiştirmek ne kadar kolay olurdu?

Bununla birlikte, Aktif Kayıt her zaman kötü değildir ve diğer seçeneklerin gereğinden fazla olacağı daha basit uygulamalar için mükemmeldir.


1
Bir şekilde doğru, ancak bir dereceye kadar uygulamaya bağlı.
Mike Woodhouse

6

Çok basit ... Sizin için net olmayan veya kendinizi rahat hissetmediğiniz Tasarım Modellerinden kaçının .

Bazılarını adlandırmak için ...

bazı pratik olmayan kalıplar vardır , örneğin:

  • Interpreter
  • Flyweight

ayrıca anlaşılması daha zor , örneğin:

  • Abstract Factory - Oluşturulan nesnelerin ailelerini içeren tam soyut fabrika deseni, göründüğü kadar kolay değil
  • Bridge - Soyutlama ve uygulama alt ağaçlara bölünürse çok soyut olabilir, ancak bazı durumlarda çok kullanılabilir bir modeldir
  • Visitor - Çift gönderim mekanizması anlayışı gerçekten bir zorunluluktur

ve son derece basit görünen , ancak ilkeleri veya uygulamaları ile ilgili çeşitli nedenlerden dolayı çok net bir seçim olmayan bazı kalıplar vardır :

  • Singleton - gerçekten tamamen kötü bir model değil, sadece ÇOK fazla kullanılmış (genellikle orada, uygun olmadığı yerlerde)
  • Observer - harika desen ... sadece kodu okumayı ve hata ayıklamayı çok daha zor hale getirir
  • Prototype - ticaret derleyicisi dinamizmi denetler (iyi veya kötü olabilir ... bağlıdır)
  • Chain of responsibility - çok sık olarak zorla / yapay olarak tasarıma itiliyor

"Pratik olmayanlar" için, kullanmadan önce gerçekten düşünmek gerekir, çünkü genellikle bir yerlerde daha zarif bir çözüm vardır.

"Kavranması daha zor" olanlar için ... uygun yerlerde kullanıldıklarında ve iyi uygulandıklarında gerçekten çok yardımcı oluyorlar ... ama yanlış kullanıldığında kabuslar.

Şimdi, sırada ne var ...


Bir kaynağı, genellikle bir resmi birden fazla kullandığınızda, uçuş ağırlığı modeli her zaman bir zorunluluktur. Bu bir kalıp değil, bir çözüm.
Cengiz Kandemir

5

Umarım bunun için fazla dayak yemeyeceğim. Christer Ericsson , gerçek zamanlı çarpışma tespit blogunda tasarım kalıpları konusunda iki makale ( bir , iki ) yazdı . Ses tonu oldukça sert ve belki biraz kışkırtıcı, ama adam işini biliyor, bu yüzden bunu bir delinin çılgınlığı olarak görmezden gelmem.


İlginç okumalar. Bağlantılar için teşekkürler!
Bombe

3
Moronlar kötü kod üretir. Kalıplara sahip moronlar, hiç kalıp görmemiş moronlardan daha mı kötü kodlar üretir? Ben yaptıklarını sanmıyorum. Akıllı insanlar için kalıplar, fikir alışverişini kolaylaştıran iyi bilinen bir kelime hazinesi sağlar. Çözüm: Kalıpları öğrenin ve yalnızca akıllı programcılarla ilgilenin.
Martin Brown

Gerçek bir moronun daha kötü kod üretmesinin gerçekten mümkün olduğunu sanmıyorum - hangi aracı kullanırlarsa kullansınlar
1800 BİLGİ

1
Sanırım onun üniversite sınavıyla ilgili örneği, yalnızca sorunlu alanlarını küçümseyen ve tek bir hafta sonu boyunca birkaç saatten fazla çalışma istemeyen kişilerin, sorunları çözmeye çalışırken yanlış cevaplar üreteceğini kanıtlıyor.
scriptocalypse

5

Bazıları servis bulucunun bir anti model olduğunu söylüyor .


Ayrıca bazen servis bulucunun gerekli olduğunu unutmamak önemlidir. Örneğin, nesnenin somutlaştırılması üzerinde uygun kontrole sahip olmadığınızda (örneğin, C # 'da sabit olmayan parametrelere sahip öznitelikler). Ancak ktor enjeksiyonlu servis bulucuyu kullanmak da mümkündür.
Sinaesthetic

2

Gözlemci modelinin cevaplaması gereken çok şey olduğuna inanıyorum, çok genel durumlarda işe yarıyor, ancak sistemler daha karmaşık hale geldikçe bir kabusa dönüşüyor, OnBefore (), OnAfter () bildirimlerine ihtiyaç duyuyor ve tekrarlanmayı önlemek için sıklıkla eşzamansız görevler yayınlıyor. eğlence. Çok daha iyi bir çözüm, hesaplamalar sırasında tüm nesne erişimlerini (okuma engelleriyle) ölçen ve otomatik olarak bir bağımlılık grafiğinde bir kenar oluşturan bir otomatik bağımlılık analizi sistemi geliştirmektir.


4
Cevabınızdaki her şeyi "A" kelimesine kadar anladım
1800 BİLGİ

Bahsettiğiniz bu otomatik bağımlılık analizini genişletmeniz veya ona bağlanmanız gerekebilir. Ayrıca .NET'te gözlemci kalıbı yerine temsilciler / olaylar kullanılır.
Spoike

3
@Spoike: delegeler / olaylar, gözlemci modelinin bir uygulamasıdır
orip

1
Observer'a karşı kişisel kinim, çöp toplanan dillerde bellek sızıntıları yaratabilmesidir. Bir nesneyi bitirdiğinizde, nesnenin temizlenmeyeceğini hatırlamanız gerekir.
Martin Brown

@orip: evet, bu yüzden delegeleri / etkinlikleri kullanıyorsunuz. ;)
Spoike

2

Spoike'ın gönderisinin bir tamamlayıcısı olan Refactoring to Patterns iyi bir okuma.


Aslında kitabın internetteki kataloğuna bağlandım. :)
Spoike

Oh! Gezinme zahmetine girmedim. Aslında soruyu bitirdikten hemen sonra bu kitap aklıma geldi ve sonra cevabını gördüm. Kendimi yayınlamaya engel olamadım. :)
Adeel Ansari

0

Yineleyici, kaçınılması gereken veya en azından alternatiflerden hiçbiri mevcut olmadığında kullanmak için bir başka GoF modelidir.

Alternatifler:

  1. her döngü için. Bu yapı çoğu ana dilde mevcuttur ve çoğu durumda yinelemelerden kaçınmak için kullanılabilir.

  2. seçiciler à la LINQ veya jQuery. Konteynerdeki tüm nesneler işlenmemesi gerektiğinden, her biri için uygun olmadığında kullanılmalıdır. Yineleyicilerden farklı olarak, seçiciler, hangi tür nesnelerin işleneceğini tek bir yerde göstermeye izin verir.


Seçicilerle aynı fikirdeyim. Foreach bir yineleyicidir, çoğu OO dili bir foreach'e izin vermek için uyguladığınız yinelenebilir bir arabirim sağlar.
Neil Aitken

Bazı dillerde her yapı için yineleyiciler yoluyla uygulanabilir, ancak bunun kavramı aslında daha üst düzey ve seçicilere daha yakındır. Her geliştirici için kullanılırken, kapsayıcıdaki tüm öğelerin işlenmesi gerektiğini açıkça bildirir.
Volodymyr Frolov

Yineleyiciler harika bir modeldir. Anti-pattern, bir yineleyici olmadan IEnumerable / IEnumerator'ü uyguluyor olacaktır . yieldLINQ'nun yineleyici aracılığıyla mümkün olduğuna inanıyorum . Eric White'ın C # 3.0'da bu konuda harika bir tartışması var: blogs.msdn.com/b/ericwhite/archive/2006/10/04/… . Ayrıca, Jeremy Likness'in yineleyicilerle birlikte eşgüdümler hakkındaki tartışmasına bakın: wintellect.com/CS/blogs/jlikness/archive/2010/03/23/… .

@Ryan Riley, yineleyiciler düşük seviyeli nesnelerdir ve bu nedenle yüksek seviye tasarım ve kodda kaçınılmalıdır. Yineleyicilerin ve farklı türdeki seçicilerin uygulanmasının ayrıntıları burada önemli değil. Seçiciler, Yineleyicilerden farklı olarak, programcının neyi işlemek istediklerini açıkça ifade etmesine ve böylece üst düzeyde olmalarına izin verir.
Volodymyr Frolov

Fwiw, alternatif, LINQ benzeri F # sözdizimi `List.map (eğlence x -> x.Value) xs` dir, bu da listenin kavranması kadar uzun sürer.
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.