设计模式:03-原型模式 / 建造者模式(生成器模式)

本篇博客主要是学习 韩顺平_Java设计模式 做一个学习笔记使用

5. 原型模式

基本介绍

  1. 原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
  2. 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
  3. 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它 们自己来实施创建,即 对象.clone()
  4. 形象的理解:孙大圣拔出猴毛, 变出其它孙大圣

5.1. 问题的引入

/**
 * @author houyu
 * @createTime 2019/11/18 20:38
 */
public class Demo1 {

    /**
     * 定义个类 Person
     */
    public static class Person {
        private int age;
        private String name;

        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public String getName() {
            return name;
        }
        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("Person{");
            sb.append("age=").append(age);
            sb.append(", name='").append(name).append('\'');
            sb.append('}');
            return sb.toString();
        }
    }

    public static void main(String[] args) {
        // 问题的引入:
        // 需要拷贝 Person 5份
        Person original = new Person(10, "tom");
        //
        Person person1 = new Person(original.getAge(), original.getName());
        Person person2 = new Person(original.getAge(), original.getName());
        Person person3 = new Person(original.getAge(), original.getName());
        Person person4 = new Person(original.getAge(), original.getName());
        Person person5 = new Person(original.getAge(), original.getName());
        //
        System.out.println(person1);
        System.out.println(person2);
        System.out.println(person3);
        System.out.println(person4);
        System.out.println(person5);
        /**
         * 分析问题:
         * 传统的方式的优缺点
         *
         * 1) 优点是比较好理解,简单易操作。
         * 2) 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
         * 3) 总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活
         * 4) 改进的思路分析
         */
    }
}

5.2 原型模式的浅拷贝

使用默认的原型模式实现的是对象的浅拷贝
什么是浅拷贝?
这个对象的类属性, 如果是基本类型 boolean int long double float char byte short 等类似会进行值传递, 如果是String(final) 也会> 进行值传递
如果是引用类型, 那就就引用拷贝, 比如 List Map Array(int[]…)

  1. 实现接口 implements Cloneable
  2. 重写方法: @Override protected Object clone(){}
  3. 调用端调用clone()即可

代码

import java.util.Arrays;

/**
 * @author houyu
 * @createTime 2019/11/18 20:38
 */
public class Demo2 {

    /**
     * 定义个类 Person
     */
    public static class Person implements Cloneable {
        private int age;
        private String name;
        private int[] array = {1, 2};

        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public String getName() {
            return name;
        }
        public int[] getArray() {
            return array;
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("Person{");
            sb.append("age=").append(age);
            sb.append(", name='").append(name).append('\'');
            sb.append(", array=").append(Arrays.toString(array));
            sb.append('}');
            return sb.toString();
        }

