面試官問,你在開發中有用過什麼設計模式嗎?我懵了

設計模式不該該停留於理論,跟具體業務結合,它纔會變得更香~git

1.前言

設計模式咱們多少都有些瞭解,可是每每也只是知道是什麼。github

在真實的業務場景中,你有用過什麼設計模式來編寫更優雅的代碼嗎?面試

咱們更多的是天天從產品經理那裏接受到新需求後,就開始MVC一把梭,面向sql編程了。算法

咱們習慣採用MVC架構,實時上是很是容易建立不少貧血對象模型,而後寫出過程式代碼。咱們使用的對象,每每只是數據的載體,沒有任何邏輯行爲。咱們的設計過程,也是從ER圖開始,以數據爲中心進行驅動設計。一個需求一個接口,從controller到service到dao,這樣日復一日的CRUD。sql

什麼設計模式?根本不存在的!數據庫

今天,咱們嘗試從經常使用設計模式(工廠模式、代理模式、模版模式)在CRUD中的可落地場景,但願能給你們帶來一些啓發。編程

2.理解設計模式

設計模式(Design pattern),不是前人憑空想象的,而是在長期的軟件設計實踐過程當中,通過總結獲得的。設計模式

使用設計模式是爲了讓代碼具備可擴展性,實現高聚合、低耦合的特性。api

世上原本沒有設計模式,寫代碼的人多了,便有了設計模式。架構

面向對象的設計模式有七大基本原則:

  • 開閉原則(首要原則):要對擴展開放,對修改關閉
  • 單一職責原則:實現類要職責單一
  • 里氏代換原則:不要破壞繼承體系
  • 依賴倒轉原則:面向接口編程
  • 接口隔離原則:設計接口要精簡單一
  • 合成/聚合複用原則:儘可能先使用組合或者聚合來實現,其次才考慮使用繼承關係來實現
  • 最少知識原則或者迪米特法則:下降耦合

過去,咱們會去學習設計模式的理論,今天,咱們嘗試從經常使用設計模式(工廠模式、代理模式、模版模式)在CRUD中的可落地場景,但願能給你們帶來一些實戰啓發。

3.設計模式實戰案例

3.1工廠模式

1)工廠模式介紹

工廠模式應該是咱們最熟悉的設計模式了,不少框架都會有經典的xxxxFactory,而後經過xxxFactory.create來獲取對象。這裏不詳細展開介紹,給出一個你們耳熟能詳的工廠模式類圖應該就能回憶起來了。

 

面試官問,你在開發中有用過什麼設計模式嗎?我懵了

 

工廠模式的優勢很明顯:

  • 一個調用者想建立一個對象,只要知道其名稱就能夠了。
  • 擴展性高,若是想增長一個產品,只要擴展一個工廠類就能夠。
  • 屏蔽產品的具體實現,調用者只關心產品的接口。

那麼,實際業務開發該怎麼落地使用呢?

2)需求舉例

咱們須要作一個HBase的管理系統,相似於MySQL的navicat或者workbench。

那麼有一個很重要的模塊,就是實現HBase的增刪改查。

而開源的HBase-client已經提供了標準的增刪改查的api,咱們如何集成到系統中呢?

3)簡單代碼

@RestController("/hbase/execute")

public class DemoController {

    private HBaseExecuteService hbaseExecuteService;

    public DemoController(ExecuteService executeService) {
        this.hbaseExecuteService = executeService;
    }

    @PostMapping("/insert")
    public void insertDate(InsertCondition insertCondition) {
        hbaseExecuteService.insertDate(insertCondition);
    }

    @PostMapping("/update")
    public void updateDate(UpdateCondition updateCondition) {
        hbaseExecuteService.updateDate(updateCondition;
    }

    @PostMapping("/delete")
    public void deleteDate(DeleteCondition deleteCondition) {
        hbaseExecuteService.deleteData(deleteCondition);
    }

    @GetMapping("/select")
    public Object selectDate(SelectCondition selectCondition) {
        return hbaseExecuteService.seletData(selectCondition);
    }
}

每次增長一個功能,都須要從controller到service寫一遍相似的操做。

還須要構建不少相關dto進行數據傳遞,裏面會帶着不少重複的變量,好比表名、列名等查詢條件。

4)模式應用

