簡體   English   中英

將裝飾器設計模式用於類的層次結構

[英]Using the decorator design pattern for a hierarchy of classes

查看以下(簡化)類的層次結構:

>     Email (base class) 
>     SimpleEmail extends Email
>     HtmlEmail extends Email

我需要修飾Email.send()以添加限制功能。 我需要實例化SimpleEmail,HtmlEmail或其他類似的Email子類。

這個模式到底應該是什么樣的? 我的猜測(需要糾正)如下:

class abstract EmailDecorator
   -> Define a constructor: EmailDecorator(Email component)
   -> Implements all methods of Email and passes values through to component
   -> Adds functionality to send() method
class SimpleEmailDecorator extends EmailDecorator
   -> Define a constructor: SimpleEmailDecorator(SimpleEmail component)
   -> Implement all methods of SimpleEmail and pass through to component
class HtmlEmailDirector extends EmaiDecorator
   -> Same as SimpleEmailDecorator

我的大腦並沒有圍繞我如何正確處理我需要“增強”的基類的重要現有子類。 大多數示例將其簡化為繼承問題變得混亂的程度。

這是裝飾器模式的簡化示例。 類層次結構被重構為static內部類,因此整個示例包含在一個編譯單元中( 如ideone.com上所示 ):

public class AnimalDecorator {

    static abstract class Animal {
        public abstract String makeNoise();
    }   
    static class Dog extends Animal {
        @Override public String makeNoise() { return "woof"; }
    }
    static class Cat extends Animal {
        @Override public String makeNoise() { return "meow"; }
    }

    static class Normal extends Animal {
        protected final Animal delegate;
        Normal(Animal delegate)     { this.delegate = delegate; }
        @Override public String makeNoise() {
            return delegate.makeNoise();
        }
    }
    static class Loud extends Normal {
        Loud(Animal delegate)       { super(delegate); }
        @Override public String makeNoise() {
            return String.format("%S!!!", delegate.makeNoise());
        }       
    }
    static class Stuttering extends Normal {
        Stuttering(Animal delegate) { super(delegate); }
        @Override public String makeNoise() {
            return delegate.makeNoise().replaceFirst(".", "$0-$0-$0-$0");
        }
    }

    public static void keepPokingIt(Animal a) {
        // let's skip the details for now...
        System.out.println(a.makeNoise());
    }
    public static void main(String[] args) {
        keepPokingIt(new Cat());
        // meow

        keepPokingIt(new Stuttering(new Dog()));
        // w-w-w-woof

        keepPokingIt(new Loud(new Cat()));
        // MEOW!!!

        keepPokingIt(new Loud(new Stuttering(new Dog())));
        // W-W-W-WOOF!!!        
    }
}

所以這里我們有一個簡單的Animal層次結構,包含DogCat子類。 我們還有一個Normal裝飾器 - 也是一個Animal - 它只是將所有方法委托給另一個Animal 也就是說,它並沒有真正做任何有效的裝飾,但它已准備好進行子類化,以便可以添加實際的裝飾。

我們這里只有一個方法, makeNoise() 然后我們有兩種實際的裝飾, LoudStuttering (考慮Animal有很多方法的情況;那么Normal會是最有價值的)。

然后我們有一個keepPokingIt(Animal)方法,該方法接受任何 Animal ,並且在makeNoise()之前makeNoise()它做任何 makeNoise()事情。 在我們的main功能中,我們隨后keepPokingIt各種各樣的動物,裝飾着各種個性特征。 請注意,我們甚至可以將一個裝飾堆疊在另一個上面。

確切的實現細節可能會有所不同,但這個簡化的示例幾乎捕獲了裝飾器模式的本質。


另一個例子:來自Guava的ForwardingCollection層次結構

在上面的例子中, keepPokingIt只關心它是一個Animal 有時您可能只想戳Cat而不是Dog ,或者以其他方式區分這兩種類型。 在這些場景中,您將提供NormalCatNormalDog等。

如果你很好地設計你的類型層次結構,這應該不是問題。 請記住,您不必為每個實現class編寫裝飾器,而是為您關心的每種類型編寫裝飾器。 理想情況下,每種類型甚至應該是一個interface而不是一個具體的class

例如,考慮Java Collections Framework類型層次結構。 我們有:

Guava方便地在這種類型層次結構上促進裝飾器模式實現:

請注意,沒有ForwardingHashMap<K,V>ForwardingTreeSet<E> 反正可能沒有必要。

也可以看看

  • Effective Java 2nd Edition,Item 18:首選接口到抽象類

相關問題

如果子類有其他方法,並且您希望通過裝飾器訪問這些方法,則必須為每個子類編寫單獨的裝飾器。 對於您的特殊問題,我建議另一種解決方案。 Email -class中刪除send -method並創建一個負責發送電子郵件的新類Mailer

class Mailer {
    public void send(Email mail) {
        // get required info from mail
        String recipents = mail.getRecipents()
        String subject = mail.getSubject()
        String content = mail.getContent()
        // do stuff with all the information
    }
}

這樣,您可以使用不同的方式發送電子郵件與所有類型的電子郵件。

您需要SimpleEmail或HtmlEmail的特殊方法嗎?

如果沒有,那么非抽象的EmailDecorator就足夠了。 如果Email,SimpleEmail或HtmlEmail實現了某些接口,那么你也應該真正實現它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM