鸟瞰设计模式

写完后倒回来看一下这篇文章,感受有点乱糟糟的,权当本身的笔记了。本文未完待续。html

我的认为,所谓的设计模式,就是前人总结的面向接口编程中的最佳实践,为何是接口呢?由于设计模式中通常涉及三个类:java

  • 客户端
  • 接口
  • 接口实现类

客户端(通常指业务代码)持有接口,设计模式就是对接口进行实现。本文不讲UML类图,由于代码实践中的设计模式跟文献中学院派的设计模式有些是有点不一样的,学院派理论很完美但复杂,代码中若是写太复杂的可能后期维护成本大。本文主要说明Java中23种设计模式在工做中的应用,同时结合Spring框架或JDK源码等经常使用的代码,说说设计模式在Java中的例子。git

总结概括

Java设计模式主要分为三种类型:建造者模式、结构性模式、行为型模式。
恰好对应于对象的生命周期:实例化、实例之间的关系、实例的使用。
Spring框架的设计也符合这个顺序:IoC(建造者)、AOP(结构型)、其余如Data Access(行为型)。 github

20191105085652.png
如上图,我已经把经常使用设计模式背景色改成黑色了。Java设计模式一共23种,上图中标红的简单工厂模式不是GoF总结出来的23种设计模式之一。一句话总结以下:

  • 建造型:
    • 工厂模式:根据传入参数实例化对象,如BeanFactory.getBean("helloBean");
    • 单例模式:构造方法私有化,提供静态方法获取实例。如Singleton.getInstance();
    • 建立者模式:根据属性命令方法,该方法返回一个自己Builer对象,build()方法建造一个实例,如HelloBean helloBean = new HelloBeanBuilder().name("donggua").age(26).build()
  • 结构型:
    • 代理模式:代理对象中持有被代理对象的实例,加强接口的抽象方法。如:HelloIntf helloIntf = new HelloIntfProxy();
    • 装饰模式:装饰对象中经过参数传入持有被装饰对象的实例,加强接口的抽象方法。如HelloIntf helloIntf = new HelloIntfDecorator(new HelloIntfImpl());
    • 适配器模式:链接两个不一样的接口,实现左边接口,持有右边接口对象。如:class USBAdapter implements USBSerialPort; class TFCard; USBAdapter{ void readSerial(tfCard.readTF()) };
  • 行为型:
    • 模板方法模式:抽象类中定义好方法顺序,子类继承后顺序不变。如:class HelloTemplate { void display(){ preHello(), hello(); postHello();} };
    • 策略模式:抽象类定义好策略,子类实现,客户端选择具体子类,如:class PayPolicy{ pay(); }; class AliPayPolicy { pay(){}; }; PayPolicy pp = new AliPayPolicy()
    • 观察者模式:被观察对象持有观察对象的列表,事件触发时遍历通知。如:class Observable{ List<Observer> observerList; };
    • 责任链模式:抽象类中定义级别和本抽象类的引用,事件触发时,级别大于本节点级别的不处理。如:class Chain{ int level; Chain nextNode; do(level){ if(level < this.level) dosomething(); nextNode.do(); }; };

建造型

建立型模式,就是建立对象的模式,抽象了实例化的过程。主要是为了规范化new实例的过程。好比,工厂模式能够对对象进行统一管理,修改一个配置就能够实例化出单例的对象或者多例对象。Spring最基本的IoC容器就是工厂模式。算法

工厂模式

工厂模式通常类名以Factory结尾,工厂模式三剑客:简单工厂模式、工厂方法模式、抽象工厂模式。数据库

简单工厂模式(经常使用)

简单工厂模式又叫静态工厂方法模式,工厂类提供一个静态方法,对不一样的入参返回不一样的具体实现类。如slf4j + logback的实现。其中。编程

private Logger logger = LoggerFactory.getLogger(HelloTest.class); 
复制代码
  • 客户端:业务代码
  • 接口:Logger
  • 接口实现:定义一个LoggerFactory工厂,在logback中返回具体的实例

工厂方法模式

连载中。。。设计模式

抽象工厂模式

连载中。。。api

单例模式(经常使用)

单例模式是 Java 中最简单的设计模式之一。其涉及到一个单一的类,该类负责建立本身的对象(通常使用getInstance()方法),同时确保只有单个对象被建立。单例有懒汉、饿汉、双重检查锁,静态内部类等多种实现方式。JDK和Spring源码中暂未找到比较经常使用的实例。双重检查锁代码以下:安全

package me.zebin.demo.javaio;

public class Singleton {

