成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

面試官:我不想聽單例、工廠了,跟我說說裝飾器模式吧!

開發 前端
如果數學和繪畫課程又新增了需求,需要額外展示對應的輔修資料,但英語課程則不需要展示這類信息,那按照繼承的方式應該如何實現呢?

我草草地估算了一下,基本上80% Java 候選人的簡歷上,在專業技能欄上都會寫上這么一條:

熟悉常用的GOF設計模式,可在實際業務場景中進行合理運用;

如果面試官恰好看到了這條專業技能,問道:“那你說一下,都熟悉并使用過哪些設計模式呢?”

然后,絕大多數候選人都會回答說:“嗯,熟悉單例模式和工廠模式。”

面試官接著問道:“還有其他的嗎?”

候選人一般會說:“嗯,還有代理模式、策略模式這些吧,其實平時用到的也不是很多。”

此時,若面試官繼續追問:“裝飾器模式有沒有了解過?”

候選人往往會發愣一下,然后說:“嗯,這個設計模式也聽過,但沒太深入了解。”

嗯,本文我們以真實場景帶入的方式來講解一下,有用且有趣的“裝飾器”模式。

接下來話不多說,Show me the case。

業務背景

某大型在線教育學習平臺,其學生端最重要的功能就是展示學生的課程列表,學生可點擊課程列表中的某個課程進教室上課,還可以查看這節課對應的課件、課前預習和課后作業等。

如下圖所示:

圖片

當然,真實的業務場景還是要復雜很多的,比如:英語課程的 PC 端按照上圖中的展示方式即可,而英語課程 iPad 端的產品經理,則希望在已經上過的課程中,加上老師給學生的打分。

數學課程與英語課程也有不同的地方,課程卡片上需要展示教師的標簽,如:名師、活躍、名校畢業、教齡長、好評多等;

最近新推出的繪畫課程,不但需要在課程卡片上展示教師的標簽,而且為了鼓勵學生更多地上課學習,會在課程卡片上展示一個完課獎品。

當然,我僅僅是舉個例子,實際的課表展示邏輯會復雜很多。

代碼質量問題

說說目前這塊的代碼實現情況。

最開始的時候,公司只有英語課程,且 PC 端和 iPad 端的課表展示邏輯是一樣的。

代碼demo如下:

public class Curriculum {


    public void query(int studentID) {
    
        System.out.println("展示課表");
        System.out.println("展示對應的課前預習");
        System.out.println("展示對應的課后作業");
        System.out.println("展示對應的課件");
    }
}

后來,英語課程的 PC 端和 iPad 端的課表展示邏輯不一樣了,iPad 端的課表展示需要加上老師給學生的打分,代碼實現如下:

public class Curriculum {


    public void query(int studentID, int origin) {


        System.out.println("展示課表");
        System.out.println("展示對應的課前預習");
        System.out.println("展示對應的課后作業");
        System.out.println("展示對應的課件");


        //英語課程iPad端
        if(origin == 1){
            System.out.println("展示對應的學生評分");
        }
    }
}

再后來,又增加了需要展示老師標簽的數學課程,以及增加老師標簽和完課獎品的繪畫課程,都在一個類中以 if else 分支判斷的方式來實現,代碼的可讀性和可維護性就太差了。

于是,負責維護這塊業務代碼的工程師干脆一刀切,直接寫成了四套代碼。

英語課程PC端:

public class EnglishPCCurriculum {


    public List query(int studentID) {
      
        System.out.println("展示課表");
        System.out.println("展示對應的課前預習");
        System.out.println("展示對應的課后作業");
        System.out.println("展示對應的課件");
    }
}

英語課程iPad端:

public class EnglishIPadCurriculum {


    public void query(int studentID) {
    
        System.out.println("展示課表");
        System.out.println("展示對應的課前預習");
        System.out.println("展示對應的課后作業");
        System.out.println("展示對應的課件");
        
        System.out.println("展示對應的學生評分");
    }
}

數學課程:

public class MathCurriculum {


    public void query(int studentID) {
    
        System.out.println("展示課表");
        System.out.println("展示對應的課前預習");
        System.out.println("展示對應的課后作業");
        System.out.println("展示對應的課件");
        
        System.out.println("展示對應的老師標簽");
    }
}

