java中设计模式的运用



前言

java的版本不断的迭代更新,各种框架和组件也层出不穷,但是设计的思想永远不会变,也许平时只是一个curl工程师,还在代码中看不到任何设计模式的影子,那可太糟心了。设计模式的应用往往也能反应一个程序员的水平,设计模式说到底就是可扩展 可解耦 多封装一些共用行为,就是为了提升代码的可读可用性,设计模式就像写文章一样,没有对错之分,一条语句可以有多种表达方式 修辞手法。


一、单例模式

保证类只产生有且只有一个实例。由私有构造函数,共用访问点,一个静态初始变量组成。单例模式分饿汉和懒汉。

1.饿汉

饿汉不管需不需要直接实例化,不用考虑多线程冲突问题,可用于配置文件初始化。

package com.orange.demo.design;

public class Single {

    private static Single instance = new Single();

    // 禁止外部实例化
    private Single(){}

    public static Single getInstance(){
        return instance;
    }

    public void execute(){
        System.out.println("饿汉");
    }

    public static void main(String[] args){
        Single single = Single.getInstance();
        single.execute();
    }
}

2.懒汉

懒汉是调用时才实例化,线程不安全,可通过双锁机制保证线程安全,多用于对外部客户端的初始化

package com.orange.demo.design;

public class Single {

    private static volatile Single instance;//禁止指令重排

    // 禁止外部实例化
    private Single(){}

    public static Single getInstance(){
        if (instance==null) {
            synchronized (Single.class){
                if (instance==null) {//防止拿到锁后对象已经实例化
                    instance = new Single();
                }
            }
        }
        return instance;
    }

    public void execute(){
        System.out.println("懒汉");
    }

    public static void main(String[] args){
        Single single = Single.getInstance();
        single.execute();
    }
}

二、装饰器模式

故名思意,作为一个装饰品添加到原有功能上,不对原有代码做变更,spring中的过滤器和AOP拦截器都是装饰器模式。
装饰器模式由装饰类和被装饰类组成,装饰类继承被装饰类,将被装饰类实例传入装饰类,类似生成一个代理类。

package com.orange.demo.design;

public class House {

    public House() {
    }

    void door(){
        System.out.println("我是一扇门");
    }
}
package com.orange.demo.design;

public class Decorator extends House {

    private House house;

    public Decorator(House house) {
        this.house = house;
    }

    @Override
    void door() {
        super.door();
        System.out.println("添加一些装饰");
    }

    public static void main(String[] args){
        Decorator decorator = new Decorator(new House());
        decorator.door();
    }
}

三、工厂模式

工厂模式主要处理对象的多态,是开闭原则的一种实现,也就是可拓展 不可修改,工程模式有以下三类。

1.简单工厂

spring中bean的获取就是一种简单工厂模式,这里不过多赘述

2.工厂方法模式

和简单工厂的区别是简单工厂是一个工厂处理所有产品,工厂方法是多个工厂处理不通的产品

public interface IFactory {

    IProduct doProduct();
}
public interface IProduct {

    void execute();
}
public class IProductImplA implements IProduct {
    @Override
    public void execute() {
        System.out.println("产品A");
    }
}
public class IFactoryImplA implements IFactory{

    @Override
    public IProduct doProduct() {
        return new IProductImplA();
    }

    public static void main(String[] args){
        IFactory f = new IFactoryImplA();
        f.doProduct().execute();
    }
}

3.抽象工厂模式

抽象工厂模式和工厂方法模式相似,抽象工厂会抽象出一些具体行为,可以直观的看出工厂的功能。
如快递的物流公司选择。工厂中可以抽象出如下单 路由查询等具体行为。不同的物流公司的相同行为由不同的产品类实现,不同的工厂类只处理自家的业务。

四、建造模式

