盘点那些在项目中使用过的设计模式

104 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情

前言

关于设计模式,相信大家都不陌生,无论是在日常的开发过程中,还是在面试的过程中面试官的提问,或多或少都会涉及到设计模式,通常我们使用设计模式开发,可以使我们的代码结构更加清晰、避免重复造轮子、也可以使代码更加简洁,所以在开发中我们需要尽量使用设计模式来开发。

关于Spring的设计模式,在网上搜了很多,其实大家讲的都差不多,这里我主要是结合项目开发,将常用的一些设计模式运用到项目中使用,让我们能了解设计模式的同时,也能很方便的运用在日常开发中。

工厂设计模式

在Spring中,我们拿到IOC容器无非有三种方式,那就是使用ApplicationContext接口下的三个实现类:ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、AnnotationConfigApplicationContext ApplicationContextAware接口是用来获取框架自动初始化的Ioc容器对象的。

通过枚举值不同来加载不同的Bean,这就是工厂模式。

Example:



public class ResourceProviderFactory implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public ResourceProvider getProviderByResourceType(CfgResourceTypeDTO resourceType) {
        if (resourceType == null) {
            return null;
        }
        ResourceProvider resourceProvider = null;
        if (StringUtils.equals("ENUM", resourceType.getResCate())) {
            resourceProvider = applicationContext.getBean("enumResourceProvider", ResourceProvider.class);
        } else if (StringUtils.equals("student", resourceType.getResCate())) {
            resourceProvider = applicationContext.getBean("studentResourceProvider", ResourceProvider.class);
        }else if(StringUtils.equals("teacher", resourceType.getResCate())) {
            resourceProvider = applicationContext.getBean("teacherResourceProvider", ResourceProvider.class);
        }else if (StringUtils.equals("class", resourceType.getResCate())) {
            resourceProvider = applicationContext.getBean(classResourceProvider, ResourceProvider.class);
        }
        return resourceProvider;
    }

        @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    }

模版设计模式

模版模式就是类似于我们定义了一个基本的机构,子类可以不去改变这个结构的情况下, 去针对于某些业务场景进行特定的实现。这样可以使我们业务得到拓展的情况下,又不会去改变原来的结构。

public abstract class TemplateTest {

    //定义模版方法
    public void templateMethod(){
        submit();
        save();
        find();
    }


    //模版类提交方法
    protected  void submit(){

    }
    //被子类重写的方法
    protected  abstract void save();
    protected  abstract void find();


    //子类实现
    class TemplateImpl extends TemplateTest{

        //子类派生提交方法
        @Override
        protected void submit() {

        }

        @Override
        protected void save() {

        }

        @Override
        protected void find() {

        }
    }
}

观察者设计模式

观察者模式(Observer),其实主要是来定义一对多的关系,通过发布者发布一个状态发生改变,其他的观察者通过订阅消息,都能接收到通知发生改变,在这个过程中还可以通过加入更多的观察者来达到可扩展。 设计主要包含这几个角色,观察者接口、不同岗位的观察者、被观察者接口、配置观察者。 我拿具体的一个审批业务场景来设计观察者模式,需要审批的业务单在公共池,如果订单被领导审批。状态发生改变,需要不同的业务员去观察业务单的状态,同时也可以新增加新的业务审批员。

302E47B7-D2EC-4C98-A534-C523A5A5A54B.png

首选定义观察者接口

public interface Observer {

    //观察者修改方法
    void update(String message);
}

定义主题接口

public interface Station {

    //添加审批业务员
    void registerObserver(Observer o);
    //删除审批业务员
    void removeObserver(Observer o);
    //通知审批业务员
    void notifyObserver();
}

推送消息主题

public class ManagerServer implements Station{

    private List<Observer> list;
    private String msg;

    public ManagerServer() {
        list = new ArrayList<>();
    }

    @Override
    public void registerObserver(Observer o) {
        list.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        if (!list.isEmpty()) {
            list.remove(o);
        }
    }

    @Override
    public void notifyObserver() {

        for (int i = 0; i <list.size() ; i++) {
            Observer observer =list.get(i);
            observer.update(msg);
        }
    }

    public void noticMessage(String msg){
        this.msg = msg;
        System.out.println("业务数据发生更新: " + msg);
        // 当观察者调用noticMessage方法时,会调用notifyObserver通知其他观察者更新消息
        notifyObserver();

    }
}

具体观察者实现

public class ObserverImpl implements Observer{

    private  String name;

    public ObserverImpl(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name+"收到推送消息"+message);
    }
}

测试方法

public class ObserverTest {

