2011-05-13 12 views
9

"Clojure Joy" adlı kitapta,ifade sorunu için bir çözüm olarak sunulur - "varolan bir varolan soyut yöntemler kümesini uygulama isteği Ya da tanımlayan kodu değiştirmek zorunda kalmadan beton sınıfı. " aşağıdaki gibi verilenİfade sorununa çözüm olarak Clojure defprotocol

örnektir:

(defprotocol Concatenatable 
    (cat [this other])) 

(extend-type String 
    Concatenatable 
    (cat [this other] 
    (.concat this other))) 

(cat "House" " of Leaves") 
;=> "House of Leaves" 

(extend-type java.util.List 
    Concatenatable 
    (cat [this other] 
    (concat this other))) 

(cat [1 2 3] [4 5 6]) 
;=> (1 2 3 4 5 6) 

Java gibi bir dilde mümkün olmadığını ileri sürülmektedir, ama nasıl aşağıdaki farklıdır?

public class Util { 
    public static String cat(final String first, 
          final String second) { 
    return first.concat(second); 
    } 

    public static <T> List<T> cat(final List<T> first, 
           final List<T> second) { 
    final List<T> list = new List<T>(first); 
    list.addAll(second); 
    return list; 
    } 
} 

Sonuçta, iki benzer şekilde kullanılır:

(cat "House" " of Leaves") 
Util.cat("House", " of Leaves"); 

Clojure fonksiyonu catString ve List sınıfları yöntemolmayan bir olmakla birlikte, bağımsız bir fonksiyonu ziyade bu ya String veya List argümanları kabul etmek aşırı yüklendi.

Gerçekten Clojure gibi, bu yapı için üstünlük iddiaları anlamıyorum rağmen.

+2

Her ikisi de benzer şekilde çalışır. Fark (amalloy tarafından vurgulanan) Clojure durumda katılımcıların seti Concatenatable' 'kullanıcıları tarafından tanımlanır oysa Java durumda katılımcıların seti, Util'' yazarı tarafından tanımlanır olmasıdır. – fogus

cevap

21

tamam. Sen tantanalı bu cat Java kütüphanesi bırakın ve herkes indirir. O ben concatenable nesneler üzerinde işlem kitaplığınızın bit göndermek böylece kendi TVCommercial tipi concatenable olmak yapmak istiyorum harika.

Ama yapamam çünkü TVCommercial için aşırı yüklenme olan Util.cat(obj1, obj2) numaralı telefonu arayın. Kodlarımı işlemek için kodunuzu genişletemiyorum çünkü kodunuzun sahibi ben değilim.

bu sorunu çözmek için bir arayüz olarak Concatenable tanımlayabiliriz:

interface Concatenable { 
    Concatenable cat(Concatenable other); 
} 

Ama şimdi cat s kolları Bilmiyorum Concatenable ve hem de bir sınıf ... Bir AnimalHandler, yazamıyor . Clojure'ın protokolleri her iki problemi de dağıtım işlevlerini ve uygulamalarını merkezden uzaklaştırarak çözüyor: tek bir yerde değil, her yerde yaşıyorlar.Java'da, seçtiğiniz arası:

    tek anahtarı/kasanın içine tüm tip sevk koyarak
  • veya aşırı yöntemle

Clojure temelde yapar belli bir isme sahip bir yöntemi zorunlu bir arayüz tanımlama o isimalanlı isimleri kullanması nedeniyle USPMGA ama, cat iyi işlev adı olduğunu düşünüyorum diğer protokolleri ile çatışma tehlikesi yoktur.

+0

Sözünü ettiğiniz isim çarpışması sorunu, İfade Problemine gerçekten Almanca değildir. 'Concatenble' arayüzünün tanımlanması problemin bir parçasıdır, ancak ilgili tek bir işlem olduğu için kilitlenir: 'cat'. Yeni işlemlerin eklenmesi, tekrar tekrar 'Concatenable' açılmasını veya katılımcı türlerin uygulamak zorunda kalacağı ek arayüzleri tanımlamayı gerektirir. – seh

+0

@seh Sanırım takip etmiyorum. Birleştirilebilir hedeflerin desteklemesi gereken operasyon sayısını arttırmak istiyorsak, bu arayüzü yeniden tanımlayacağımız gibi, protokolü yeniden tanımlamamız gerekir. Bence Clojure buna izin veriyor ve sadece bir protokol uygulanmayan bir işlev çağırıyorsanız, yalnızca kontrol edilmeyen bir istisna atar, ancak bu problemin çözümünde değil, çözülüyor. – amalloy

+0

Evet, Clojure, bu işlevin uzantısı olmayan bir türdeki bir örneğe karşı bir protokol işlevi çağırırken 'IllegalArgumentException öğesini atar. Protokollerle yapılan sözleşme, belki de mektubun ruhundan daha fazlası olsa da, * bütün * protokolünün genişletildiği türler için tanımlanacağıdır. Aksi halde, bir protokolde fonksiyonları bir araya getirmenin amacı nedir? Bunu, CL'nin aynı paket üyeliği ile sadece zayıf bir şekilde bir araya getiren genel işlevleri ile karşılaştırınız, ancak bir dizi fonksiyonun birlikte tanımlanması gerektiğini ifade etmenin bir yolu yoktur. – seh

2

cat işlevinizi uygulamak istediğiniz yeni bir tür her seferinde gelirse, Util sınıfınızı yeniden açmanız ve yeni hedef türleri için yöntem aşırı yüklemeleri eklemeniz gerekir.

İfade Sorunu, bu tür gereksinimlerin önüne geçmeyi amaçlamaktadır, öyle ki, yeni tiplerin tanımlanmasıyla mevcut türler rahatsız edilmemekte ve mevcut operasyonlar bu operasyonlara katılmak isteyen yeni tipler tarafından bozulmamaktadır. Yayımlanmış protokole yeni bir işlev eklemek gibi, ilk golü yerine getirmemesi Burada gösterilen Clojure protokol örneği her türlü hangi protokolü zaten yeni yöntem için bir uygulama tanımlamak uzatılır için gerektirir.

+0

* Yayınlanmış bir protokole yeni bir fonksiyon ekleyerek her türlü protokol zaten yeni yöntem için bir uygulama tanımlamak uzatılır hangi gerektirir * -. Ben acaba - Sen denedin mi? (defprotocol Concatenatable (kedi [bu diğer]) (rev [bu])) kullanıcı => (cat: Eğer OP kodu ve ardından aşağıdakileri uygularsanız göreceksiniz ki bu doğru değildir gözlemlemek edeceğiz Yapraklar "Ev" "") "House of Leaves" yeniden tanımı gereklidir. – fogus

+0

Hayır, denemedim, ama yeni 'rev' işlevini bir dizgeye karşı çağırdığınızda ne olur? Bu noktada, 'String' sadece '* Concatenatable' protokolünü uygular mı? Belki de bir tür önceden tanımlanmış uzantıları * * tanımlanmış kalmıştır bu yüzden, soyut sınıflar ile temin ama şimdi eksik ve böyle bir türü ile yeni işlevini çağırarak bir verecektir olabilir gibi bir protokol arttırıcı varsayılan bir uygulama içeremez yazılı olmalıdır 'IllegalArgumentException'. – seh