建造模式主要场景是对象的实例化或客户端的初始化。相比于构造函数的构造入参不可扩展,建造模式通过链式调用解决这一问题,对入参的一些校验可以放入build方法中,优势就是可扩展,可解耦。
实际应用的场景可以是调用第三方接口时生成入参,这种场景往往是通过本地的数据生成接口需要的入参。

import lombok.Data;

@Data
public class Good {

    private String name;

    private String price;

    public static class Builder {
        private String name;
        private String price;



        public Builder name(String name){
            this.name = name;
            return this;
        }

        public Builder price(String price){
            this.price = price;
            return this;
        }

        public Good build(){
            Good good = new Good();
            good.name = this.name;
            good.price = this.price;
            return good;
        }

    }
}
    public static void main(String[] args){
        Good good = new Good.Builder().name("1").price("2").build();
        System.out.println(good);
    }

五、适配器模式

顾名思义,购买了国外的电器,电压标准110V,而国内标准为220V,这时候需要一个电压转换器,适配器模式就是让本不兼容的事务可以使用。
拿上述举例,220V为适配者,110V为目标类

public class V220 {

    public void execute(){
        System.out.println("输出220V的电压");
    }
}
public interface Transform {

    /**
     * 负责电压转换
     */
    void transform();
}
/**
 * 110V电压转换器
 */
public class V110 implements Transform {

    private V220 v220;

    public V110(V220 v220) {
        this.v220 = v220;
    }

    @Override
    public void transform() {
        v220.execute();
        System.out.println("输出110V电压");
    }

    public static void main(String[] args){
        V110 v110 = new V110(new V220());
        v110.transform();
    }
}

适用对需要对输出结果进行转换的场景。

六、模板模式

模板是个人对一些流程化的 较复杂的业务常使用的设计模式,拿医疗系统的开立检查检验项目为例,业务流程大概是这么几块,创建就诊卡->科室挂号->项目申请,可能大部分人就是通过controller+接口实现进行开发,这种开发模式时间久了或者接手了别人的代码,这种流程性的行为就很难通过后端代码有一个直观的认识,往往需要通过前端代码确定整个接口流程。如果通过抽象类抽象公有行为,并设置流程模板,并且抽象类不同于接口,抽象类中可以提取并实现一些公有方法,对于后面的扩展是十分方便的。


/**
 * 检查检验申请抽象类
 */
@Data
public abstract class AbstractMedTecApply {

    //开立检查检验业务代码模板 获取病历号 + 挂号 + 申请
    public final void addMedicalTec() throws PBMException {
        init();
        checkApply();
        //挂号
        register();
        //申请
        apply();
    }

    /**
     * 申请前做一些校验
     */
    public void checkApply() throws PBMException {
        //校验就诊状态
    }

    //获取his病历号
    public void setPatientId(Map<String, Object> outPatientInfo) throws PBMException {
        //如果没有his病历号则调用接口获取
    }


    /**
     * 检查检验做各自的初始化
     */
    protected abstract void init();

    /**
     * 挂号 区分检验检查
     *
     * @throws PBMException
     */
    protected abstract void register() throws PBMException;

    /**
     * 医技申请
     *
     * @throws PBMException
     */
    protected abstract void apply() throws PBMException;

}

七、代理模式

代理模式的设计思路参考nginx的反向代理,客户类通过代理类调用委托类。
代理模式和装饰模式很像,说到区别网上有很多,但是我觉得还是参考nginx的反向代理,当调用的时候感知不到委托类的就是代理模式。
代理模式就不写代码了,无非就是隐藏委托类的代码。

八、责任链模式

使用链表的流程性场景,责任链模式中所有的对象都有一个共用的父类或接口,子类都调用的父类的同一个方法处理业务,子类间链式引用,最终通过父类方法处理多个子类。
下面的代码通过大家最熟悉的请假流程举例

package com.orange.demo.design.chain;

public abstract class Handler {

    // 最大请假天数
    public int maxDays;

    // 部门
    public String dept;

