面试官问,你在开发中有用过什么设计模式吗?我懵了

设计模式不该该停留于理论,跟具体业务结合,它才会变得更香~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技术栈电子书、各个大厂面试题。

阿丸笔记

相关文章
相关标签/搜索