目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

设计模式

简单工厂

 由一个工厂对象决定创建出哪一种产品类的实例。
 创建型,但不属于GOF23种设计模式。
编码风格,不属于设计模式。

# 适用场景
* 工厂类负责创建的对象比较少
* 客户端(应用层)只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心

# 优点
* 只需要传入一个正确的参数,就可以获取你所需要的对象,无需知道其创建细节。

# 缺点
* 工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,违背开闭原则。
* 无法形成基于继承的结构。

简单工厂

Video

public abstract class Video {
    public abstract void produce();
}

JavaVideo

public class JavaVideo extends Video {
    @Override
    public void produce() {
        System.out.println("Java 开讲了!!!");
    }
}

PythonVideo

public class PythonVideo extends Video {
    @Override
    public void produce() {
        System.out.println("Python开讲了!!!");
    }
}

VideoFactory

public class VideoFactory {

    /**
     * 重载   通过反射弥补扩展性(复杂的逻辑判断)
     * @param Class c
     * @return Video
     */
    public Video getVideo(Class c){
        Video video = null;
        try {
            video = (Video) Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return video;
    }

    /**
     * 重载   工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,违背开闭原则。
     * @param String  type
     * @return Video
     */
    public Video getVideo(String type){
        if("java".equalsIgnoreCase(type)){
            return new JavaVideo();
        }else if("python".equalsIgnoreCase(type)){
            return new PythonVideo();
        }
        return null;
    }
}

Test

public class Test {
    public static void main(String[] args) {
        //创建工厂
        VideoFactory videoFactory = new VideoFactory();
        //工厂根据传入的参数生产出对应的对象
        Video video = videoFactory.getVideo(JavaVideo.class);
        //Video video = videoFactory.getVideo("java");
        if(video == null){
            return;
        }
        //指定目标逻辑
        video.produce();
    }
}

案例: JDK 的 Calendar

private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale){...  根据不同国家做判断}

案例: JDBC 加载启动

	Class.forName("com.mysql.jdbc.Driver");

// Driver  类
package com.mysql.jdbc;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }
	//使用静态代码块注册了驱动:这里注册的Driver 是mysql的Driver
    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

案例: logback 的 LoggerContext

  public final Logger getLogger(final String name) { 
	Logger childLogger = (Logger) loggerCache.get(name);
  }

工厂方法

 定义一个创建对象的抽象方法,让子类通过继承来实现该抽象方法,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化过程推迟到子类中进行,也就是将创建对象的任务委托给不同的子类工厂
 工厂方法是为了解决同一产品等级业务抽象问题。
 创建型。

# 适用场景
* 创建对象需要大量重复的代码
* 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
* 一个类通过其子类来指定创建哪个对象

# 优点
* 用户只需要关心所需产品对应的工厂,无须关心创建细节
* 加入新产品符合开闭原则,提高可扩展性

# 缺点
* 类的个数容易过多,增加复杂度。(不同类型的工厂类,负责生生产不同类型的产品类)
* 增加了系统的抽象性和理解难度

工厂方法

Video

public abstract class Video {
    //Java、Python 都属于同一个产品等级
    public abstract void produce();

}

VideoFactory

public abstract class VideoFactory {
    public abstract Video getVideo();
}

JavaVideo

public class JavaVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Java课程视频");
    }
}

JavaVideoFactory

public class JavaVideoFactory extends VideoFactory {
    @Override
    public Video getVideo() {
        return new JavaVideo();
    }
}

PythonVideo

public class PythonVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Python课程视频");
    }
}

PythonVideoFactory

public class PythonVideoFactory extends VideoFactory {
    @Override
    public Video getVideo() {
        return new PythonVideo();
    }
}

Test

public class Test {
    public static void main(String[] args) {
        VideoFactory videoFactory = new PythonVideoFactory();
        VideoFactory videoFactory2 = new JavaVideoFactory();
        //通过工厂创建具体实例
        Video video = videoFactory.getVideo();
        video.produce();
    }
}

案例: Collection 类的 iterator() 方法

抽象方法是 Collection 类的 iterator() 方法
具体实现工厂 ArrayList 类
具体创建的产品的方法是  ArrayList 类的 iterator()
抽象产品是 Iterator<E> 接口
实际产品 Collection 的各个具体实现类 iterator() 方法 返回的 Iterator<E>

抽象工厂

 抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口。
 无须指定它们具体的类
 创建型

产品等级与产品族

# 适用场景
* 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
* 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。
* 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。

# 优点
* 具体产品在应用层代码隔离,无需关系创建细节
* 将一个系列的产品族统一到一起创建

# 缺点
* 新增产品等级比较困难,规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口
* 增加了系统的抽象性和理解难度

抽象工厂

CourseFactory

public interface CourseFactory {
    Video getVideo();
    Article getArticle();
}

Video

public abstract class Video {
    public abstract void produce();

}

Article

public abstract class Article {
    public abstract void produce();
}

JavaVideo

public class JavaVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Java课程视频");
    }
}

JavaArticle

public class JavaArticle extends Article {
    @Override
    public void produce() {
        System.out.println("编写Java课程手记");
    }
}

JavaCourseFactory

public class JavaCourseFactory implements CourseFactory {
    @Override
    public Video getVideo() {
        return new JavaVideo();
    }

    @Override
    public Article getArticle() {
        return new JavaArticle();
    }
}

PythonVideo

public class PythonVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Python课程视频");
    }
}

PythonArticle

