装饰器模式java_Java 8的装饰器模式
裝飾器模式j(luò)ava
在最近的一篇文章中,我描述了裝飾器模式如何挽救了我的一天。 我給出了一個小代碼段,其中包含創(chuàng)建裝飾器的最簡單方法,但承諾Java 8會有更好的方法。
這里是:
用Java 8裝飾
我將在文章的其余部分中說明如何到達那里。
我在GitHub上創(chuàng)建了一個小示例項目 ,我將從這里重復引用。 我只建議您檢查一下它,因為它提供了更多詳細信息。 它是公共領(lǐng)域 ,因此可以不受限制地使用該代碼。
為了繼續(xù)我的上一篇文章,它使用Swing的HyperlinkListener作為裝飾的基礎(chǔ)。 由于該接口不是通用接口,并且僅具有一個僅帶有一個參數(shù)的方法(對于lambda表達式而言非常好!),因此具有使接口保持簡單的其他優(yōu)點。
總覽
像其他帖子一樣,該帖子也沒有嘗試教授模式本身。 (不過,我找到了另一個很好的解釋 。)相反,它推薦了一種在Java 8中實現(xiàn)它的方法,以使其使用起來非常方便。 因此,該帖子嚴重依賴Java 8功能,尤其是默認方法和lambda表達式 。
這些圖只是草圖,并省略了許多細節(jié)。 更完整的是容易找到的 。
香草
在模式的通常實現(xiàn)中,存在一個接口(上面稱為Component ),該接口將通過“常規(guī)”類以及所有裝飾器以常規(guī)方式實現(xiàn)。
抽象裝飾器類
裝飾器通常從中間抽象基類( AbstractDecorator )繼承,從而簡化了實現(xiàn)。 它使用另一個組件作為構(gòu)造函數(shù)參數(shù),并通過將所有調(diào)用轉(zhuǎn)發(fā)給接口來實現(xiàn)接口本身。 因此,裝飾組件的行為不變。
現(xiàn)在由子類實際更改它。 他們通過有選擇地重寫那些他們想改變其行為的方法來做到這一點。 這通常包括對裝飾組件的調(diào)用。
裝飾器的創(chuàng)建
通常,不使用特殊技術(shù)來創(chuàng)建裝飾器。 只是簡單的構(gòu)造函數(shù)。 使用復雜的裝飾器,您甚至可以使用工廠。
我是靜態(tài)構(gòu)造方法的忠實擁護者,因此我使用它們并將構(gòu)造方法設(shè)為私有。 為了使這些方法的調(diào)用者不了解任何細節(jié),我將這些方法的返回類型聲明為Component ,而不是裝飾器的更詳細類型。 例如,這可以在LogEventsToConsole中看到。
我的建議改變了裝飾器的創(chuàng)建方式。
使用Java 8
要使用Java 8的所有功能,我建議為所有裝飾器添加一個特殊的接口DecoratingComponent 。 裝飾器的抽象超類實現(xiàn)了該接口,但像以前一樣,僅保留對Component的引用。
重要的是要注意,由于新接口的定義(請參見下文),混凝土裝飾器沒有任何變化。 它們在模式的兩種實現(xiàn)中都是完全相同的。 抽象類實際上也沒有任何變化(請參見下文),因此切換到該解決方案不會產(chǎn)生明顯的成本。
新介面
新接口DecoratingComponent擴展了基本組件接口,并為裝飾器提供了工廠方法。 這些是靜態(tài)或默認/防御方法(因此它們已經(jīng)實現(xiàn),如果可以的話將是最終方法),并且不應聲明任何抽象方法。 這樣,新接口不會在繼承樹后面的實現(xiàn)上增加額外的負擔。
關(guān)于以下代碼示例:通用示例僅是為該帖子創(chuàng)建的。 涉及超鏈接偵聽器的對象來自演示應用程序 。 最值得注意的是DecoratingHyperlinkListener ( 到源文件的鏈接 ),它擴展了Swing的HyperlinkListener 。
方法
接口本身實際上非常簡單,由三種類型的方法組成。
適配器
若要快速從Component移至DecoratingComponent ,接口應具有靜態(tài)方法,該方法采用第一個方法,然后返回后者。 由于DecoratingComponent擴展了Component且未添加抽象方法,因此這很簡單。 只需創(chuàng)建一個匿名實現(xiàn),并將所有調(diào)用轉(zhuǎn)發(fā)到已適配的component 。
通用方法如下所示:
靜態(tài)適配器方法
static DecoratingComponent from(Component component) {DecoratingComponent adapted = new DecoratingComponent() {@Overridepublic SomeReturn someMethod(SomeArgument argument) {return component.someMethod(argument);}// ... more methods here ...};return adapted; }在使用DecoratingHyperlinkListener情況下,它要容易得多,因為它是一個功能接口,因此可以使用lambda表達式:
'DecoratingHyperlinkListener'中的靜態(tài)適配器方法
static DecoratingHyperlinkListener from(HyperlinkListener listener) {return event -> listener.hyperlinkUpdate(event); }通用裝飾
這是接口的基本方法:
default DecoratingComponent decorate(Function<? super DecoratingComponent, ? extends DecoratingComponent>decorator) {return decorator.apply(this); }它從一個裝飾組件到另一個裝飾組件接受一個函數(shù)作為參數(shù)。 它將功能應用于自身以創(chuàng)建裝飾實例,然后將其返回。
可以在整個代碼中使用此方法,以一種簡單易讀的方式裝飾任何組件:
用'DecoratingComponent'裝飾
Component some = ...; DecoratingComponent decorated = DecoratingComponent// create an instance of 'DecoratingComponent' from the 'Component'.from(some)// now decorate it.decorate(component -> new MyCoolComponentDecorator(component, ...));// if you already have an instance of 'DecoratingComponent', it get's easier decorated = decorated.decorate(component -> new MyBestComponentDecorator(component, ...));// constructor references are even clearer (but cannot always be used) decorated = decorated.decorate(MyBestComponentDecorator::new);混凝土裝飾
您還可以添加使用具體裝飾器裝飾實例的方法:
“ DecoratingHyperlinkListener”中的混凝土裝飾
default DecoratingHyperlinkListener logEvents() {return LogEventsToConsole.decorate(this); }default DecoratingHyperlinkListener onHoverMakeVisible(JComponent component) {return OnHoverMakeComponentVisible.decorate(this, component); }它們使裝飾非常簡潔易讀:
用'DecoratingComponent'裝飾
DecoratingComponent decorated = ... decorated = decorated.logEvents();但是,是否應真正添加這些方法仍有待商de。 盡管它們非常方便,但是當它們創(chuàng)建循環(huán)依賴關(guān)系時,可以對它們進行強烈的爭論。 裝飾者不僅知道接口(它們是通過抽象超類間接實現(xiàn)的),現(xiàn)在接口也知道其實現(xiàn)。 通常,這是刺激性的代碼氣味。
最終召集尚未結(jié)束,但我建議采取務(wù)實的中間方式。 我讓該接口知道存在于同一包中的實現(xiàn)。 這將是通用的,因為它們沒有引用其余代碼中的任何具體內(nèi)容。 但是我不會讓我知道在系統(tǒng)深處創(chuàng)建的每個瘋狂裝飾器。 (當然,除非將其稱為the_kraken,否則我不會將所有這些裝飾器都添加到同一包中。)
為什么需要額外的接口?
是的,是的,所有那些Java 8功能都非常不錯,但是您不能簡單地將這些方法添加到AbstractDecorator嗎? 好問題!
當然,我可以在這里添加它們。 但由于兩個原因,我不喜歡這種解決方案。
單一責任原則
首先,這將模糊類的職責。 新接口負責裝飾Component實例,抽象超類負責啟用裝飾器的輕松實現(xiàn)。
這些不是相同的事物,它們不會因相同的原因而改變。 每當必須包含新的裝飾器時,新的界面可能就會更改。 每當Component更改時,抽象類都會更改。
類型層次結(jié)構(gòu)
如果將這些方法添加到AbstractDecorator ,則只能在此類實例上調(diào)用它們。 因此,所有裝飾器都必須從該類繼承,這限制了將來實現(xiàn)的范圍。 誰知道,也許出現(xiàn)了一些非常好的理由,為什么另一個類不能成為AbstractDecorator 。
更糟糕的是,所有裝飾器都必須公開一個事實,即它們是AbstractDecorator 。 突然有一個抽象類,它只是為了簡化實現(xiàn)而創(chuàng)建的,它遍及整個代碼庫。
其他差異
除了引入新界面之外,這種模式的變化不會有太大變化。
對抽象裝飾器類的更改
如果可以訪問該類,則應讓它實現(xiàn)DecoratingComponent而不是Component 。 由于沒有引入新的抽象方法,因此不需要進一步的更改。 上面的UML圖中顯示了這一點。
如果您不能更改類,則裝飾器將僅實現(xiàn)Component 。 這將使您避免使用其構(gòu)造函數(shù)來創(chuàng)建將組件映射到裝飾組件的函數(shù)。 由于需要將該函數(shù)作為decorate方法的參數(shù),因此必須將該方法更改為如下所示:
通用裝飾
// note the more general second type of the 'Function' interface default DecoratingComponent decorate(Function<? super DecoratingComponent, ? extends Component> decorator) {// create the decorated instance as beforeComponent decorated = decorator.apply(this);// since it is no 'DecoratingComponent' use 'from' to turn it into onereturn from(decorated); }裝飾者的變化
無需更改這些類。 當然,除非您是使用靜態(tài)工廠方法的那些瘋狂的人之一。 比您必須確保它們將其返回類型聲明為DecoratingComponent否則您將處于與抽象超類無法實現(xiàn)新接口的情況相同的情況。 如果您不能更改裝飾器類,則此處使用相同的解決方案。
例
因此,讓我們從上方再次查看代碼段:
用Java 8裝飾
// create a 'HyperlinkListener' with a method reference HyperlinkListener listener = this::changeHtmlViewBackgroundColor; // decorate that instance with different behaviors // (note that each call actually returns a new instance // so the result has to be assigned to a variable) listener = DecoratingHyperlinkListener// adapt the 'HyperlinkListener' to be a 'DecoratingHyperlinkListener'// (looks better if it is not on its own line).from(listener)// call some concrete decorator functions.onHoverMakeVisible(urlLabel).onHoverSetUrlOn(urlLabel).logEvents()// call the generic decorator function with a lambda expression.decorate(l -> new OnActivateHighlightComponent(l, urlLabel))// call the generic decorator function with a constructor reference.decorate(OnEnterLogUrl::new);反射
我們了解了如何使用Java 8的靜態(tài)和默認接口方法為裝飾器模式創(chuàng)建流暢的API。 它使代碼同時更加簡潔和可讀性,同時又不干擾模式的機制。
正因為如此,我們使用默認的方法來創(chuàng)建特質(zhì)有關(guān)其作者Brian Goetz寫道 :
了解默認方法的關(guān)鍵是,主要的設(shè)計目標是接口演變 ,而不是“將接口轉(zhuǎn)變?yōu)?#xff08;中等)特性”
抱歉,Brian,這太誘人了。 ;)
對裝飾器模式有一些見解? 想要改善我的想法還是批評它? 然后發(fā)表評論! 并且不要忘記在GitHub上簽出代碼 。
翻譯自: https://www.javacodegeeks.com/2015/01/the-decorator-pattern-with-java-8.html
裝飾器模式j(luò)ava
總結(jié)
以上是生活随笔為你收集整理的装饰器模式java_Java 8的装饰器模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓锁屏解锁状态怎么录屏(安卓锁屏解锁)
- 下一篇: linux设置内存大小(linux设置内