抽象接口

public interface HBaseCommand {
/**
* 執行命令
*/
ExecResult execute();
}

抽象類實現公共配置

public class AbstractHBaseCommand implements HBaseCommand {

    Configuration configuration;

    AbstractHBaseCommand(ExecuteCondition executeCondition) {
        this.configuration = getConfiguration(executeCondition.getResourceId());
    }

    private Configuration getConfiguration(String resourceId) {
        Configuration conf = HBaseConfiguration.create();
        //作一些配置相關事情
        //。。。。
        return conf;
    }

    @Override
    public ExecResult execute() {
       return null;
    }
}

 

工廠類生產具體的命令

public class CommandFactory {

    private ExecuteCondition executeCondition;

    public CommandFactory(ExecuteCondition executeCondition) {
        this.executeCondition = executeCondition;
    }

    public HBaseCommand create() {
        HBaseCommand hBaseCommand;
        switch (ExecuteTypeEnum.getTypeForName(executeCondition.getQueryType())) {
            case Get:
                return new GetCommand(executeCondition);
            case Put:
                return new PutCommand(executeCondition);
            case Delete:
                return new DeleteCommand(executeCondition);
        }
        return null;
    }
}

一個執行接口,執行增刪改查多個命令

public class ExecuteController {

    private ExecuteService executeService;

    public ExecuteController(ExecuteService executeService) {
        this.executeService = executeService;
    }

    @GetMapping
    public Object exec(ExecuteCondition executeCondition) {
        ExecResult execResult = executeService.execute(executeCondition);
        return transform(execResult);
    }
}

 

service調用工廠來建立具體的命令進行執行

@Service

public class ExecuteService {

    public ExecResult execute(ExecuteCondition executeCondition) {
        CommandFactory factory = new CommandFactory(executeCondition);
        HBaseCommand command = factory.create();
        return command.execute();
    }
}

 

每次添加一個新的命令,只須要實現一個新的命令相關內容的類便可

面試官問,你在開發中有用過什麼設計模式嗎?我懵了

 

3.2 代理模式

1) 模式介紹

代理模式也是你們很是熟悉的一種模式。

它給一個對象提供一個代理,並由代理對象控制對原對象的引用。它使得用戶不能直接與真正的目標對象通訊。

代理對象相似於客戶端和目標對象之間的中介,能發揮比較多做用,好比擴展原對象的能力、作一些切面工做(打日誌)、限制原對象的能力,同時也在必定程度上面減小了系統的耦合度。

 

面試官問,你在開發中有用過什麼設計模式嗎?我懵了

 

2)需求舉例

如今已經有一個client對象,實現了若干方法。

如今,有兩個需求:

  • 但願這個對象的方法A再也不被支持。
  • 但願對這個client的全部方法的執行時間進行記錄。

3)簡單代碼

對本來的類進行修改

  • 刪除方法A(不兼容改動,若是別人有引用,可能會編譯報錯)
  • 對每一個方法的先後埋點計算時間(業務入侵太大,代碼嚴重冗餘)

4)模式應用

對於方法A的再也不支持,其實有挺多辦法的,繼承或者靜態代理均可以。

靜態代理代碼:

public class ConnectionProxy implements Connection {

    private Connection connection;

    public ConnectionProxy(Connection connection) {
        this.connection = connection;
    }

    @Override
    public Admin getAdmin() throws IOException {
//拋出一個異常
        throw new UnsupportedOperationException();
    }

    @Override

    public boolean isClosed() {

        return connection.isClosed();

    }

    @Override

    public void abort(String why, Throwable e) {

        connection.abort(why, e);

    }

}

對於每一個方法的先後計算埋點,可使用動態代理進行實現。

public class TableProxy implements InvocationHandler {

    private Object target;