繪畫課程:

public class DrawCurriculum {


     public void query(int studentID) {
     
        System.out.println("展示課表");
        System.out.println("展示對應的課前預習");
        System.out.println("展示對應的課后作業");
        System.out.println("展示對應的課件");
        
        System.out.println("展示對應的老師標簽");
        System.out.println("展示對應的完課獎品");
      }
}

劃重點,代碼按照上述方式實現,有何問題?

在《重構—改善既有代碼的設計》一書中,有兩種非常常見的Bad Smell(糟糕的代碼),叫做 “過長的方法” 和 “重復的代碼” 。

其實問題還是挺大的,我們上面的代碼只是實現了一個demo而已,如果是真實的代碼,這個查詢課表的query()方法實現了太多的業務邏輯,一定命中了“過長的方法”這個Bad Semll。

而且,上面這四個類中的query()方法,在實現展示課表、作業、預習、課件業務無邏輯的時候,也命中了“重復的代碼”這個Bad Semll。

除此之外,這段代碼還命中了一種叫做 “發散式變化” 的 Bad Smell。

發散式變化的定義是,一個類被錨定了多個變化,當這些變化中的任意一個發生時,就必須對類進行修改。這說明該類承擔的職責過多,不符合單一職責的設計原則。

而上面這四個類的query()方法中,從頭到尾實現了整個課表展示的邏輯,只要課表、作業、預習、課件等任意邏輯發生變化都需要對這個類進行修改,確實承擔的職責過多了。

接下來,我們看看如何這塊代碼進行重構,使其實現方式更具可維護性和可擴展性。

裝飾器模式

裝飾器模式(Decorator Pattern),在不改變一個現有對象結構的情況下,為其動態地增加一些額外的職責。

裝飾器模式的優點在于:

  1. 可動態地為現有對象增加額外的職責,無需改動原來的代碼,具備更好的靈活性和可擴展性,且符合開閉原則。
  2. 每種額外的職責都被實現為一個單獨且通用的裝飾器,符合單一職責,解決了“過長的方法”、“重復的代碼”和“發散式變化”等Bad Smell。

其類結構圖如下:

圖片圖片

Component:定義了被裝飾對象和裝飾器都需要實現的接口。

ConcreteComponent:被裝飾對象,需要提供業務邏輯的核心功能。

Decorator:抽象裝飾器,可通過其子類進行額外功能職責的擴展。

ConcreteDecorator:具體裝飾類,對被裝飾對象進行額外功能職責的擴展。

代碼重構優化

接下來我們通過裝飾器模式將代碼進行重構優化。

Curriculum接口:

public interface Curriculum {


    public void query(int studentID);
}

Curriculum具體實現:

public class ConcreteCurriculum implements Curriculum {


    public void query(int studentID) {


        System.out.println("展示課表");
        System.out.println("展示對應的課前預習");
        System.out.println("展示對應的課后作業");
        System.out.println("展示對應的課件");
    }
}

Curriculum的抽象裝飾器:

public abstract class CurriculumDecorator implements Curriculum {
    protected Curriculum curriculum;


    public CurriculumDecorator(Curriculum curriculum){
        this.curriculum = curriculum;
    }


    public void query(int studentID){
        curriculum.query(studentID);
    }
}

Curriculum的評分裝飾器:

public class ScoreDecorator extends CurriculumDecorator{


    public ScoreDecorator(Curriculum curriculum) {
        super(curriculum);
    }


    @Override
    public void query(int studentID) {
        curriculum.query(studentID);
        System.out.println("展示對應的學生評分");


    }
}

Curriculum的老師標簽裝飾器:

public class LabelDecorator extends CurriculumDecorator{


    public LabelDecorator(Curriculum curriculum) {
        super(curriculum);
    }


    @Override
    public void query(int studentID) {
        curriculum.query(studentID);
        System.out.println("展示對應的老師標簽");


    }
}

Curriculum的獎品裝飾器:

public class GiftDecorator extends CurriculumDecorator{


    public GiftDecorator(Curriculum curriculum) {
        super(curriculum);
    }


    @Override
    public void query(int studentID) {
        curriculum.query(studentID);
        System.out.println("展示對應的完課獎品");


    }
}

