2016-02-20 10 views
5

Java'da şablon kalıbı uyguluyorum. Bize aşağıdaki kod parçasını varsayalım:Şablon deseninde kancaların korunması

public abstract class A { 

    public final void work() { 
    doPrepare(); 
    doWork(); 
    } 

    protected abstract void doPrepare(); 

    protected abstract void doWork(); 
} 

public final class B extends A { 

    @Override 
    protected abstract void doPrepare() { /* do stuff here */ } 

    @Override 
    protected abstract void doWork() { /* do stuff here */ } 
} 

public final class Main { 

    public static void main(String[] args) { 
    B b = new B(); 
    b.work(); 
    } 
} 

Bunun bir sorun düşünün bir kazayla hep b.work() aramak yerine b.doWork() çağırmak kolayca mümkün olduğunu. Mümkünse kancaları gizlemek için en zarif çözüm hangisidir?

+0

"kolayca yanlışlıkla b.doWork çağırmak yapabiliyor()". Gerçekten mi? Korunuyor, bu yüzden sadece A ve B örnekleri arayabilir. –

+0

Ne yazık ki, korumalı olduğundan, paketteki tüm sınıflar bunu arayabilir. Paketleri bölmek bir yoldur, ancak 1 veya 2 sınıf paketlerden kaçınmayı tercih ederim. –

+0

Hayır! Varsayılan erişim değiştiricisini tanımlıyorsunuz (yani, hiçbiri). Korumalı "sadece bana veya beni genişleten sınıflar bunu kullanabilir" anlamına gelir –

cevap

2

kolay ve daha belirgin şey, başka bir paketten sizin A -> B hiyerarşisi kullanmak olacaktır.

Herhangi bir nedenden dolayı bunu yapamazsanız, bir arabirim kullanabilir ve B sınıfının aslında A'dan gelen sınıfı sarmasını sağlayabilirsiniz. Böyle bir şey:

public interface Worker { 

    void work(); 
} 

public abstract class A implements Worker { 

    @Override 
    public final void work() { 
     doPrepare(); 
     doWork(); 
    } 

    protected abstract void doPrepare(); 

    protected abstract void doWork(); 
} 

public static class B implements Worker { // does not extend A, but implements Worker 

    private final A wrapped = new A() { // anonymous inner class, so it extends A 

     @Override 
     protected void doPrepare() { 
      System.out.println("doPrepare"); 
     } 

     @Override 
     protected void doWork() { 
      System.out.println("doWork"); 
     } 
    }; 

    @Override 
    public void work() { // delegate implementation to descendant from A 
     this.wrapped.work(); 
    } 
} 

Bu şekilde, korumalı yöntemler B bir özel nihai üyesidir A uzanan Anonim iç sınıf, gizli kalır. Anahtar, B'un A'u genişletmediği, ancak anonim iç sınıfına delege ederek Worker arabiriminin work() yöntemini uyguladığıdır.Bu nedenle, B 'in work() yönteminin bile aynı pakete ait bir sınıftan çağrılması durumunda, korunan yöntemler görünmeyecektir.

B b = new B(); 
b.work(); // this method is accessible via the Work interface 

Çıktı:

doPrepare 
doWork 
+0

Bu yaklaşımı izlemek için kodumu denedim ve ayarladık ve bir çekicilik gibi çalışıyor, teşekkürler. Jeneriklerle de çalışır. Bulduğum tek olumsuz yanı, soyut A sınıfındaki her yöntem için bir sarıcı sağlamanız gerektiğidir; Kalıtım kullandığınızda kaçınabileceğiniz bir şey. Ama bu kesinlikle kabul edilebilir bir ticaret. –

3

Gerçekten şablon şablonunu kullanıyorsunuz. Bu can yansıma ile bu korumaların geçmiş yollarını zorbalık belirlenen hacker

    Access Levels 
Modifier | Class | Package | Subclass | World | 
-------------------------------------------------- 
public  | Y  | Y  | Y  | Y  | 
protected | Y  | Y  | Y  | N  | 
no modifier | Y  | Y  | N  | N  | 
private  | Y  | N  | N  | N  | 

Short:

1) access modifiers anlayın: Eğer dışındaki kullanıcılardan "gizlemeye" ayrıntıları yapabileceği iki şey vardır Sıradan kodlayıcıları yanlışlıkla yollarından uzak tutulan yöntemleri çağırmaktan korur. Kodlayıcılar bunu takdir edecektir. Kimse karmaşık bir API kullanmak istemiyor.

Şu anda b.doWork()protected'dir. senin main(), (bu değil) sınıfında A içinde alt sınıf B (bu değil) veya aynı paketi (net değil de, sen lar (paketine ilişkin herhangi bir ipucu gönderilmeyen) ise Yani sadece senin main() içinde görülmeye devam ediyor kodunuzda bulunur).

Bu, b.doWork()'u görmekten korunmaya çalıştığınız soruyu akla getiriyor? Oluşturduğunuz paket sadece bir şey geliştiriyorsanız, o kadar da kötü olmayabilir. Pek çok kişi bu pakette birçok başka sınıfla uğraşmaktan hoşlanıyorsa, muhtemelen A ve B sınıflarına aşina olacaklardır. Herkesin görebileceği yerde takılmak istemediğiniz durumlar budur. Burada sadece sınıf ve alt sınıfa erişime izin vermek güzel olurdu. Ancak paket erişimine de izin vermeden alt sınıf erişimine izin veren bir düzenleyici yoktur. Alt sınıfta olduğunuz sürece protected yapabileceğiniz en iyi şeydir. Bu düşünceyle, çok sayıda sınıf içeren paketler oluşturmaya gitmeyin. Paketleri sıkı ve odaklı tutun. Bu şekilde çoğu insan, şeylerin güzel ve basit görüneceği paketin dışında çalışacaktır. Özetle main()'un b.doWork() numarasını main() numaralı telefonu başka bir pakete koymasını engellemek istiyorsanız, özetle bakın.