    public TableProxy(Object target) {
        this.target = target;
    }

    /**
     * 獲取被代理接口實例對象
     *
     * @param <T>
     * @return
     */

    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long current = System.currentTimeMillis();
        Object invoke;
        try {
            invoke = method.invoke(target, args);
        } catch (Throwable throwable) {
            throw throwable;
        }
        long cost = System.currentTimeMillis() - current;
        System.out.println("cost time: " + cost);
        return invoke;
    }
}

3.3 模板方法

1) 模式介紹

定義一個操做中的流程框架,而將一些流程中的具體步驟延遲到子類中實現。

模板方法使得子類能夠不改變一個流程框架下,經過重定義該算法的某些特定步驟實現自定義的行爲。

固然,最便利之處在於,咱們能夠保證一套完善的流程,使得不一樣子類明確知道本身須要實現哪些方法來完成這套流程。

 

面試官問,你在開發中有用過什麼設計模式嗎?我懵了

 

2)需求舉例

其實模板方法是最容易理解的,也很是高效。

咱們最經常使用模版方法的一類需求就是工單審批流。

具體來講,假如咱們如今須要定義一套流程來實現一個工單審批,包含工單建立、審批操做、事件執行、消息通知等流程(實際上流程可能會更加複雜)。

而工單的對象很是多,能夠是一個服務的申請、一個數據庫的變動申請、或者是一個權限申請。

3)簡單代碼

每一個工單流程寫一套代碼。

  • 重複工做多;
  • 流程某些關鍵環節可能會缺失,好比事件執行之後忘記通知了。

4)模式應用

定義一個接口,裏面包括了若干方法

public interface ChangeFlow {

    void createOrder();

    boolean approval();

    boolean execute();

    void notice();

}

在一個流程模版中,拼接各個方法,實現完整工做流

public class MainFlow {

    public void mainFlow(ChangeFlow flow) {
        flow.createOrder();
        if (!flow.approval()){
            System.out.println("抱歉,審批沒有經過");
        }
        if (!flow.execute()) {
            System.out.println("抱歉,執行失敗");
        }
        flow.notice();
    }
}

而後,能夠在AbstractChangeFlow裏面實現通用的方法,好比approval、notice,你們都是同樣的邏輯。

public class AbstractChangeFlow implements ChangeFlow {

    @Override
    public void createOrder() {
        System.out.println("建立訂單");
    }

    @Override
    public boolean approval() {
        if (xxx) {
            System.out.println("審批經過");
            return true;
        }
        return false;
    }

    @Override
    public boolean execute() {
        //交給其餘子類本身複寫
        return true;
    }

    @Override
    public void notice() {
        System.out.println("notice");
    }
}

 

最後,就根據具體的工單來實現自定義的excute()方法就好了,實現DbFlow、MainFlow就好了。

面試官問,你在開發中有用過什麼設計模式嗎?我懵了

 

4.總結

學習設計模式最重要的就是要在業務開發過程當中保持思考,在某一個特定的業務場景中,結合對業務場景的理解和領域模型的創建,才能體會到設計模式思想的精髓。

若是脫離具體的業務場景去學習或者談論設計模式,那是沒有意義的。

有人說,怎麼區分設計模式和過分設計呢?

其實很簡單。

1)業務設計初期。若是很是熟悉業務特性,理解業務迭代方向,那麼就能夠作一些簡單的設計了。

2)業務迭代過程當中。當你的代碼隨着業務的調整須要不斷動刀改動,破壞了設計模式的七大原則,尤爲是開閉原則,那麼,你就該去考慮考慮使用設計模式了。

 

看到這裏了,原創不易,點個關注、點個贊吧,你最好看了~

知識碎片從新梳理,構建Java知識圖譜:https://github.com/saigu/JavaKnowledgeGraph(歷史文章查閱很是方便)

掃碼關注個人公衆號「阿丸筆記」,第一時間獲取最新更新。同時能夠免費獲取海量Java技術棧電子書、各個大廠面試題。

阿丸筆記

相關文章
相關標籤/搜索