Demo:

public class Demo {
    public static void main(String[] args) {
        Curriculum curriculum = new ConcreteCurriculum();
        CurriculumDecorator scoreDecorator = new ScoreDecorator(new ConcreteCurriculum());
        CurriculumDecorator labelDecorator = new LabelDecorator(new ConcreteCurriculum());
        CurriculumDecorator giftLabelDecorator = new GiftDecorator(labelDecorator);




        System.out.println("英語PC端課表展示");
        curriculum.query(123);
        System.out.println();


        System.out.println("英語iPad端課表展示");
        scoreDecorator.query(123);
        System.out.println();


        System.out.println("數學課表展示");
        labelDecorator.query(123);
        System.out.println();


        System.out.println("繪畫課表展示");
        giftLabelDecorator.query(123);


    }
}

執行結果:

英語PC端課表展示
展示課表
展示對應的課前預習
展示對應的課后作業
展示對應的課件


英語iPad端課表展示
展示課表
展示對應的課前預習
展示對應的課后作業
展示對應的課件
展示對應的學生評分


數學課表展示
展示課表
展示對應的課前預習
展示對應的課后作業
展示對應的課件
展示對應的老師標簽


繪畫課表展示
展示課表
展示對應的課前預習
展示對應的課后作業
展示對應的課件
展示對應的老師標簽
展示對應的完課獎品

至此,展示課表業務場景的代碼改造完畢。

有的同學可能會問,為什么不通過繼承的方式進行實現呢?

其原因在于,繼承的方式不如這種動態組合的方式靈活,也很難實現這種細粒度的代碼復用。

舉個例子:如果數學和繪畫課程又新增了需求,需要額外展示對應的輔修資料,但英語課程則不需要展示這類信息,那按照繼承的方式應該如何實現呢?


責任編輯:武曉燕 來源: 托尼學長
相關推薦

2021-11-02 22:04:58

模式

2020-08-03 07:38:12

單例模式

2021-11-03 14:10:28

工廠模式場景

2020-07-20 07:48:53

單例模式

2020-07-02 07:52:11

RedisHash映射

2021-02-16 10:53:19

單例模式面試

2021-09-10 06:50:03

TypeScript裝飾器應用

2024-03-05 10:33:39

AOPSpring編程

2024-08-22 10:39:50

@Async注解代理

2024-03-06 13:19:19

工廠模式Python函數

2021-05-28 11:18:50

MySQLbin logredo log

2024-05-30 08:04:20

Netty核心組件架構

2025-03-05 08:04:31

2024-02-29 16:49:20

volatileJava并發編程

2025-04-08 00:00:00

@AsyncSpring異步

2024-11-19 15:13:02

2023-12-27 18:16:39

MVCC隔離級別幻讀

2025-04-16 00:00:01

JWT客戶端存儲加密令

2024-08-29 16:30:27

2024-08-12 17:36:54

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品久久久久久久久久久久 | 欧美一级黄色片在线观看 | 皇色视频在线 | 亚洲精品成人网 | 日日夜夜免费精品视频 | 日韩三级 | 美女黄网 | 午夜欧美一区二区三区在线播放 | 日本精品久久久久 | 久久久精品视 | 亚洲人成免费 | 最新国产在线 | 精品欧美一区二区三区久久久 | 午夜精品久久久久久久久久久久久 | 七七婷婷婷婷精品国产 | 欧美aⅴ| 日韩精品一区二区三区在线播放 | 麻豆视频在线免费观看 | 色成人免费网站 | 亚洲444kkkk在线观看最新 | 一级片毛片 | 亚洲一页 | 久久香蕉网 | 成人字幕网zmw | 欧美在线一区二区三区 | 一区二区三区影院 | 一区二区精品 | 国产精品mv在线观看 | 一区二区三区四区在线视频 | 成在线人视频免费视频 | 精品一区二区视频 | www.9191| 国产精品乱码一区二三区小蝌蚪 | 黄色在线 | 亚洲福利在线观看 | 一区二区av | 视频在线日韩 | 日韩午夜影院 | 做a视频在线观看 | 久久精品久久综合 | 毛片黄片免费看 |