    // 这里的变量务必使用volatile关键字,不然会有线程安全问题
    public static volatile Singleton singleton = null;
    // 构造方法私有化,禁止直接new
    private Singleton(){

    }
    // 获取惟一实例的方法
    public static Singleton getInstance(){
        if (null != singleton) {
            return singleton;
        }
        // 多个线程在此等待
        synchronized (Singleton.class) {
            // 只有第一个线程执行了new代码,第二个线程条件不成立
            if (null == singleton) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}
复制代码

建立者模式(经常使用)

建立者模式(建造者模式)将一个复杂对象的构建与它的表示分离,使得一样的构建过程能够建立不一样的表示。说人话,建造者和工厂模式不一样之处在于,工厂模式只须要说明我要哪一个对象,工厂就能帮你造,而建立者则须要提供原材料。建立者一个典型的建造过程就是链式建造,最后调用build()方法触发。JDK源码中的例子就是StringBuilder类。

StringBuilder sb = new StringBuilder("Hello").append("world").sppend("!").toString();
复制代码

普通的JavaBean也能够很容易实现建造者模式,使用lombok插件,在Bean上加@Builder就可给你建立一个建造者模式的类。而后你使用建造者的链式写法来建立一个实例,如:

Person person = new Person().age(26).name("dongua").city("guangzhou").build();
复制代码

lombok是提升编码效率的很强大的插件,不了解的能够搜索一下。

原型模式

连载中。。。

结构型

结构型模式是为解决怎样组装现有的类,设计它们的交互方式,从而达到实现必定的功能目的。结构型模式包容了对不少问题的解决。例如,Spring的AOP就是解决类间的结构融合,使用的时结构型的代理模式。

代理模式

代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。可是JDK和Spring源码中大多使用动态代理,虽然静态代理实现比较简单,可是在实际项目中咱们须要为每一个类都写一个代理类,须要写不少重复冗余的代码,不利于代码的解耦与扩展。为一个类作代理主要是为了加强该类的功能,咱们这里只讨论静态代理,JDK和Spring中没找到合适的静态代理的例子,这里手动写一个吧。

以下,定义一个Car接口,只有一个drive()方法,全部品牌的车都继承自该接口,普通的奔驰车实现了Car接口的drive()方法,经过定义奔驰车的代理,加强车的功能。为了方便,我写在了一个类里。

package me.zebin.demo.javaio;

import org.junit.Test;

public class ProxyTest {

    @Test
    public void client() {
        // 面向接口编程,持有一个Car接口
        Car car = new BenzCarProxy();
        car.drive();
    }

    // Car接口,Car能drive
    interface Car{
        void drive();
    }

    // 普通的奔驰车,能开
    class BenzCar implements Car{

        @Override
        public void drive() {
            System.out.println("我是奔驰车,我在开车呢老铁...");
        }
    }

    // 代理,加强型的奔驰
    class BenzCarProxy implements Car{

        // 持有普通奔驰引用
        BenzCar benzCar = new BenzCar();
        @Override
        public void drive() {
            System.out.println("普通车进化,超级车,能飞了兄dei...");
            benzCar.drive();
        }
    }

}

复制代码
  • 客户端:业务代码client
  • 接口:Car接口
  • 接口实现:BenzCar,BenzCarProxy。

装饰模式

装饰模式容许向一个现有的对象添加新的功能,同时又不改变其结构。因此,装饰模式是加强类的功能的,这点和代理模式很像。相同之处是,代理类或装饰类都持有源类的引用,而不一样点是,代理类在内部直接new出一个被代理对象,装饰类在初始化时须要传入被代理类做为参数。因此装饰模式才是在源类自己上增长功能,源类变强了。而代理模式只是在代理类在源类上作一些操做,实际上源类并无加强。但整个代理类看起来是加强了。
Java中的源码好比io流就大量使用装饰模式,如:

FileInputStream  fis = new FileInputStream(inputStream);
复制代码

其中FileInputStream加强了InputStream的功能。

适配器模式

适配器模式(Adapter Pattern)是做为两个不兼容的接口之间的桥梁。例如,读卡器是做为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就能够经过笔记原本读取内存卡。代码以下:

package me.zebin.demo.javaio;

import org.junit.Test;

public class AdapterTest {

    // USB计算机串口,能够读USB设备
    interface USBSerialPort{
        void readUSB();
    }

    // 手机的tf卡
    class TFCard{

        // TFCard能够被读取
        public void readTFCard(){
            System.out.println("我是TFCard,数据正在输出,被读取中...");
        }
    }

    // TFCard适配器是一种USB串口设备,因此实现了该接口,USBSerialPort和TFCard接口不兼容,须要适配器转接
    class TFAdapater implements USBSerialPort{

        // TF适配器是专门适配TFCard的,因此持有一个TFCard
        TFCard tfCard = new TFCard();

        // 计算机读取TFCard适配器时,适配器读取TFCard中的内容
        @Override
        public void readUSB() {
            tfCard.readTFCard();
        }
    }

    @Test
    public void computerClient(){

        // USB接口插上适配器
        USBSerialPort usbSerialPort = new TFAdapater();
        usbSerialPort.readUSB();
    }

}
复制代码

固然,USB串口上能够插各类各样的其余设备,如键盘等,只要你作一个适配器就能够了。

JDK源码中,早期日志框架使用的是java自带的JUL,后来出现了log4j,这二者都是具体的实现,若是一个项目中这两种日志都使用了,那么就很难控制日志的配置,好比,调整日志级别等。因而出现了common-logging这种面向接口的设计,客户端只须要持有common-logging中的Logger引用,commom-logging对接JUL或者log4j,固然这JUL和log4j具体的接口是不一致的,common-logging就使用了适配器来对接二者。以下图部分源码。

20191104144503.png

还有JDK IO中的InputStreamReader也属于适配器模式,InputStreamReader适配器两端链接了Reader和InputStream。

门面模式

门面模式是对象的结构模式,外部与一个子系统的通讯必须经过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。说白了,就是提供给外部使用的要尽量简单,外部系统可能完成一件事要调用你多个api,这时可使用门面模式,将这些步骤整合在一块儿,提供一个门面接口便可。例如,slf4j和common-logging,他们并不具体实现写日志的功能,而只是一个门面,咱们只须要和门面交互,就能够实现打日志的功能,而不须要和具体的log4j,jul等交互。

桥接模式

桥接模式将抽象部分与它的实现部分分离,使它们均可以独立地变化。举个例子,汽车Car有品牌Brand、驾驶模式DriveMode两种模式。品牌有宝马、奔驰等,驾驶模式又有手动挡和自动挡等,至关于在桥的一端有宝马、奔驰,另外一端有自动挡、手动挡。若是以品牌为实现类,为驾驶模式建立子类,如宝马手动挡、宝马自动挡。那么将会创造出大量的类。这个时候,能够将DriveMode类型不做为Brand的子类,而是与Brand平级,Brand持有DriveMode的实例,即Brand桥接到DriveMode。如此,新增一个Brand就不会影响DriveMode,二者独立变化。
JDK中的例子,就是jdbc,然而jdbc的实现看起来跟桥接模式定义有点不同,我分不清,如今先不讨论了。

行为型模式

在对象的结构和对象的建立问题都解决了以后,就剩下对象的行为问题了,若是对象的行为设计的好,那么对象的行为就会更清晰,它们之间的协做效率就会提升。好比Spring中JdbcTemplate即便来控制数据库访问流程,使用的是行为型的模板方法模式。

策略模式

在策略模式(Strategy Pattern)中,一个类的行为或其算法能够在运行时更改。策略模式比较简单,通俗的讲,就是客户端持有一个策略抽象,并实例化须要的具体策略,进行调用便可。
策略模式常常用来解决if-else嵌套过多的问题。
JDK中使用策略模式的有ThreadPoolExecutor中的拒绝策略,总共四个拒绝策略实现了RejectedExecutionHandler,子类实现其拒绝算法rejectedExecution(),使用时能够本身选择不一样算法。这就是策略模式。

模板方法模式

在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类能够按须要重写方法实现,但调用将以抽象类中定义的方式进行。通俗的讲,模板方法就是一个思路很清晰的智者,他将第一步作啥第二步作啥都清清楚楚的罗列出来,等子类来继承,从新子类本身须要的步骤。
Spring源码中jdbcTemplate就是使用模板方法模式,他将链接数据库的步骤放置在模板中,子类去从新自定义的步骤。

责任链模式

责任链模式(Chain of Responsibility Pattern)为请求建立了一个接收者对象的链。在这种模式中,一般每一个接收者都包含对另外一个接收者的引用。若是一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。通俗的讲,就是每一个子类都给自身定一个级,并持有下一个父引用,每次执行抽象方法先判断级别,级别不够的转入链中的下一个节点。

观察者模式

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。好比,当一个对象被修改时,则会自动通知它的依赖对象。通俗的说,就是被观察者持有观察者对象列表,当某个事件发生时,会遍历列表执行方法。

参考资料

23种设计模式要在一篇文章中写完,篇幅有点太长了,花了一周有些设计模式还没写到。心力有点交瘁,感受越写到后面越简略,只能当成本身的笔记来看了。先把文章post出来吧,改成连载模式。

相关文章
相关标签/搜索