1. 設(shè)計模式詳解——裝飾者模式

        共 3295字,需瀏覽 7分鐘

         ·

        2021-10-16 15:32

        前言

        在程序開發(fā)設(shè)計中,有一個特別重要的原則是,類應(yīng)該對擴(kuò)展開放,對修改關(guān)閉,雖然這一原則聽起來很矛盾,但是在一些比較優(yōu)秀的設(shè)計模式中,是完全可以達(dá)成這一原則的,比如裝飾者模式,它就是這一原則的最佳實(shí)踐,下面我們來看下它的基本原理和用法,希望能通過這篇內(nèi)容,引發(fā)給位小伙伴對于設(shè)計模式的思考。

        裝飾者模式

        裝飾者模式動態(tài)地將責(zé)任附加到對象上,若要擴(kuò)展功能,裝飾者提供了比繼承更有彈性的替代方案。

        要點(diǎn)

        • 繼承屬于擴(kuò)展形式之一,但不見得是達(dá)到彈性設(shè)計的最佳方式;
        • 在我們的設(shè)計中,應(yīng)該允許行為可以被擴(kuò)展,而無須修改現(xiàn)有代碼
        • 組合和委托可用于在運(yùn)行時動態(tài)地加上新的行為
        • 除了繼承,裝飾者模式也可以讓我們擴(kuò)展行為
        • 裝飾者模式意味著一群裝飾者類,這些類用來包裝具體組件
        • 裝飾者類反映出被裝飾組件的類型(他們具有相同的類型,都經(jīng)過接口或繼承實(shí)現(xiàn))
        • 裝飾者可以在被裝飾者的行為前面或者后面加上自己的行為,甚至將被裝飾者的行為整體覆蓋,從而達(dá)到特定目的
        • 裝飾者一般對組件的客戶是透明的,除非客戶程序依賴于組件的具體類型
        • 裝飾者會導(dǎo)致設(shè)計中出現(xiàn)許多小對象,如果過度使用,會讓程序變得復(fù)雜。

        示例代碼

        原始對象接口

        基類接口,也是包裝類最底層的內(nèi)容(俄羅斯套娃里面第一個被套的那個),也可以理解為包裝類中的最小單位,這里我們用形狀這個抽象概念作為示例演示。

        首先這個抽象接口有一個基本方法,就是draw繪制方法,凡是實(shí)現(xiàn)了形狀這個接口的所有類都必須實(shí)現(xiàn)這個接口。

        public?interface?Shape?{
        ????/**
        ?????*?繪制方法
        ?????*/

        ????void?draw();
        }
        接口實(shí)現(xiàn)

        這里是接口的實(shí)現(xiàn),不過為了方便理解,各位小伙伴可以把接口的實(shí)現(xiàn)當(dāng)作第一層包裝類,這一層包裝相當(dāng)于直接確定了我們形狀的基本屬性,表明它是一個圓形。

        public?class?Circle?implements?Shape?{
        ????@Override
        ????public?void?draw()?{
        ????????System.out.println("繪制圓形");
        ????}
        }

        同理,這里確定了我們的形狀為矩形。

        public?class?Rectangle?implements?Shape?{
        ????@Override
        ????public?void?draw()?{
        ????????System.out.println("繪制矩形");
        ????}
        }
        裝飾對象抽象

        這里相當(dāng)于做一個通用的包裝接口,后續(xù)的包裝類只需要繼承這個抽象類,然后再在其中加入新的行為或?qū)傩约纯?,而不需要重?fù)封裝。

        public?abstract?class?ShapeDecorator?implements?Shape?{
        ????protected?Shape?decoratedShape;

        ????public?ShapeDecorator(Shape?decoratedShape){
        ????????this.decoratedShape?=?decoratedShape;
        ????}

        ????public?void?draw(){
        ????????decoratedShape.draw();
        ????}
        }
        裝飾者實(shí)現(xiàn)

        這里就是第一層包裝(如果按我們上面的說法應(yīng)該是第三層,因?yàn)槲覀儌鬟f的肯定是shape的實(shí)現(xiàn),而不是抽象接口),這里繼承了上面的抽象包裝類。

        public?class?RedShapeDecorator?extends?ShapeDecorator?{

        ????public?RedShapeDecorator(Shape?decoratedShape)?{
        ????????super(decoratedShape);
        ????}

        ????@Override
        ????public?void?draw()?{
        ????????decoratedShape.draw();
        ????????setRedBorder(decoratedShape);
        ????}

        ????private?void?setRedBorder(Shape?decoratedShape){
        ????????System.out.println("Border?Color:?Red");
        ????}
        }

        同時引入了一個新的方法setRedBorder,這個方法讓我們的形狀有了一個新的屬性——紅色。當(dāng)然,我們還可以繼續(xù)封裝,我們可以在顏色的基礎(chǔ)上給他封裝一個尺寸:

        public?class?SizeShapeDecorator?extends?RedShapeDecorator{

        ????public?SizeShapeDecorator(Shape?decoratedShape)?{
        ????????super(decoratedShape);
        ????}

        ????@Override
        ????public?void?draw()?{
        ????????super.draw();
        ????????setSize(decoratedShape);
        ????}

        ????private?void?setSize(Shape?decoratedShape)?{
        ????????System.out.println("size:?100");
        ????}
        }
        測試代碼

        這里我們分別創(chuàng)建一個圓形和矩形,指定不同的包裝類,并調(diào)用他們的draw方法:

        ?@Test
        ????public?void?testDecorator()?{
        ????????Shape?circle?=?new?Circle();
        ????????ShapeDecorator?redCircle?=?new?RedShapeDecorator(new?Circle());
        ????????ShapeDecorator?redRectangle?=?new?RedShapeDecorator(new?Rectangle());
        ????????ShapeDecorator?sizeRedRectangle?=?new?SizeShapeDecorator(new?Rectangle());
        ????????System.out.println("原始圓形");
        ????????circle.draw();

        ????????System.out.println("===========\n包裝的紅色圓形");
        ????????redCircle.draw();

        ????????System.out.println("===========\n包裝的紅色矩形");
        ????????redRectangle.draw();?
        ????????
        ????????System.out.println("===========\n包裝的100紅色矩形");
        ????????sizeRedRectangle.draw();
        ????}

        最后運(yùn)行結(jié)果如下:

        總結(jié)

        從示例代碼及運(yùn)行結(jié)果我們可以看出來,雖然后續(xù)我們不斷為原始的形狀加入了新的屬性,但是我們并沒有改變原始代碼,而是一層一層地添加不同的包裝類,然后讓它逐步有了更多的新屬性。

        這樣的操作好處是,我們原有的形狀擁有了新的屬性,但這些后增加的屬性和原始形狀之間并沒有任何強(qiáng)依賴關(guān)系,也沒有破壞原因業(yè)務(wù)邏輯,保證了系統(tǒng)的可擴(kuò)展性,同時耦合性還很低,這就是裝飾者設(shè)計模式的魅力。

        不知道各位小伙伴是否還記得我們前段時間分享的spring boot源碼相關(guān)內(nèi)容,spring boot的源碼中就用到了裝飾者模式,比如BeanFactory的相關(guān)內(nèi)容,另外我們?nèi)粘i_發(fā)過程中用到的Tomcat有好多地方也用到了這種設(shè)計模式,最典型的是ServletRequestServletResponse

        - END -


        瀏覽 24
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
          
          

            1. 亚洲国产日韩一区无码精品久久久久 | 欧美性折磨bdsm另类 | A级国内。无遮挡影视 | 91亚洲国产专区 | 欧美日韩色综合 |