public class PythonArticle extends Article {
    @Override
    public void produce() {
        System.out.println("编写Python课程手记");
    }
}

PythonCourseFactory

public class PythonCourseFactory implements CourseFactory {
    @Override
    public Video getVideo() {
        return new PythonVideo();
    }

    @Override
    public Article getArticle() {
        return new PythonArticle();
    }
}

Test

public class Test {
    public static void main(String[] args) {
        //创建A产品族的工厂
        CourseFactory courseFactory = new JavaCourseFactory();
        //获取产品 A1
        Video video = courseFactory.getVideo();
        //获取产品 A2
        Article article = courseFactory.getArticle();
  
        video.produce();//录制Java课程视频
        article.produce();//编写Java课程手记
    }
}

案例:Mysql 的 Connection、Mybatis 的 SqlSessionFactory

建造者

 流程固定、顺序不固定:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
 用户只需指定需要建造的类型就可以得到它们,建造过程及细节不需要知道。
 创建型。

建造者模式和工厂模式的区别
 建造者模式更注重于方法的调用顺序,工厂模式只关注创建产品。
 建造者模式可以用于构建复杂的产品,而工厂模式创造出来的都是一样等级的产品。
 建造者模式更专注于创建产品的过程,而工厂模式只要把产品创建出来就行了。

# 适用场景
* 如果一个对象有非常复杂的内部结构(很多属性)
* 想把复杂对象的创建和使用分离

# 优点
* 封装性好,创建和使用分离
* 扩展性好、建造类之间独立、一定程度上解耦

# 缺点
* 产生多余的Builder对象
* 产品内部发生变化,建造者都要修改,成本较大

建造者V1

Course

public class Course {
    private String courseName;
    private String coursePPT;
    private String courseVideo;
    private String courseArticle;
    //question & answer
    private String courseQA;

    public String getCourseName() {
        return courseName;
    }

    public void setCourseName(String courseName) {
        this.courseName = courseName;
    }

    public String getCoursePPT() {
        return coursePPT;
    }

    public void setCoursePPT(String coursePPT) {
        this.coursePPT = coursePPT;
    }

    public String getCourseVideo() {
        return courseVideo;
    }

    public void setCourseVideo(String courseVideo) {
        this.courseVideo = courseVideo;
    }

    public String getCourseArticle() {
        return courseArticle;
    }

    public void setCourseArticle(String courseArticle) {
        this.courseArticle = courseArticle;
    }

    public String getCourseQA() {
        return courseQA;
    }

    public void setCourseQA(String courseQA) {
        this.courseQA = courseQA;
    }

    @Override
    public String toString() {
        return "Course{" +
                "courseName='" + courseName + '\'' +
                ", coursePPT='" + coursePPT + '\'' +
                ", courseVideo='" + courseVideo + '\'' +
                ", courseArticle='" + courseArticle + '\'' +
                ", courseQA='" + courseQA + '\'' +
                '}';
    }
}

CourseBuilder

public abstract class CourseBuilder {

    public abstract void buildCourseName(String courseName);
    public abstract void buildCoursePPT(String coursePPT);
    public abstract void buildCourseVideo(String courseVideo);
    public abstract void buildCourseArticle(String courseArticle);
    public abstract void buildCourseQA(String courseQA);

    public abstract Course makeCourse();
}

CourseActualBuilder

public class CourseActualBuilder extends CourseBuilder {

    private Course course = new Course();


    @Override
    public void buildCourseName(String courseName) {
        course.setCourseName(courseName);
    }

    @Override
    public void buildCoursePPT(String coursePPT) {
        course.setCoursePPT(coursePPT);
    }

    @Override
    public void buildCourseVideo(String courseVideo) {
        course.setCourseVideo(courseVideo);
    }

    @Override
    public void buildCourseArticle(String courseArticle) {
        course.setCourseArticle(courseArticle);
    }

    @Override
    public void buildCourseQA(String courseQA) {
        course.setCourseQA(courseQA);
    }

    @Override
    public Course makeCourse() {
        return course;
    }
}

Coach

public class Coach {
    private CourseBuilder courseBuilder;

    public void setCourseBuilder(CourseBuilder courseBuilder) {
        this.courseBuilder = courseBuilder;
    }

    public Course makeCourse(String courseName,String coursePPT,
                             String courseVideo,String courseArticle,
                             String courseQA){
        this.courseBuilder.buildCourseName(courseName);
        this.courseBuilder.buildCoursePPT(coursePPT);
        this.courseBuilder.buildCourseVideo(courseVideo);
        this.courseBuilder.buildCourseArticle(courseArticle);
        this.courseBuilder.buildCourseQA(courseQA);
        return this.courseBuilder.makeCourse();
    }
}

Test

public class Test {
    public static void main(String[] args) {
        //课程构建类
        CourseBuilder courseBuilder = new CourseActualBuilder();
        //建造者
        Coach coach = new Coach();
        coach.setCourseBuilder(courseBuilder);

        //建造者通过课程构建类完成课程的具体构建流程
        Course course = coach.makeCourse("Java设计模式精讲",
                "Java设计模式精讲PPT",
                "Java设计模式精讲视频",
                "Java设计模式精讲手记",
                "Java设计模式精讲问答");
        System.out.println(course);//Course{courseName='Java设计模式精讲', coursePPT='Java设计模式精讲PPT', courseVideo='Java设计模式精讲视频', courseArticle='Java设计模式精讲手记', courseQA='Java设计模式精讲问答'}
    }
}

或者改进(链式调用)

建造者V2

Course