    public Handler(String dept, int maxDays){
        this.dept = dept;
        this.maxDays = maxDays;
    }

    private Handler nextHandler;

    public void next(Handler handler){
        this.nextHandler = handler;
    }

    /**
     * 请假天数校验
     * @param n 申请请假天数
     */
    public final void check(int n){
        if (this.maxDays>=n) {
            this.agree();
        } else {
            if (this.nextHandler!=null) {
                nextHandler.check(n);
            } else {
                System.out.println("审批不通过");
            }
        }
    }

    protected void agree(){
        System.out.println("部门 "+ this.dept + " 已同意");
    }
}

package com.orange.demo.design.chain;

public class DeptA extends Handler {

    public DeptA(String dept, int maxDays) {
        super(dept, maxDays);
    }
}

package com.orange.demo.design.chain;

public class DeptB extends Handler {

    public DeptB(String dept, int maxDays) {
        super(dept, maxDays);
    }
}

package com.orange.demo.design.chain;

public class DeptC extends Handler {
    public DeptC(String dept, int maxDays) {
        super(dept, maxDays);
    }
}

    public static void main(String[] args){
        DeptA a = new DeptA("研发部", 5);
        DeptB b = new DeptB("主管部", 10);
        DeptC c = new DeptC("董事局", 15);
        a.next(b);
        b.next(c);
        a.check(15);
    }

九、享元模式

享元模式的核心就是享,做到对象共亨,避免创建大量重复变量。
线程池,连接池,字符串常量池都是这类模式的运用。

十、观察者模式

类似消息队列的发布订阅模式,观察者相当于消费者的角色,多个观察者同时监听某一事件。

public interface Broker {

    void addObserver(Observer observer);

    void notify(String msg);
}

public class BrokerImpl implements Broker {

    private List<Observer> obsList = new ArrayList<>();

    @Override
    public void addObserver(Observer observer) {
        obsList.add(observer);
    }

    @Override
    public void notify(String msg) {
        for (Observer ob : obsList) {
            ob.getMsg(msg);
        }
    }
}

public class Observer {

    private String name;

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

    public void getMsg(String msg){
        System.out.println(name+"收到消息了 " + msg);
    }
}
    public static void main(String[] args){
        Observer a = new Observer("a");
        Observer b = new Observer("b");
        Broker broker = new BrokerImpl();
        broker.addObserver(a);
        broker.addObserver(b);
        broker.notify("xxxxxx");
    }

十一、策略模式

策略模式由抽象策略类 策略实现类 上下文类三部分组成,策略模式中通过上下文类实现策略。
乍看下和工厂模式很像,策略模式侧重对单个行为的算法的不同选择,工厂模式的范围比较大,是对多个行为封装到工厂后,再对行为进行实现。策略模式通过将策略实例传入上下文类中实现行为,工厂模式选择工厂后直接调用行为方法。
下面用多种支付方法举例

/**
 * 多种支付方式
 */
public interface PayStrategy {

    /**
     * 支付方式
     */
    void payWay();
}

/**
 * 支付宝
 */
public class AliStrategy implements PayStrategy {
    @Override
    public void payWay() {
        System.out.println("支付宝支付");
    }
}
/**
 * 微信
 */
public class WXPayStrategy implements PayStrategy {
    @Override
    public void payWay() {
        System.out.println("微信支付");
    }
}

public class PayContext {

    private PayStrategy payStrategy;

    public PayContext(PayStrategy payStrategy) {
        this.payStrategy = payStrategy;
    }

    public void pay(){
        payStrategy.payWay();
    }
}
    public static void main(String[] args){
        PayStrategy ali = new AliStrategy();
        PayContext context = new PayContext(ali);
        context.pay();
    }

总结

上面列举了部分设计模式,可以发现很多设计模式的思想都是相似的,就像张三丰教张无忌太极一样,设计模式是一种解决问题的思想,没有固定的套路。
快去使用设计模式吧!!!

to be continue…