    public static void main(String[] args) {
        ManagerServer managerServer = new ManagerServer();

        //加入
        ObserverImpl Lxlxxx = new ObserverImpl("Lxlxxx");
        ObserverImpl Lxlxxx2 = new ObserverImpl("Lxlxxx2");
        //注册需要监听的观察者
        managerServer.registerObserver(Lxlxxx);
        managerServer.registerObserver(Lxlxxx2);

        managerServer.noticMessage("业务单的审批状态已发生改变");
System.out.println("----------------------------------------------");
        //删除Lxlxxx2业务员
        managerServer.removeObserver(Lxlxxx2);
        managerServer.noticMessage("业务单的审批状态再次发生改变");

    }
}

测试结果 业务单子状态发生改变后会通知其他注册业务员,观察者业务员会收到通知单子状态发生改变,同时如果去remove业务员,那么下次发送业务单子发生改变时,就无法接受到订单状态通知了。

E7092E75-F8E8-4244-AFD3-4D0037242247.png

适配器设计模式

Spring MVC 的HandlerAdapter就是适配处理器,主要是用于适配DispatcherServlet通过HandlerMapping的映射到对应的Handler,通过HandlerAdapter适配器来适配不同的Controller

Example:

支付方法

public class Payment {

    public void offline() {
        System./out/.println(“目前付款模式只支持线下付款~~~”);
    }
}

多种支付方法

public interface PaymentType {

    public void offline();
    //微信支付
    public void weixin();

    //支付宝支付
    public void zhifubao();

    //银行转账
    public void bankTransfer();

}

通过Adapter适配器使Payment支持多种付款模式

public class Adapter extends Payment implements PaymentType{


    @Override
    public void weixin() {
        System./out/.println(“使用微信线上付款”);
    }

    @Override
    public void zhifubao() {
        System./out/.println(“使用支付宝线上付款”);
    }

    @Override
    public void bankTransfer() {
        System./out/.println(“使用银行转账付款”);
    }
}

测试方法:

public class AdapterTest {

    public static void main(String[] args) {
        PaymentType paymentType = new Adapter();
        paymentType.offline();
        paymentType.weixin();
        paymentType.zhifubao();
        paymentType.bankTransfer();
    }
}

A662EBD8-9E4C-4E94-95AE-11C309041367.png 以上是对类的适配,需要继承适配的类,当然还有对象适配器,就不用去继承类了,直接在适配器里面去实例化对象。

对象适配模式

public class ObjectAdapter implements PaymentType{

    private Payment payment;

    public ObjectAdapter(Payment payment) {
        super();
        this.payment = payment;
    }

    @Override
    public void offline() {
        System.out.println("线下现金付款");
    }

    @Override
    public void weixin() {
        System.out.println("使用微信线上付款");
    }

    @Override
    public void zhifubao() {
        System.out.println("使用支付宝线上付款");
    }

    @Override
    public void bankTransfer() {
        System.out.println("使用银行转账付款");
    }
}

测试方法:

public class AdapterTest {

    public static void main(String[] args) {
        PaymentType paymentType = new ObjectAdapter(new Payment());
        paymentType.offline();
        paymentType.weixin();
        paymentType.zhifubao();
        paymentType.bankTransfer();
    }
}

2A5815E9-557E-4061-8631-1CE563FFDCFE.png

策略(Strategy)设计模式

策略模式和适配器模式大同小异,从类型上看,一个是结构模式,一个是行为模式。 策略模式:通过环境类把所有的算法统一封装起来,统一提供一个接口给客户端用,算法的新增和修改都比较灵活。 适配器模式:把两个或多个不兼容的接口,通过Adapte适配器类,让它们都彼此兼容,都可以进行调用。

Example 下面通过业务场景说下策略模式,保费计算,子女付费50%,父母付费80%。

策略方法

public interface Strategy {

    //保费计算基本方法
    Double premiumFormula(Double cost);

}

子女保费计算

public class ChildrenStrategy implements Strategy {

    @Override
    public Double premiumFormula(Double cost) {
        //子女费用减半
        cost =cost*0.5;
        return cost;
    }
}

父母保费计算

public class ParentStrategy implements Strategy{

    @Override
    public Double premiumFormula(Double cost) {
        //父母八折
        cost =cost*0.8;
        return cost;
    }
}

环境类通过类型去判断调哪种策略方法计算保费

public class Content {

    private Strategy strategy;

    public Content(String type) {
        if (type==“children”){
            this.strategy=new ChildrenStrategy();
        }
        if (type==“parent”){
            this.strategy = new ParentStrategy();
        }

    }
    //环境类方法计算方法
    public Double contextMethod(Double cost){

        return  strategy.premiumFormula(cost);
    }
}

测试方法

public class StrategyTest {

    public static void main(String[] args) {

        System./out/.println(“子女保费计算金额:”+new Content(“children”).contextMethod(100.0));
        System./out/.println(“父母保费计算金额:”+new Content(“parent”).contextMethod(100.0));

    }
}

EC64BADD-5247-489D-9FC3-0A8751B39348.png

责任链设计模式