package some.other.package 

public final class Main { 
... 
} 

2) tavsiye bu sonraki parçanın değeri kodunuzun genişletilmişse sadece gelecek sorunları çözmede çünkü mevcut kodla anlamak zor olacaktır.

public static void main(String[] args) { 
    A someALikeThingy = new B(); // <-- note use of type A 
    someALikeThingy.work();  //The name is silly but shows we've forgotten about B 
           //and know it isn't really just an A :) 
} 

: Ama gerçekten yakından ana gibi ise daha iyi olurdu demek istediği Kodunuzda b.doWork()

What does "program to interfaces, not implementations" mean?

gibi görerek şeylerden insanların korunması fikrine bağlıdır Niye ya? A türü B türüne göre ne işe yarar? İyi A, arayüzü'a daha yakındır. Not, burada sadece interface anahtar kelimesini kullandığınızda elde ettiğiniz şeyleri kastetmiyorum.Ben B tür A türünün somut bir uygulama olduğunu ve B karşı kesinlikle kod yazmak zorunda kalmazsam gerçekten tercih ederim çünkü kim AB şeyler var garip bilir kim bilir. Burada anlamanız zor çünkü ana kod, kısa & tatlı. Ayrıca,ve B arasındaki arabirimler herkesten farklı değildir. Her ikisi de sadece bir kamu yöntemi work() var. main()'un uzun ve kıvrımlı olduğunu düşünün, Imagine B, her türlü A yönteminin kapalı kalmasını sağladı. Şimdi başa çıkmak istediğiniz bir sınıf C oluşturmanız gerektiğini hayal edin. Ana, B no.lu ana hat beslendiğinde, B'un sadece A gibi basit bir şey olduğunu bildiğine inanmak rahatlatıcı olmaz mıydı? new'dan sonraki bölüm haricinde, bu doğru olabilir ve main()'a bir şey yapmanıza rağmen bu new'u güncelleştirmenizden tasarruf etmenizi sağlar. Gerçekten de, bu, güçlü bir şekilde yazılan bir dilin kullanılmasının bazen güzel olabileceğidir değil mi?

<rant>
bile B() ile new sonra o bölümü güncellenmesi düşünüyorum Eğer yalnız değilsin çok fazla çalışmadır. Bu küçük bir saplantı, bize, herhangi bir kurucunun yapmanıza izin verebileceği basit bir enjeksiyondan çok, nesne yapısını otomatikleştiren bir dizi çerçeveyi yaratan dependency injection movement'u verdi.
</rant >

Güncelleme: Sana küçük dar paketleri oluşturmak için isteksiz yorumlarınızı görmek

. Bu durumda, bir kompozisyon tabanlı strateji modeli lehine bir kalıtım tabanlı şablon kalıbı terk düşünün. Hala polimorfizm oluyorsun. Sadece bedavaya olmaz. Biraz daha kod yazımı ve dolaylı. Fakat çalışma zamanında bile durumu değiştirirsiniz.

Bu, sezgisel karşıt görünebilir çünkü şimdi doWork() herkese açık olmalıdır. Bu ne biçim bir koruma? Peki şimdi B'u tamamen geride veya hatta AHandler'un içinde saklayabilirsiniz. Bu, şablon kodunuzda kullanılanı tutan ancak sadece work()'u görüntüleyen insan dostu bir cephe sınıfıdır. A sadece interface olabilir, AHandler hala bir B olduğunu bilmiyor. Bu yaklaşım bağımlılık enjeksiyon kalabalığı ile popülerdir, ancak kesinlikle evrensel çekiciliğe sahip değildir. Bazıları her seferinde körü körüne çeker, bu da birçok şeyi rahatsız eder. Burada bu konuda daha fazla bilgi edinebilirsiniz: Yapabileceğin Prefer composition over inheritance?

+0

Kapsamlı bir cevap için teşekkür ederiz. Tüm fikirleri dener ve çözerim: 1) Erişim değiştirici ve paket yapısı, soruyu göndermeden önce uzun zamandır düşündüğüm ilk şeydi. Paketim zaten küçük ve daha fazla bölüme ayırdığımda, önceki yorumlardan birinde belirttiğim gibi 1 veya 2 sınıf paketler vereceğim. Bu benim mevcut durumumda bir çözüm değil, gelecekte başka biri için de olabilir. –

+0

2) Arabirim yaklaşımı için, iki downsides vardır. İlk olarak, jenerikler ile iyi çalışmaz (bir örnek verebilirim). İkincisi, sizin yazmanızı engeller: 'Sonuç r = yeni B (args) .work(); ', biraz zorlanmaya daha az dikkat ettim,' a a = yeni B (args); Sonuç r = a.work(); '. Bu yine de denediğim ve hala daha iyi bir şey aramaya devam ettiğim bir şey. Bazıları için kabul edilebilir olsa da. –

+0

3) Kompozisyon gerçekten cevaptır. Sınıflarımın semantikleri nedeniyle mirasa doğru atladım ve bu seçeneği düşünmeyi unuttum. Bay Schaffner'ın cevabını bir örnek sunduğundan doğru olarak işaretliyorum ve kancalara sınırlı (korumalı) erişimi koruyor; ama başkalarının gönderdiğiniz bağlantılara bir göz atmasını öneriyorum. –