Yineleyiciyi Tasarım Deseni Yapan Nedir?


9

Iterator'ı diğer benzer yapılara kıyasla özel kılan şeyin ne olduğunu merak ediyorum ve bu da Dörtlü Çeteyi bir tasarım deseni olarak listeledi.

Yineleyici polimorfizme (ortak bir arayüze sahip koleksiyonlar hiyerarşisi) ve endişelerin ayrılmasına (koleksiyonlar üzerinde yineleme, verilerin yapılandırılma biçiminden bağımsız olmalıdır) dayanmaktadır.

Ancak, koleksiyonlar hiyerarşisini, örneğin, güç nesneleri gibi bu nesneler üzerindeki bazı ilgili işlemleri temsil eden bir sınıfla, matematiksel nesnelerin (tamsayı, kayan nokta, karmaşık, matris vb.) Hiyerarşisi ve yineleyici ile değiştirirsek. Sınıf diyagramı aynı olurdu.

Muhtemelen aynı şekilde çalışan Yazar, Ressam, Kodlayıcı ve muhtemelen daha iyi olanlar gibi daha birçok benzer örnek bulabiliriz. Ancak bunların hiçbirinin Tasarım Deseni olarak adlandırıldığını hiç duymadım.

Peki, Iterator'ı özel yapan nedir?

Koleksiyondaki mevcut pozisyonu saklamak için değişebilir bir duruma ihtiyaç duyduğundan daha karmaşık bir durum mudur? Ama sonra, değişebilir durum genellikle arzu edilen bir şey olarak kabul edilmez.


Demek istediğim açıklığa kavuşmak için daha ayrıntılı bir örnek vereyim.

İşte tasarım sorunumuz:

Diyelim ki bir sınıflar hiyerarşisi ve bu sınıfların nesneleri üzerinde tanımlanmış bir işlemimiz var. Bu işlemin arabirimi her sınıf için aynıdır, ancak uygulamalar tamamen farklı olabilir. Ayrıca, işlemin farklı parametrelerle aynı nesneye birden çok kez uygulanmasının mantıklı olduğu varsayılmaktadır.

İşte tasarım problemimiz için mantıklı bir çözüm (pratikte yineleyici modelinin genelleştirilmesi):

Endişelerin ayrılması için, operasyonun uygulamaları orijinal sınıf hiyerarşisine (işlenen nesneler) fonksiyon olarak eklenmemelidir. İşlemi aynı işlenene birden çok kez uygulamak istediğimizden, yalnızca bir işlevle değil, işlenene referansı tutan bir nesne ile temsil edilmelidir. Bu nedenle işlenen nesnesi, işlemi temsil eden nesneyi döndüren bir işlev sağlamalıdır. Bu nesne, gerçek işlemi gerçekleştiren bir işlev sağlar.

Bir örnek:

MathObjectTüretilmiş sınıflarla bir temel sınıf veya arayüz (aptal isim, biliyorum, belki birinin daha iyi bir fikri vardır.) MyIntegerVe MyMatrix. Her biri MathObjectiçin Powerkare, küp vb. Hesaplamalara izin veren bir işlem tanımlanmalıdır. Böylece (Java ile) yazabiliriz:

MathObject i = new MyInteger(5);
Power powerOfFive = i.getPower();
MyInteger square = powerOfFive.calculate(2); // should return 25
MyInteger cube = powerOfFive.calculate(3); // should return 125

5
"Sınıf diyagramı aynı olurdu" - ne olmuş yani? Tasarım deseni bir sınıf diyagramı değildir. Tekrarlayan bir soruna bir çözüm sınıfı için yüksek düzeyde bir soyutlamadır.
Doc Brown

@DocBrown: Doğru, ancak matematiksel işlemler, bir dosyaya nesne yazma, grafik çıktısı veya yinelenen veriyi kodlama, tıpkı yineleme gibi değil mi?
Frank Puffer

Tasarım Deseni seçimi özneldir (yani "tasarımcıların" ya da tasarımları yargılayanların gözünde). Tasarım örüntülerinin isimlendirilmesi, alana agnostik olarak tasarlanmıştır (böylece, alana özgü olduğunu düşünmek için dikkatimizi dağıtmazız). Sadece benim düşüncem, atıf yapacak hiçbir referansım yok.
rwong

@FrankPuffer Bir dosyaya nesneler yazmak için ortak bir çözüm çizerseniz, çözümünüzü yazabilir ve bunu yapmak yararlı olursa Nesne Yazma Deseni olarak adlandırabilirsiniz.
Brandin