public class Course {

    private String courseName;
    private String coursePPT;
    private String courseVideo;
    private String courseArticle;

    //question & answer
    private String courseQA;

    public Course(CourseBuilder courseBuilder) {
        this.courseName = courseBuilder.courseName;
        this.coursePPT = courseBuilder.coursePPT;
        this.courseVideo = courseBuilder.courseVideo;
        this.courseArticle = courseBuilder.courseArticle;
        this.courseQA = courseBuilder.courseQA;
    }


    @Override
    public String toString() {
        return "Course{" +
                "courseName='" + courseName + '\'' +
                ", coursePPT='" + coursePPT + '\'' +
                ", courseVideo='" + courseVideo + '\'' +
                ", courseArticle='" + courseArticle + '\'' +
                ", courseQA='" + courseQA + '\'' +
                '}';
    }

    public static class CourseBuilder{
        private String courseName;
        private String coursePPT;
        private String courseVideo;
        private String courseArticle;

        //question & answer
        private String courseQA;

        public CourseBuilder buildCourseName(String courseName){
            this.courseName = courseName;
            return this;
        }


        public CourseBuilder buildCoursePPT(String coursePPT) {
            this.coursePPT = coursePPT;
            return this;
        }

        public CourseBuilder buildCourseVideo(String courseVideo) {
            this.courseVideo = courseVideo;
            return this;
        }

        public CourseBuilder buildCourseArticle(String courseArticle) {
            this.courseArticle = courseArticle;
            return this;
        }

        public CourseBuilder buildCourseQA(String courseQA) {
            this.courseQA = courseQA;
            return this;
        }

	/**
         * 完成 Course 的构造
         * @return
         */
        public Course build(){
            return new Course(this);
        }
  
    }
}

Test

public class Test {
    public static void main(String[] args) {
        //链式调用
        Course course = new Course.CourseBuilder().buildCourseName("Java设计模式精讲").buildCoursePPT("Java设计模式精讲PPT").buildCourseVideo("Java设计模式精讲视频").build();
        System.out.println(course);//Course{courseName='Java设计模式精讲', coursePPT='Java设计模式精讲PPT', courseVideo='Java设计模式精讲视频', courseArticle='null', courseQA='null'}

    }
}

案例:StringBuilder、StringBuffer、ImmutableSet(Guava)

        Set<String> set = ImmutableSet.<String>builder().add("a").add("b").build();
        System.out.println(set);//[a, b]

单例

 保证一个类仅有一个实例,并提供一个全局访问点。
 创建型

# 适用场景
* 想确保任何情况下都绝对只有一个实例

# 优点
* 在内存里只有一个实例,减少了内存开销
* 可以避免对资源的多重占用
* 设置了全局访问点,严格控制访问

# 缺点
* 没有接口,扩展困难(需要修改代码)

# 重点
* 私有构造函数
* 线程安全
* 延迟加载
* 序列化和反序列化安全
* 防御反射攻击

# 单例模式和其他设计模式的结合应用
* 单例模式和工厂模式
* 单例模式和享元模式

懒汉式(线程安全:性能太低 —— 同步方法)

public class LazySingleton {
    //初始化时不创建(延迟加载)
    private static LazySingleton lazySingleton = null;

    /**
     * 私有化无参构造函数
     */
    private LazySingleton(){
    }

    /**
     * 全局访问点
     * @return
     */
    public synchronized static LazySingleton getInstance(){
        //空则创建:延迟加载
        if(lazySingleton == null){
            lazySingleton = new LazySingleton();
        }
        //非空直接返回
        return lazySingleton;
    }

懒汉式 (性能优化)
单例-Double Check
单例_Double Check_volatile
LazyDoubleCheckSingleton

第一种解决思路(双重检查):不允许 第二步、第三步 发生重排序

public class LazyDoubleCheckSingleton {
    //不允许:第二步、第三步 (禁止重排序)
    private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
    private LazyDoubleCheckSingleton(){

    }
    public static LazyDoubleCheckSingleton getInstance(){
        if(lazyDoubleCheckSingleton == null){
            synchronized (LazyDoubleCheckSingleton.class){
                if(lazyDoubleCheckSingleton == null){
                    lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
                    //1.分配内存给这个对象
                    //3.设置lazyDoubleCheckSingleton 指向刚分配的内存地址
                    //2.初始化对象
                    //intra-thread semantics
                    //---------------//3.设置lazyDoubleCheckSingleton 指向刚分配的内存地址
                }
            }
        }
        return lazyDoubleCheckSingleton;
    }
}

第二种解决思路(静态内部类:基于类初始化的延迟加载解决方案):允许发生重排序,但是不允许其他线程看到
静态内部类:基于类初始化的延迟加载解决方案

public class StaticInnerClassSingleton {
    /**
     * 静态内部类:随类加载而加载
     */
    private static class InnerClass{
        private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }

    /**
     * 全局访问点
     * @return
     */
    public static StaticInnerClassSingleton getInstance(){
        return InnerClass.staticInnerClassSingleton;
    }

    /**
     * 私有化无参构造函数,如果实例已经存在则抛出异常
     */
    private StaticInnerClassSingleton(){
        if(InnerClass.staticInnerClassSingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }
}

饿汉式

public class HungrySingleton implements Serializable,Cloneable{
  
    private final static HungrySingleton hungrySingleton;
  
    //随类加载而初始化
    static{
        hungrySingleton = new HungrySingleton();
    }