主要是某个请求需要对个步骤进去处理,相当于一个链路的处理,沿着我们定义好的流程进行,相当于流程的审批。 下面举个贷款流程审批的例子,贷款人Lxlxxx审批贷款,金额<=1000的需要部门组长审批,金额>1000部门组长没有权限了,需要经理去审批,经理可以审批金额在<=2000的范围,如果金额大于2000则就需要老板审批

8B51D916-DFA0-4777-992E-5D6EB582F653.png

贷款类,包括贷款人和贷款的具体金额

public class LoansRequest {

    //贷款人
    private  String name;
    //贷款金额
    private BigDecimal money;

    public LoansRequest(String name, BigDecimal money) {
        this.name = name;
        this.money = money;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getMoney() {
        return money;
    }

    public void setMoney(BigDecimal money) {
        this.money = money;
    }
}

定义抽象审批流程,包含审批者和下一个审批者

public abstract class SpHandler {
    protected String name; // 审批者
    protected SpHandler nextSpHandler;  // 下一个审批者

    public SpHandler(String name) {
        this.name = name;
    }

    //审批提交
    public abstract boolean spSubmit(LoansRequest loansRequest);

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public SpHandler getNextSpHandler() {
        return nextSpHandler;
    }

    public void setNextSpHandler(SpHandler nextSpHandler) {
        this.nextSpHandler = nextSpHandler;
    }
}

三个角色分别为部门组长、经理、老板

public class FirstTrial extends SpHandler {

    public FirstTrial(String name) {
        super(name);
    }

    @Override
    public boolean spSubmit(LoansRequest loansRequest) {
        int result = loansRequest.getMoney().compareTo(new BigDecimal(1000));
        if (result == -1) {
            System./out/.println(“借款人” + loansRequest.getName() + “——“ + “贷款金额小于1000,可以允许贷款”);
            return true;
        } else if (result == 0) {
            System./out/.println(“借款人” + loansRequest.getName() + “——“ + “贷款金额等于1000,可以允许贷款”);
            return true;
        }
        System./out/.println(“借款人” + loansRequest.getName() + “——“ + “贷款金额大于1000,需要经理审批”);
        return nextSpHandler.spSubmit(loansRequest);
    }

}
public class Manager extends SpHandler {

    public Manager(String name) {
        super(name);
    }

    @Override
    public boolean spSubmit(LoansRequest loansRequest) {
        int result = loansRequest.getMoney().compareTo(new BigDecimal(2000));
        if (result == -1) {
            System./out/.println(“经理审批ing”);
            System./out/.println(“借款人”+loansRequest.getName()+”——“+”贷款金额小于2000,可以允许贷款”);
            return true;
        } else if (result == 0) {
            System./out/.println(“经理审批ing”);
            System./out/.println(“借款人”+loansRequest.getName()+”——“+”贷款金额等于2000,可以允许贷款”);
            return true;
        }
        System./out/.println(“借款人”+loansRequest.getName()+”——“+”贷款金额大于2000,需要老板审批”);
        return nextSpHandler.spSubmit(loansRequest);
    }
}

public class Boos extends SpHandler{

    public Boos(String name) {
        super(name);
    }

    @Override
    public boolean spSubmit(LoansRequest loansRequest) {
        int result = loansRequest.getMoney().compareTo(new BigDecimal(10000));
        if (result == -1) {
            System./out/.println(“老板审批ing”);
            System./out/.println(“借款人”+loansRequest.getName()+”——“+”贷款金额小于10000,可以允许贷款”);
            return true;
        }
        return false;
    }
}

测试类

public class ResponsibilityTest {

    public static void main(String[] args) {
        SpHandler firstTrial = new FirstTrial(“部门组长”);
        SpHandler manager = new Manager(“经理”);
        SpHandler boss = new Boos(“老板”);

        firstTrial.setNextSpHandler(manager);
        manager.setNextSpHandler(boss);

        boolean result = firstTrial.spSubmit(new LoansRequest(“Lxlxxx”, new BigDecimal(1000)));
        System./out/.println(“审批结果:” + result + “\n”);

        boolean result1 = firstTrial.spSubmit(new LoansRequest(“Lxlxxx”, new BigDecimal(2000)));
        System./out/.println(“审批结果:” + result1 + “\n”);
        boolean result2 = firstTrial.spSubmit(new LoansRequest(“Lxlxxx”, new BigDecimal(8000)));
        System./out/.println(“审批结果:” + result2 + “\n”);

    }
    
}

可以看到审批流程随着贷款金额的变化,逐渐形成了一个审批链路,达到了我们责任链的一个设计模式

9BF157F8-27AF-4EB6-BC01-B72AAC8A4687.png

总结

通过总结以上比较常用的设计模式,在日常开发中的某些场景,确实使我们的代码结构变得很清晰,也能让整个业务结构变得很清楚,由此可见设计模式的重要性,还有其他的设计模式,请评论区的小伙伴们补充。