3
Bunu çok düşünüyorsun. Tasarım Deseni, yaygın bir bilgi işlem sorununa iyi bilinen bir çözümdür ve hepsi bu kadardır. Deseni, sağladığı faydaları tanıyabildiğinizde ve uygulayabildiğinizde kullanırsınız.
Robert Harvey

Yanıtlar:


9

GoF kitabındaki desenlerin çoğunun ortak noktaları şunlardır:

  • temel tasarım problemlerini nesne yönelimli araçlar kullanarak çözerler
  • insanlar genellikle bu tür sorunlarla, alan adı veya iş dünyasından keyfi programlarla karşılaşırlar.
  • genellikle daha katı hale getirerek kodu daha yeniden kullanılabilir hale getirmek için tariflerdir
  • bu sorunlara kanonik çözümler sunuyorlar

Bu kalıpların çözdüğü problemler o kadar basittir ki, birçok geliştirici bunları çoğunlukla eksik programlama dili özellikleri için geçici çözümler olarak anlar , ki IMHO geçerli bir bakış açısıdır (GoF kitabının 1995'ten geldiğini ve Java ve C ++ özellikleri bugün olduğu gibi).

Yineleyici model bu tanıma çok uygundur: herhangi bir alandan bağımsız olarak çok sık ortaya çıkan temel bir problemi çözer ve kendiniz yazdığınızda "endişelerin ayrılması" için iyi bir örnektir. Bildiğiniz gibi, doğrudan yineleyici desteği, günümüzde birçok dalgın programlama dilinde bulduğunuz bir şeydir.

Şimdi bunu seçtiğiniz problemlerle karşılaştırın:

  • bir dosyaya yazma - IMHO sadece "temel" yeterli değil. Bu çok spesifik bir problem. İyi, kanonik bir çözüm de yoktur - bir dosyaya nasıl yazılacağı konusunda çok farklı yaklaşımlar vardır ve net bir "en iyi uygulama" yoktur.
  • Ressam, Encoder: Aklınızda ne varsa, bu problemler benim için daha az basit görünüyor ve alandan bağımsız bile değil.
  • farklı tür nesneler için "güç" işlevine sahip olmak: ilk bakışta, bir desen oluşturmaya değer olabilir, ancak önerilen çözümünüz beni ikna etmiyor - daha çok güç işlevini benzer bir şeye sokmak gibi görünüyor yineleyici deseni. Mühendislik hesaplamaları ile birçok kod uyguladım, ancak güç işlevi nesnenize benzer bir yaklaşımın bana yardımcı olacağı bir durumu hatırlayamıyorum (ancak yineleyiciler günlük olarak uğraşmam gereken bir şey).

Dahası, güç işlevi örneğinizde strateji kalıbı veya komut kalıbı uygulaması olarak yorumlanamayacak hiçbir şey görmüyorum, bu da temel parçaların zaten GoF kitabında olduğu anlamına geliyor. Daha iyi bir çözüm, operatörün aşırı yüklenmesi veya genişletme yöntemlerini içerebilir, ancak bunlar dil özelliklerine tabidir ve "Gang" tarafından kullanılan "OO" nun tam olarak sağlayamadığı şey budur.


The problems solved by these patterns are so basic that many developers think their main purpose is to be workarounds for missing programming language features- İronik olan, yazılım geliştiricilerin rutin olarak 20 yaşında yazılım tasarım kalıplarını kullanmaları ve aynı zamanda son teknoloji kod yazdıklarına inanmalarıdır.
Robert Harvey

@RobertHarvey: Pek çok geliştiricinin bugün yineleyici kalıbı GoF tarafından önerilen "OO" şeklinde uygulayacağını sanmıyorum. Bunlar dilin sağladığı olanaklar sayesinde, tipik uygulamak ya da (kullanarak C #, örneğin standart lib var IEnumerableve yield). Ancak diğer GoF kalıpları için yazdıklarınız muhtemelen doğru olabilir.
Doc Brown

1
Alakalı workarounds for missing programming language features: blog.plover.com/prog/johnson.html
Monica jrw32982 destekleri

8

Dörtlü Çete, Christopher Alexander'ın desen tanımını alıntılar:

Her desen çevremizde tekrar tekrar ortaya çıkan bir sorunu açıklar ve daha sonra bu soruna çözümün çekirdeğini açıklar […]

Yineleyiciler tarafından çözülen sorun nedir?

Amaç: Birleştirilmiş nesnenin öğelerine, temel temsilini göstermeden sırayla erişmek için bir yol sağlayın.

...

Uygulanabilirlik: Yineleme desenini kullanma

  • birleşik nesnenin içeriğine, iç temsilini göstermeden erişmek
  • toplu nesnelerin birden fazla geçişini desteklemek için
  • farklı agrega yapılarının çaprazlanması için tekdüze bir arayüz sağlamak (yani polimorfik yinelemeyi desteklemek için).

Böylece, yineleyici deseninin tanım gereği koleksiyonlara etki alanına özgü olduğu söylenebilir. Ve bu gayet iyi. Yorumlayıcı kalıp gibi diğer kalıplar, alana özgü dillere özgüdür, fabrika kalıpları nesne oluşturma için alana özgüdür…. Tabii ki bu, “alana özgü” oldukça aptalca bir anlayış. Tekrarlanan bir sorun-çözüm çifti olduğu sürece, buna bir örüntü diyebiliriz.

Yineleyici modelin mevcut olması iyidir. Eğer kullanmazsan kötü şeyler olur. En sevdiğim anti-örnek Perl. Burada her koleksiyon (dizi veya karma), koleksiyonun bir parçası olarak yineleyici durumunu içerir. Bu neden kötü? Bir karma ile her bir döngüde kolayca yineleyebiliriz: her döngü:

while (my ($key, $value) = each %$hash) {
  say "$key => $value";
}

Peki ya döngü gövdesinde bir fonksiyon çağırırsak?

while (my ($key, $value) = each %$hash) {
  do_something_with($key, $value, $hash);
}

Bu işlev artık aşağıdakileri dışında istediği her şeyi yapabilir:

  • karma girişleri ekleyin veya silin, çünkü bunlar yineleme sırasını öngörülemez şekilde değiştirir (C ++ - speak, yineleyici geçersiz kılar).
  • bir kopya yapmadan aynı karma tabloyu yineleyin, çünkü bu aynı yineleme durumunu tüketir. Hata.

Aranan işlev yineleyiciyi kullanacaksa, döngümüzün davranışı tanımsız hale gelir. Bu bir problem. Ve yineleyici deseninin bir çözümü vardır: tüm yineleme durumunu yineleme başına oluşturulan ayrı bir nesneye koyun.

Evet, elbette yineleyici paterni diğer paternlerle ilgilidir. Örneğin, yineleyici nasıl somutlaştırılır? Java'da bir jenerik Iterable<T>ve Iterator<T>arayüzümüz var. Benzer bir beton ArrayList<T>, belirli bir tür yineleyici yaratırken, HashSet<T>tamamen farklı bir yineleyici türü sağlayabilir. Bu bana soyut fabrika desenini hatırlatıyor, burada Iterable<T>soyut fabrika ve Iteratorürün.

Bir polimorfik yineleyici de strateji modelinin bir örneği olarak yorumlanabilir. Örneğin, bir ağaç farklı türde yineleyiciler sunabilir (ön sipariş, sipariş, sipariş,…). Dışarıdan, bunların hepsi bir yineleyici arayüzünü paylaşacak ve bir dizi eleman verebilir. İstemci kodunun herhangi bir ağaç geçiş algoritmasına değil, yalnızca yineleyici arayüzüne bağlı olması gerekir.

Kalıplar birbirinden bağımsız olarak tek başına mevcut değildir. Bazı modeller aynı soruna farklı çözümlerdir ve bazı modeller aynı çözümü farklı bağlamlarda tanımlar. Bazı kalıplar bir diğerini ifade eder. Ayrıca, Tasarım Desenleri kitabının son sayfasını çevirdiğinizde desen alanı kapanmaz (ayrıca bkz. Önceki sorunuz , Dörtlü Çete “Desen Alanı” nı iyice araştırdı mı? ). Tasarım Kalıpları kitabında açıklanan kalıplar çok esnek ve geniştir, sonsuz varyasyona açıktır ve var olan tek kalıp kesinlikle değildir.

Listelediğiniz kavramlar (yazma, boyama, kodlama) desen değildir, çünkü problem-çözüm kombinasyonunu tanımlamazlar. “Veri yazmam gerekiyor” veya “Verileri kodlamam gerekiyor” gibi bir görev gerçekten bir tasarım sorunu değildir ve bir çözüm içermez; “Biliyorum, bir Yazar sınıfı yaratacağım” dan oluşan bir “çözüm” anlamsız. Ancak “Ekranda yarı oluşturulan grafiklerin boyanmasını istemiyorum” gibi bir sorunumuz varsa, bir model olabilir: “Biliyorum, çift tamponlu grafikler kullanacağım!”


Güzel cevap, teşekkürler. Son paragrafta yazdıklarınızın burada geçerli olduğuna hala tam olarak ikna olmadım. Ne demek istediğimi açıklamak için sorumu düzenledim.
Frank Puffer
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.