    private HungrySingleton(){
        if(hungrySingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }

    /**
     * 全局访问点
     * @return
     */
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

序列化破坏单例模式的解决方案
HungrySingleton

public class HungrySingleton implements Serializable,Cloneable{

    private final static HungrySingleton hungrySingleton;

    //随类加载而初始化
    static{
        hungrySingleton = new HungrySingleton();
    }

    private HungrySingleton(){
        if(hungrySingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }

    /**
     * 全局访问点
     * @return
     */
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }

    /**
     * ObjectInputStream 需要用到
     * @return
     */
    private Object readResolve(){
        return hungrySingleton;
    }

    /**
     *
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return getInstance();
    }
}

测试

        //序列化
        HungrySingleton instance = HungrySingleton.getInstance();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
        oos.writeObject(instance);

        //反序列化
        File file = new File("singleton_file");
        //需要用到 HungrySingleton 类的 readResolve() 方法 
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        HungrySingleton newInstance = (HungrySingleton) ois.readObject();
  
        System.out.println(instance);//序列化:HungrySingleton@9807454
        System.out.println(newInstance);//反序列化:HungrySingleton@9807454
        System.out.println(instance == newInstance);//true

反射攻击破坏单例模式的解决方案
 懒汉式模式下无法阻止反射攻击,反射可以拿到所有成员变量做修改。
 饿汉式可以避免方式攻击:通过阻止反射调用私有构造函数
HungrySingleton

public class HungrySingleton implements Serializable{

    private final static HungrySingleton hungrySingleton;

    //随类加载而初始化
    static{
        hungrySingleton = new HungrySingleton();
    }

    //阻止反射调用私有构造函数:阻止反射攻击
    private HungrySingleton(){
        if(hungrySingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }

    /**
     * 全局访问点
     * @return
     */
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }

    /**
     * ObjectInputStream 需要用到
     * @return
     */
    private Object readResolve(){
        return hungrySingleton;
    }
}

测试

        Class objectClass = HungrySingleton.class;
        Constructor constructor = objectClass.getDeclaredConstructor();
        constructor.setAccessible(true);

        HungrySingleton instance = HungrySingleton.getInstance();
        HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();

        System.out.println(instance);
        System.out.println(newInstance);
        System.out.println(instance == newInstance);

会抛出异常,防止反射攻击破坏单例模式

Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.geely.design.pattern.creational.singleton.Test.main(Test.java:65)
Caused by: java.lang.RuntimeException: 单例构造器禁止反射调用
	at com.geely.design.pattern.creational.singleton.HungrySingleton.<init>(HungrySingleton.java:17)
	... 5 more

枚举类(最佳实践:可以防止序列化和反射攻击)

public enum EnumInstance {
    INSTANCE{
        protected  void printTest(){
            System.out.println("Geely Print Test");
        }
    };
    protected abstract void printTest();
  
    private Object data;
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
  
    public static EnumInstance getInstance(){
        return INSTANCE;
    }
}

容器单例
ContainerSingleton

public class ContainerSingleton {

    private ContainerSingleton(){

    }
    private static Map<String,Object> singletonMap = new ConcurrentHashMap<String, Object>();

    public static void putInstance(String key,Object instance){
        if(StringUtils.isNotBlank(key) && instance != null){
            if(!singletonMap.containsKey(key)){
                singletonMap.put(key,instance);
            }
        }
    }

    public static Object getInstance(String key){
        return singletonMap.get(key);
    }
}

线程单例(不保证全局唯一,但是可以保证线程唯一)

同步锁:时间换空间
ThreadLocal:空间换时间(线程间隔离,每个线程都有一个实例,并不是真正意义上的单例)

ThreadLocalInstance

public class ThreadLocalInstance {
    private static final ThreadLocal<ThreadLocalInstance> threadLocalInstanceThreadLocal
             = new ThreadLocal<ThreadLocalInstance>(){
        @Override
        protected ThreadLocalInstance initialValue() {
            return new ThreadLocalInstance();
        }
    };
    private ThreadLocalInstance(){

    }

    public static ThreadLocalInstance getInstance(){
        return threadLocalInstanceThreadLocal.get();
    }

}

案例:RunTime、Desktop

原形模式

 原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
 不需要知道任何创建的细节,不调用构造函数。
 创建型

# 适用场景
* 类初始化消耗较多资源
* new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
* 构造函数比较复杂
* 循环体中生产大量对象时

# 优点
* 原型模式性能比直接new一个对象性能高(在内存中进行二进制流拷贝)
* 简化创建过程

# 缺点
* 必须配备克隆方法
* 对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
* 深拷贝、浅拷贝要运用得当

# 深克隆
	引用类型指向不同的对象(用于克隆引用类型的成员变量)

# 浅克隆
	可用于克隆基本类型的成员变量

Mail

public class Mail implements Cloneable{
    private String name;
    private String emailAddress;
    private String content;
    public Mail(){
        System.out.println("Mail Class Constructor");
    }

    public String getName() {
        return name;
    }

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

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "Mail{" +
                "name='" + name + '\'' +
                ", emailAddress='" + emailAddress + '\'' +
                ", content='" + content + '\'' +
                '}'+super.toString();
    }