        @Override
        protected Object clone() {
            try {
                return super.clone();
            } catch(CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    public static void main(String[] args) {
        // 问题的引入:
        // 需要拷贝 Person 5份
        Person original = new Person(10, "tom");
        //
        Person person1 = (Person) original.clone();
        Person person2 = (Person) original.clone();
        Person person3 = (Person) original.clone();
        Person person4 = (Person) original.clone();
        Person person5 = (Person) original.clone();
        //
        System.out.println(person1 == original);
        System.out.println(person2 == original);
        System.out.println(person3.getArray() == original.getArray());
        System.out.println(person4);
        System.out.println(person5);
        /*
         * false
         * false
         * true
         * Person{age=10, name='tom', array=[1, 2]}
         * Person{age=10, name='tom', array=[1, 2]}
         */
        //
        /**
         * 注意点:
         *
         * 1. 使用默认的原型模式实现的是对象的浅拷贝
         *
         * 什么是浅拷贝?
         * 这个对象的类属性, 如果是基本类型 boolean int long double float char byte short 等类似会进行值传递, 如果是String(final) 也会进行值传递
         *
         * 如果是引用类型, 那就就引用拷贝, 比如 List Map Array(int[]...)
         *
         */
    }
}

5.3 重写默认原型模式实现深拷贝

注意点:

  • 使用默认的原型模式close,会导致引用类型没有深拷贝,引用是同一个,因此可能会导致其他问题,如:线程安全等。。。
  • 改进方案1(不推荐):在原来的clone基础之上,手动一个一个添加引用类型的clone
  • 改进方案2(推荐):使用流写入写出进行克隆(ObjectOutputStream,ObjectInputStream)
  • 改进方案3(推荐):使用JSON序列化工具进行克隆(fastjson,gson,jackson…)
  1. 实现接口 implements Cloneable, Serializable
  2. 重写方法: @Override protected Object clone(){},使用 ObjectOutputStream ObjectInputStream 实现
  3. 调用端调用clone()即可
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;

/**
 * @author houyu
 * @createTime 2019/11/18 20:38
 */
public class Demo3 {

    /**
     * 定义个类 Person
     */
    public static class Person implements Cloneable, Serializable {
        private int age;
        private String name;
        private int[] array = {1, 2};

        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public String getName() {
            return name;
        }
        public int[] getArray() {
            return array;
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("Person{");
            sb.append("age=").append(age);
            sb.append(", name='").append(name).append('\'');
            sb.append(", array=").append(Arrays.toString(array));
            sb.append('}');
            return sb.toString();
        }

        @Override
        protected Object clone() {
            ObjectOutputStream objectOutputStream = null;
            ByteArrayOutputStream byteArrayOutputStream = null;
            ObjectInputStream objectInputStream = null;
            ByteArrayInputStream byteArrayInputStream = null;
            try {
                byteArrayOutputStream = new ByteArrayOutputStream();
                objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
                objectOutputStream.writeObject(this);
                byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                objectInputStream = new ObjectInputStream(byteArrayInputStream);
                return objectInputStream.readObject();
            } catch(IOException | ClassNotFoundException e) {
                e.printStackTrace();
            } finally {
                close(objectOutputStream, byteArrayOutputStream, objectInputStream, byteArrayInputStream);
            }
            return null;
        }

        private void close(Closeable... closeables) {
            for(Closeable closeable : closeables) {
                if(closeable == null) {
                    continue;
                }
                try { closeable.close(); } catch(IOException e) {}
            }
        }
    }

    public static void main(String[] args) {
        // 问题的引入:
        // 需要拷贝 Person 5份
        Person original = new Person(10, "tom");
        //
        Person person1 = (Person) original.clone();
        Person person2 = (Person) original.clone();
        Person person3 = (Person) original.clone();
        Person person4 = (Person) original.clone();
        Person person5 = (Person) original.clone();
        //
        System.out.println(person1 == original);
        System.out.println(person2 == original);
        System.out.println(person3.getArray() == original.getArray());
        System.out.println(person4);
        System.out.println(person5);
        /*
         * false
         * false
         * true
         * Person{age=10, name='tom', array=[1, 2]}
         * Person{age=10, name='tom', array=[1, 2]}
         */
        //
        /**
         * 注意点:
         *
         * 1. 必须需要实现Serializable
         *
         */
    }
}

5.4 原型模式在 Spring 框架中源码分析(getBean)

// 1.0 org.springframework.context.support.AbstractApplicationContext
public Object getBean(String name) throws BeansException {
	assertBeanFactoryActive();
	return getBeanFactory().getBean(name);
}

// 2.0 org.springframework.beans.factory.support.AbstractBeanFactory
public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}

// 3.0 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean()
// Create bean instance.
if (mbd.isSingleton()) {
	sharedInstance = getSingleton(beanName, () -> {
		try {
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			// Explicitly remove instance from singleton cache: It might have been put there
			// eagerly by the creation process, to allow for circular reference resolution.
			// Also remove any beans that received a temporary reference to the bean.
			destroySingleton(beanName);
			throw ex;
		}
	});
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

else if (mbd.isPrototype()) {
	// It's a prototype -> create a new instance.
	Object prototypeInstance = null;
	try {
		beforePrototypeCreation(beanName);
		prototypeInstance = createBean(beanName, mbd, args);
	}
	finally {
		afterPrototypeCreation(beanName);
	}
	bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

else {
	String scopeName = mbd.getScope();
	final Scope scope = this.scopes.get(scopeName);
	if (scope == null) {
		throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
	}
	try {
		Object scopedInstance = scope.get(beanName, () -> {
			beforePrototypeCreation(beanName);
			try {
				return createBean(beanName, mbd, args);
			}
			finally {
				afterPrototypeCreation(beanName);
			}
		});
		bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
	}
	catch (IllegalStateException ex) {
		throw new BeanCreationException(beanName,
				"Scope '" + scopeName + "' is not active for the current thread; consider " +
				"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
				ex);
	}
}

6. 建造者模式(生成器模式)

基本介绍

  1. 建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出 来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
  2. 建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们, 用户不需要知道内部的具体构建细节。

6.1 建造者模式的四个角色

  1. Product(产品角色): 一个具体的产品对象。
  2. Builder(抽象建造者): 创建一个 Product 对象的各个部件指定的 接口/抽象类。
  3. ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
  4. Director(指挥者): 构建一个使用 Builder 接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作 用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。

建造者模式解决盖房需求应用实例

/**
 * @author houyu
 * @createTime 2019/11/18 20:00
 */
public class Demo1 {

    /**
     * 产品: 房屋
     */
    public static class House {
        private String wall;
        private String houseTop;

        public String getWall() {
            return wall;
        }

        public House setWall(String wall) {
            this.wall = wall;
            return this;
        }

        public String getHouseTop() {
            return houseTop;
        }

        public House setHouseTop(String houseTop) {
            this.houseTop = houseTop;
            return this;
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("House{");
            sb.append(", wall='").append(wall).append('\'');
            sb.append(", houseTop='").append(houseTop).append('\'');
            sb.append('}');
            return sb.toString();
        }
    }

    /**
     * 抽象建造者: 房屋抽象构建者
     *
     * interface / abstract class
     */
    public interface HouseBuilder {
        /**
         * 做墙
         */
        void makeWall();

        /**
         * 做房顶
         */
        void makeHourTop();

        /**
         * 制作完成房子
         */
        House buildHouse();
    }


    /**
     * 具体构建者: 我的房屋构建者
     */
    public static class MyHouseBuilder extends House implements HouseBuilder {
        House house = new House();

        @Override
        public void makeWall() {
            house.setWall("MyHouseBuilder.makeWall");
        }
        @Override
        public void makeHourTop() {
            house.setHouseTop("MyHouseBuilder.makeHourTop");
        }

        @Override
        public House buildHouse() {
            /**
             * 由 具体构建者 进行构建
             */
            makeWall();
            makeHourTop();
            return house;
        }
    }

    /**
     * 指挥者: 我自己
     */
    public static class My {

        private HouseBuilder builder;

        public My setBuilder(HouseBuilder builder) {
            this.builder = builder;
            return this;
        }

        public House build() {
            return builder.buildHouse();
        }
    }

    public static void main(String[] args) {
        // 创建具体构建者
        HouseBuilder builder = new MyHouseBuilder();
        // 创建指挥者
        My my = new My();
        // 给指挥者传递构建者
        my.setBuilder(builder);
        // 指挥者调用构建者的具体流程构建完成返回房子
        House build = my.build();
        // 输出房子
        System.out.println("build.toString() = " + build.toString());
        /**
         * 这个案例的构建者模式是比较标准的, 很明确的存在4个角色
         *
         * 在很多源码中的构建者模式对于这4个角色, 分界比较模糊, 有些类担任双重角色也是有可能的, 如StringBuilder
         * 也有些构建者模式没有完全存在这4个角色, 有的只有三个角色也有可能的...
         */
    }
}

说明:
在很多源码中的构建者模式对于这4个角色, 分界比较模糊, 有些类担任双重角色也是有可能的, 如StringBuilder
也有些构建者模式没有完全存在这4个角色, 有的只有三个角色也有可能的…

6.2 构造者模式在 JDK 框架中源码分析(StringBuilder)

// 产品
public final class String implements java.io.Serializable, Comparable<String>, CharSequence, Constable, ConstantDesc {
}

// 抽象构建者
public interface Appendable {
	// ...
	Appendable append(CharSequence csq) throws IOException;
}

// 具体建造者 (AbstractStringBuilder 实现了 Appendable 接口方法,这里的 AbstractStringBuilder 已经是建造者,只是不能 实例化)
abstract class AbstractStringBuilder implements Appendable, CharSequence {
	// ...
    public AbstractStringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }
}

// StringBuilder 即充当了指挥者角色,同时充当了具体的建造者,建造方法的实现是由 AbstractStringBuilder 完成, 而 StringBuilder 继承了 AbstractStringBuilder
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, Comparable<StringBuilder>, CharSequence {
	// ...
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }
}

6.3 构造者模式在 lombok 中的应用

6.3.1编写实体类

import lombok.Builder;

@Builder
public class Friend {
    private String name;
    private Integer age;
}

6.3.2编写使用类

public class Test {
    public static void main(String[] args) throws Exception {
	    // 下面一行代码充当了 指挥者 和 具体建造者
        Friend friend = Friend.builder().age(10).name("张三").build();
        System.out.println("friend = " + friend);
    }
}

6.3.3反编译 Friend 的构建者

public class Friend {
    private String name;
    private Integer age;

    Friend(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public static Friend.FriendBuilder builder() {
        return new Friend.FriendBuilder();
    }

	// 这里没有抽象构建者, 只有具体构建者, 因此构建者模式不一定说一定要存在4个角色的
    public static class FriendBuilder {
        private String name;
        private Integer age;

        FriendBuilder() {
        }

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

        public Friend.FriendBuilder age(Integer age) {
            this.age = age;
            return this;
        }

        public Friend build() {
            return new Friend(this.name, this.age);
        }

        public String toString() {
            return "Friend.FriendBuilder(name=" + this.name + ", age=" + this.age + ")";
        }
    }
}

6.4 建造者模式的注意事项和细节

  1. 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可 以创建不同的产品对象
  2. 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具 体建造者, 用户使用不同的具体建造者即可得到不同的产品对象
  3. 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰, 也更方便使用程序来控制创建过程
  4. 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭 原则”
  5. 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使 用建造者模式,因此其使用范围受到一定的限制。
  6. 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因 此在这种情况下,要考虑是否选择建造者模式.
  7. 抽象工厂模式 VS 建造者模式
    抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采 用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定 的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品
发布了46 篇原创文章 · 获赞 13 · 访问量 2万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览