    @Override
    protected Object clone() thro	ws CloneNotSupportedException {
        System.out.println("clone mail object");
        return super.clone();
    }
}

MailUtil

public class MailUtil {
    public static void sendMail(Mail mail){
        String outputContent = "向{0}同学,邮件地址:{1},邮件内容:{2}发送邮件成功";
        System.out.println(MessageFormat.format(outputContent,mail.getName(),mail.getEmailAddress(),mail.getContent()));
    }
    public static void saveOriginMailRecord(Mail mail){
        System.out.println("存储originMail记录,originMail:"+mail.getContent());
    }
}

Test

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        //假设 new 一个 Mail 对象非常复杂,非常消耗性能。
        Mail mail = new Mail();
        mail.setContent("初始化模板");
        System.out.println("初始化mail:"+mail);//初始化mail:Mail{name='null', emailAddress='null', content='初始化模板'}com.geely.design.pattern.creational.prototype.Mail@7530d0a

        for(int i = 0;i < 10;i++){
            //使用原始的mail进行clone
            Mail mailTemp = (Mail) mail.clone();
            mailTemp.setName("姓名"+i);
            mailTemp.setEmailAddress("姓名"+i+"@imooc.com");
            mailTemp.setContent("恭喜您,此次慕课网活动中奖了");
            MailUtil.sendMail(mailTemp);
            System.out.println("克隆的mailTemp:"+mailTemp);
        }

        MailUtil.saveOriginMailRecord(mail);//存储originMail记录,originMail:初始化模板
    }
}

深拷贝(默认是浅克隆)
Pig

public class Pig implements Cloneable{
    private String name;
    private Date birthday;

    public Pig(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Pig pig = (Pig)super.clone();

        //深克隆
        pig.birthday = (Date) pig.birthday.clone();
        return pig;
    }

    @Override
    public String toString() {
        return "Pig{" +
                "name='" + name + '\'' +
                ", birthday=" + birthday +
                '}'+super.toString();
    }
}

Test

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Date birthday = new Date(0L);
        Pig pig1 = new Pig("佩奇",birthday);
        Pig pig2 = (Pig) pig1.clone();
        System.out.println(pig1);
        System.out.println(pig2);

        pig1.getBirthday().setTime(666666666666L);

        System.out.println(pig1);//Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.geely.design.pattern.creational.prototype.clone.Pig@4fca772d
        System.out.println(pig2);//Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.geely.design.pattern.creational.prototype.clone.Pig@9807454
    }
}

案例:ArrayList、HashMap、CacheKey(Mybatis)

外观模式

 又叫门面模式,提供了一个统一的接口,用来访问子系统中的一群接口。
 外观模式定义一个高层接口,让子系统更容易使用。
 结构型

# 适用场景
* 子系统越来越复杂,增加外观模式提供简单调用接口
* 构建多层系统结构,利用外观对象作为每层的入口,简化层间调用

# 优点
* 简化了调用过程,无需了解深入子系统,防止带来风险。
* 减少系统依赖、松散耦合
* 更好的划分访问层次

# 缺点
* 增加子系统、扩展子系统行为容易引入风险
* 不符合开闭原则

# 外观-相关设计模式
* 外加模式和中介者模式
* 外观模式和单例模式
* 外观模式和抽象工厂模式

外观模式

PointsGift

public class PointsGift {
    private String name;

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

    public String getName() {
        return name;
    }
}

QualifyService

public class QualifyService {
    public boolean isAvailable(PointsGift pointsGift){
        System.out.println("校验"+pointsGift.getName()+" 积分资格通过,库存通过");
        return true;
    }
}

PointsPaymentService

public class PointsPaymentService {
    public boolean pay(PointsGift pointsGift){
        //扣减积分
        System.out.println("支付"+pointsGift.getName()+" 积分成功");
        return true;
    }
}

ShippingService

public class ShippingService {
    public String shipGift(PointsGift pointsGift){
        //物流系统的对接逻辑
        System.out.println(pointsGift.getName()+"进入物流系统");
        String shippingOrderNo = "666";
        return shippingOrderNo;
    }
}

GiftExchangeService

/**
 * 把多个子系统封装在一起,只对外提供一个外观(门面)类提供服务,调用者无需知道各个子系统的逻辑
 */
public class GiftExchangeService {
    private QualifyService qualifyService = new QualifyService();
    private PointsPaymentService pointsPaymentService = new PointsPaymentService();
    private ShippingService shippingService = new ShippingService();

    public void giftExchange(PointsGift pointsGift){
        if(qualifyService.isAvailable(pointsGift)){
            //资格校验通过
            if(pointsPaymentService.pay(pointsGift)){
                //如果支付积分成功
                String shippingOrderNo = shippingService.shipGift(pointsGift);
                System.out.println("物流系统下单成功,订单号是:"+shippingOrderNo);
            }
        }
    }
}

Test

public class Test {
    public static void main(String[] args) {
        PointsGift pointsGift = new PointsGift("T恤");
        GiftExchangeService giftExchangeService = new GiftExchangeService();
        giftExchangeService.giftExchange(pointsGift);
    }
}

测试结果

校验T恤 积分资格通过,库存通过
支付T恤 积分成功
T恤进入物流系统
物流系统下单成功,订单号是:666

案例:JdbcUtils(Spring)、Configuration(Mybatis)、RequestFacade(Tomcat)

装饰者

 在不改变原有对象的基础之上,将功能附加到对象上。

 提供了比继承更有弹性的替代方案(扩展原有对象功能)。

 结构型

# 使用场景
* 扩展一个类的功能或给一个类添加附加职责
* 动态的给一个对象添加功能,这些功能可以再动态的撤销

# 优点
* 继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能(动态扩展功能,运行期间决定增加的功能)
* 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果
* 符合开闭原则

# 缺点
* 会出现更多的代码,更多的类,增加程序复杂性
* 动态装饰时,多层装饰时会更复杂

# 关联其他的设计模式
* 装饰者模式、代理模式
* 装饰者模式、适配器模式

装饰者

ABattercake(被装饰者)

public abstract class ABattercake {
    protected abstract String getDesc();
    protected abstract int cost();
}

Battercake

public class Battercake extends ABattercake {
    @Override
    protected String getDesc() {
        return "煎饼";
    }

    @Override
    protected int cost() {
        return 8;
    }
}

AbstractDecorator(抽象装饰者)

public abstract class AbstractDecorator extends ABattercake {
    private ABattercake aBattercake;

    public AbstractDecorator(ABattercake aBattercake) {
        this.aBattercake = aBattercake;
    }

    protected abstract void doSomething();

    @Override
    protected String getDesc() {
        return this.aBattercake.getDesc();
    }

    @Override
    protected int cost() {
        return this.aBattercake.cost();
    }
}

EggDecorator(鸡蛋实体装饰类)

public class EggDecorator extends AbstractDecorator {
    public EggDecorator(ABattercake aBattercake) {
        super(aBattercake);
    }

    @Override
    protected void doSomething() {

    }

    @Override
    protected String getDesc() {
        return super.getDesc()+" 加一个鸡蛋";
    }

    @Override
    protected int cost() {
        return super.cost()+1;
    }
}

SausageDecorator(香肠实体装饰类)

public class SausageDecorator extends AbstractDecorator{
    public SausageDecorator(ABattercake aBattercake) {
        super(aBattercake);
    }

    @Override
    protected void doSomething() {

    }

    @Override
    protected String getDesc() {
        return super.getDesc()+" 加一根香肠";
    }

    @Override
    protected int cost() {
        return super.cost()+2;
    }
}

Test

public class Test {
    public static void main(String[] args) {
        ABattercake aBattercake;
        aBattercake = new Battercake();
        aBattercake = new EggDecorator(aBattercake);
        aBattercake = new EggDecorator(aBattercake);
        aBattercake = new SausageDecorator(aBattercake);
  
        System.out.println(aBattercake.getDesc()+" 销售价格:"+aBattercake.cost());
        //煎饼 加一个鸡蛋 加一个鸡蛋 加一根香肠 销售价格:12
    }
}

案例

* BufferedReader:Reader是一个抽象类,BufferedReader装饰Reader. 
* Servlet:SessionRepositoryRequestWrapper.

适配器

 将一个类的接口转换成客户期望的另一个接口

 使原本接口不兼容的类可以一起工作

 结构型

# 适用场景
* 已经存在的类,它的方法和需求不匹配时(方法结果相同或相似)
* 不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。

# 优点
* 能提高类的透明性和复用,现有的类复用但不需要改变
* 目标类和适配器类解耦,提高程序扩展性
* 符合开闭原则

# 缺点
* 适配器编写过程需要全面考虑,可能会增加系统的复杂性。
* 增加系统代码可读的难度

# 适配器-扩展
* 对象适配器
* 类适配器

方式一:类适配器模式(继承)
适配器模式

Adaptee

public class Adaptee {
    public void adapteeRequest(){
        System.out.println("被适配者的方法");
    }
}

Adapter

public class Adapter extends Adaptee implements Target{
    @Override
    public void request() {
        //...
        super.adapteeRequest();
        //...
    }
}

ConcreteTarget

public class ConcreteTarget implements Target {
    @Override
    public void request() {
        System.out.println("concreteTarget目标方法");
    }
}

Target

public interface Target {
    void request();
}

Test

public class Test {
    public static void main(String[] args) {
        Target target = new ConcreteTarget();
        target.request();//concreteTarget目标方法

        Target adapterTarget = new Adapter();
        adapterTarget.request();//被适配者的方法
    }
}

方式二:对象适配器模式(组合优先于继承)
创建对象

Adaptee

public class Adaptee {
    public void adapteeRequest(){
        System.out.println("被适配者的方法");
    }
}

Adapter

public class Adapter implements Target{
    private Adaptee adaptee = new Adaptee();

    @Override
    public void request() {
        //...
        adaptee.adapteeRequest();
        //...
    }
}

ConcreteTarget

public class ConcreteTarget implements Target {
    @Override
    public void request() {
        System.out.println("concreteTarget目标方法");
    }
}

Target

public interface Target {
    void request();
}

Test

public class Test {
    public static void main(String[] args) {
        Target target = new ConcreteTarget();
        target.request();//concreteTarget目标方法

        Target adapterTarget = new Adapter();
        adapterTarget.request();//被适配者的方法
    }
}

220交流电通过适配器变为直流电5V
AC220

public class AC220 {
    public int outputAC220V(){
        int output = 220;
        System.out.println("输出交流电"+output+"V");
        return output;
    }
}

DC5

public interface DC5 {
    int outputDC5V();
}

PowerAdapter

public class PowerAdapter implements DC5{
    private AC220 ac220 = new AC220();

    @Override
    public int outputDC5V() {
        int adapterInput = ac220.outputAC220V();
        //变压器...
        int adapterOutput = adapterInput/44;

        System.out.println("使用PowerAdapter输入AC:"+adapterInput+"V"+"输出DC:"+adapterOutput+"V");
        return adapterOutput;
    }
}

Test

public class Test {
    public static void main(String[] args) {
        DC5 dc5 = new PowerAdapter();
        dc5.outputDC5V();//使用PowerAdapter输入AC:220V输出DC:5V
    }
}

案例:XmlAdapter(XML序列化和反序列化)、JpaVendorAdapter

享元模式

 提高了减少对象数量(减少内存占用,提高性能),从而改善应用所需的对象结构的方式。
 运用共享技术有效地支持大量细粒度的对象。
 结构型。

# 适用场景
* 常常应用于系统底层的开发,以便解决系统的性能问题。
* 系统有大量相似对象、需要缓冲池的场景。

# 优点
* 减少对象的创建,降低内存中对象的数据量,降低系统的内存,提高效率。
* 减少内存之外的其他资源占用

# 缺点
* 关注内/外部状态、关注线程安全问题
* 使系统、程序的逻辑复杂化

# 扩展
* 内部状态:享元对象的一个属性,该属性不会随着外部的环境变化而变化。
* 外部状态:记录在享元对象的外部,不可以被共享的状态。

# 享元模式关联的其他模式
* 享元模式和代理模式
* 享元模式和单例模式

享元模式

Employee

public interface Employee {
    void report();
}

Manager

public class Manager implements Employee {
    @Override
    public void report() {
        System.out.println(reportContent);
    }
    private String title = "部门经理"; //内部状态
    private String department; //外部状态
    private String reportContent; 

    public void setReportContent(String reportContent) {
        this.reportContent = reportContent;
    }

    public Manager(String department) {
        this.department = department;
    }
}

EmployeeFactory

public class EmployeeFactory {
    private static final Map<String,Employee> EMPLOYEE_MAP = new HashMap<String,Employee>();

    public static Employee getManager(String department){
        Manager manager = (Manager) EMPLOYEE_MAP.get(department);

        if(manager == null){
            manager = new Manager(department);
            System.out.print("创建部门经理:"+department);
            String reportContent = department+"部门汇报:此次报告的主要内容是......";
            manager.setReportContent(reportContent);
            System.out.println(" 创建报告:"+reportContent);
            EMPLOYEE_MAP.put(department,manager);

        }
        return manager;
    }
}

Test

public class Test {
    //部门列表
    private static final String departments[] = {"RD","QA","PM","BD"};

    public static void main(String[] args) {
        for(int i=0; i<10; i++){
            String department = departments[(int)(Math.random() * departments.length)];
            Manager manager = (Manager) EmployeeFactory.getManager(department);
            manager.report();
        }
    }

}

控制台输出

创建部门经理:PM 创建报告:PM部门汇报:此次报告的主要内容是......
PM部门汇报:此次报告的主要内容是......
创建部门经理:BD 创建报告:BD部门汇报:此次报告的主要内容是......
BD部门汇报:此次报告的主要内容是......
PM部门汇报:此次报告的主要内容是......
BD部门汇报:此次报告的主要内容是......
创建部门经理:QA 创建报告:QA部门汇报:此次报告的主要内容是......
QA部门汇报:此次报告的主要内容是......
BD部门汇报:此次报告的主要内容是......
BD部门汇报:此次报告的主要内容是......
QA部门汇报:此次报告的主要内容是......
创建部门经理:RD 创建报告:RD部门汇报:此次报告的主要内容是......
RD部门汇报:此次报告的主要内容是......
QA部门汇报:此次报告的主要内容是......

案例:String、数据库连接池、Integer.valueOf(100)
 源码解析

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

 测试Integer.valueOf()

public class Test {

        Integer a = Integer.valueOf(100);
        Integer b = 100;

        Integer c = Integer.valueOf(1000);
        Integer d = 1000;

        System.out.println("a==b:"+(a==b));//a==b:true
        System.out.println("c==d:"+(c==d));//c==d:false
    }
}

组合模式

 将对象组合成树形结构以表示 “部分-整体” 的层次结构。

 组合模式使客户端对单个对象和组合对象保持一致的方式处理。

 结构型

组合-定义与类型

# 适用场景
* 希望客户端可以忽略组合对象与单个对象的差异时
* 处理一个树形结构时

# 优点
* 清楚地定义分层次的复杂对象,表示对象的全部或部分层次
* 让客户端忽略了层次的差异,方便对整个层次结构进行控制
* 简化客户端代码
* 符合开闭原则

# 缺点
* 限制类型时会比较复杂
* 使设计变得更加抽象

# 组合模式-相关设计模式
* 组合模式和访问者模式

组合模式

CatalogComponent

public abstract class CatalogComponent {

    //CourseCatalog
    public void add(CatalogComponent catalogComponent){
        throw new UnsupportedOperationException("不支持添加操作");
    }

    //CourseCatalog
    public void remove(CatalogComponent catalogComponent){
        throw new UnsupportedOperationException("不支持删除操作");
    }

    //Course / CourseCatalog
    public String getName(CatalogComponent catalogComponent){
        throw new UnsupportedOperationException("不支持获取名称操作");
    }

    //Course
    public double getPrice(CatalogComponent catalogComponent){
        throw new UnsupportedOperationException("不支持获取价格操作");
    }
  
    //Course
    public void print(){
        throw new UnsupportedOperationException("不支持打印操作");
    }
}

Course

public class Course extends CatalogComponent {

    private String name;
    private double price;

    public Course(String name, double price) {
        this.name = name;

        this.price = price;
    }

    @Override
    public String getName(CatalogComponent catalogComponent) {
        return this.name;
    }

    @Override
    public double getPrice(CatalogComponent catalogComponent) {
        return this.price;
    }

    @Override
    public void print() {
        System.out.println("Course Name:"+name+" Price:"+price);
    }
}

CourseCatalog

public class CourseCatalog extends CatalogComponent {
    private List<CatalogComponent> items = new ArrayList<CatalogComponent>();
    private String name;
    private Integer level;

    public CourseCatalog(String name,Integer level) {
        this.name = name;
        this.level = level;
    }

    @Override
    public void add(CatalogComponent catalogComponent) {
        items.add(catalogComponent);
    }

    @Override
    public String getName(CatalogComponent catalogComponent) {
        return this.name;
    }

    @Override
    public void remove(CatalogComponent catalogComponent) {
        items.remove(catalogComponent);
    }

    @Override
    public void print() {
        System.out.println(this.name);
        for(CatalogComponent catalogComponent : items){
            if(this.level != null){
                for(int  i = 0; i < this.level; i++){
                    System.out.print("  ");
                }
            }
            catalogComponent.print();
        }
    }

}

控制台输出


慕课网课程主目录
  Course Name:Linux课程 Price:11.0
  Course Name:Windows课程 Price:11.0
  Java课程目录
    Course Name:Java电商一期 Price:55.0
    Course Name:Java电商二期 Price:66.0
    Course Name:Java设计模式 Price:77.0 

案例:awt.Container、HashMap.putAll()、ArrayList.addAll()

桥接模式

 将抽象部分与它的具体实现部分分离,使它们都可以独立地变化(一定程度上实现解耦)。

 通过组合的方式建立两个类之间联系,而不是继承。

 结构型

# 适用场景
* 抽象和具体实现之间增加更多的灵活性。
* 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。
* 不希望使用继承,或因为多层继承导致系统类的个数剧增。

# 优点
* 分离抽象部分及其具体实现部分
* 提高了系统的可扩展性
* 符合开闭原则
* 符合合成复用原则
* 组合优于继承

# 缺点
* 增加系统的理解与设计难度
* 需要正确地识别出系统中两个独立变化的维度

# 桥接 - 相关设计模式
* 桥接模式和组合模式
* 桥接模式和适配器模式

桥接

Account

public interface Account {
    Account openAccount();
    void showAccountType();
}

DepositAccount

public class DepositAccount implements Account {
    @Override
    public Account openAccount() {
        System.out.println("打开定期账号");
        return new DepositAccount();
    }

    @Override
    public void showAccountType() {
        System.out.println("这是一个定期账号");
    }
}

SavingAccount

public class SavingAccount implements Account {
    @Override
    public Account openAccount() {
        System.out.println("打开活期账号");
        //...
        return new SavingAccount();
    }

    @Override
    public void showAccountType() {
        System.out.println("这是一个活期账号");
    }
}

Bank

public abstract class Bank {
    protected Account account;
    public Bank(Account account){
        this.account = account;
    }
    abstract Account openAccount();
}

ABCBank

public class ABCBank extends Bank {
    public ABCBank(Account account) {
        super(account);
    }

    @Override
    Account openAccount() {
        System.out.println("打开中国农业银行账号");
        account.openAccount();//委托业务实现
        return account;
    }
}

ICBCBank

public class ICBCBank extends Bank {
    public ICBCBank(Account account) {
        super(account);
    }

    @Override
    Account openAccount() {
        System.out.println("打开中国工商银行账号");
        account.openAccount();//委托业务实现
        return account;
    }
}

Test

public class Test {
    public static void main(String[] args) {
        Bank icbcBank = new ICBCBank(new DepositAccount());
        Account icbcAccount = icbcBank.openAccount();
        icbcAccount.showAccountType();

        Bank icbcBank2 = new ICBCBank(new SavingAccount());
        Account icbcAccount2 = icbcBank2.openAccount();
        icbcAccount2.showAccountType();

        Bank abcBank = new ABCBank(new SavingAccount());
        Account abcAccount = abcBank.openAccount();
        abcAccount.showAccountType();
    }
}

控制台输出

打开中国工商银行账号
打开定期账号
这是一个定期账号

打开中国工商银行账号
打开活期账号
这是一个活期账号

打开中国农业银行账号
打开活期账号
这是一个活期账号

案例:Driver(JDBC,mysql、oracle都实现了 Driver 接口)、

代理模式

 为其他对象提供一种代理,以控制对这个对象的访问。
 代理对象在客户端和目标对象之间起到中介的作用。
 结构型

# 适用场景
* 保护目标对象(客户端无需于目标对象直接接触,而是和代理对象直接接触)
* 增强目标对象

# 优点
* 代理模式能将代理对象与真实被调用的目标对象分离
* 一定程度上降低了系统的耦合度,扩展性好
* 保护目标对象
* 增强目标对象

# 缺点
* 会造成系统设计中类的数目增加
* 在客户端和目标对象之间引入代理对象,会造成请求处理速度变慢
* 增加系统的复杂度

# 扩展
* 静态代理
* 动态代理(JDK动态代理只支持实现接口的类、CGLib可以代理类,但不支持final修饰的类或方法)

# Spring 代理选择
* 当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理
* 当 Bean 没有实现接口时,Spring 使用 CGlib
* 可以强制使用 CGlib
	在 spring 配置中加入 <aop:aspectj-autoproxy proxy-target-class="true"/>

# 代理速度对比
* CGLib:底层通过ASM字节码生成,性能高于JAVA反射。
* JDK动态代理:JAVA反射。
* JDK8 的 JDK动态代理 性能大约比 CGLib 快20%左右。

# 代理 - 相关设计模式
* 代理模式和装饰模式
* 代理模式和适配器模式

作者:Soulboy