Java设计模式之五大创建型模式

  • 设计模式(23种)
  • 单例模式(Singleton Pattern)
    • 参考链接
    • 概念
    • 使用场景
    • 实现思路
    • 实现方式
      • 饿汉式(静态常量)
      • 饿汉式(静态代码块)
      • 懒汉式(线程不安全)
      • 懒汉式(同步方法)
      • 懒汉式(同步代码块)
      • 双重检查
      • 静态内部类
      • 枚举
    • JDK应用源码分析
    • 注意事项
    • 使用场景
  • 工厂方法模式(Factory method Pattern)
    • 参考链接
    • 简单工厂模式
    • 概念
    • 使用场景
    • 实现思路
    • 实现方式
  • 抽象工厂模式(Abstract Factory Pattern)
    • 参考链接
    • 概念
    • 使用场景
    • 实现思路
    • 实现方式
    • 注意事项
  • 建造者模式(Builder Pattern)
    • 参考链接
    • 概念
    • 使用场景
    • 角色分类
    • 实现方式
      • 传统方式
      • 建造者方式
    • JDK源码应用分析
    • 注意事项
  • 原型模式(Prototype Pattern)
    • 参考链接
    • 概念
    • 原理
    • 实现思路
    • 实现方式
    • JDK源码应用分析
    • 浅拷贝与深拷贝
      • 浅拷贝
      • 深拷贝
      • 案例代码
    • 注意事项

设计模式(23种)

  • 介绍

    设计模式是程序员在面对同类软件工程设计问题所总结出来的经验,模式不是代码,而是某类问题的通用解决方法,代表了最佳实践

  • 经典书籍

    《设计模式》–Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides Design(俗称"四人组GOF")

  • 最常用6种设计模式

    1. 创建型模式:单例模式、工厂方法模式

    2. 结构型模式:装饰模式、代理模式

    3. 行为型模式:观察者模式

单例模式(Singleton Pattern)

参考链接

https://mp.weixin.qq.com/s/aJSf8yESPeex78S6W0FWWw

概念

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例该类只提供一个取得其对象实例的方法

使用场景

  • SessionFactory

    比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个SessionFactory就足够。

实现思路

  1. 构造器私有化
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法

实现方式

饿汉式(静态常量)

  • 案例代码

    class Singleton {//1. 构造器私有化, 外部不能newprivate Singleton() {}//2.本类内部创建对象实例private final static Singleton instance = new Singleton();//3. 提供一个公有的静态方法,返回实例对象public static Singleton getInstance() {return instance;}}
  • 优缺点

    优点
    写法简单,就是在类装载的时候就完成实例化。避免了线程同步问题

    缺点
    在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存浪费

  • 总结

    单例模式可用,可能造成内场浪费

饿汉式(静态代码块)

  • 案例代码

    class Singleton {//1. 构造器私有化, 外部能newprivate Singleton() {}//2.本类内部创建对象实例private static Singleton instance;static { // 在静态代码块中,创建单例对象instance = new Singleton();}//3. 提供一个公有的静态方法,返回实例对象public static Singleton getInstance() {return instance;}}

懒汉式(线程不安全)

  • 案例代码

    class Singleton {private static Singleton instance;private Singleton() {}//提供一个静态的公有方法,当使用到该方法时,才去创建 instance//即懒汉式public static Singleton getInstance() {if(instance == null) {instance = new Singleton();}return instance;}}
  • 优缺点

    优点
    起到了Lazy Loading的效果,但是只能在单线程下使用

    缺点

    如果在多线程下,一个线程进入了if(singleton == null)判断语句块,还未往下执行,另外一个线程也通过这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式
    
  • 总结
    在实际开发中,不要使用这种方式

懒汉式(同步方法)

  • 案例代码

    class Singleton {private static Singleton instance;private Singleton() {}//提供一个静态的公有方法,加入了同步处理的代码,解决线程安全问题//即懒汉式public static synchronized Singleton getInstance() {if(instance == null) {instance = new Singleton();}return instance;}}
  • 优缺点

    优点
    解决了线程不安全问题

    缺点

    效率太低,每个线程在想获得类的实例的时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。

  • 总结
    效率低下,实际开发中不建议使用

懒汉式(同步代码块)

  • 案例代码

    class Singleton {private static Singleton instance;private Singleton() {}//即懒汉式 同步代码块public static Singleton getInstance() {if(instance == null) {synchronized (Singleton.class){instance = new Singleton();}}return instance;}}
  • 总结

    没有解决线程安全问题,且效率低下,实际开发中禁止使用

双重检查

  • 案例代码

    class Singleton {//volatile防止指令重排,将实例强制刷到主存private static volatile Singleton instance;private Singleton() {}//双重检查public static Singleton getInstance() {if(instance == null) {synchronized (Singleton.class){if (instance == null) {instance = new Singleton();}}}return instance;}}
  • 优缺点

    优点

    1. Double-Check概念是多线程开发中常使用到的,如代码中所示,进行了两次if(singleton == null)检查,这样可以保证线程安全

    2. 实例化代码只执行一次,后面再次访问时,判断if(singleton == null),直接return实例化对象,也避免反复进行方法同步

    3. 线程安全,延迟加载,效率较高

  • 总结

    实际开发中,推荐使用这种单例设计模式

静态内部类

  • 案例代码

    class Singleton {private Singleton(){}/*** @Author charlesYan* @Description 1. 初始加载类Singleton的时候不会加载静态内部类SingletonInstance*              2. 当加载getInstance方法时,才会加载SingletonInstance,而且只会装载1次,且不会线程安全问题* @Date 14:16 2020/3/3* @Param* @return**/private static class SingletonInstance{private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance(){return SingletonInstance.INSTANCE;}}
  • 优缺点

    优点

    1. 采用类装载的机制来保证初始化实例时只有一个线程
    2. 静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化
    3. 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的

    避免了线程不安全,利用静态内部类特点实现延迟加载,效率高

    缺点

  • 总结

    推荐使用

枚举

  • 案例代码

    enum Singleton {INSTANCE;//属性public void response(){System.out.println("调用成功!");}}
  • 优缺点

    优点
    借助JDK1.5添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象

    缺点

  • 总结
    Effective Java 作者Josh Bloch提倡的方式

JDK应用源码分析

  • java.lang.Runtime

    public class Runtime {private static Runtime currentRuntime = new Runtime();/*** Returns the runtime object associated with the current Java application.* Most of the methods of class <code>Runtime</code> are instance* methods and must be invoked with respect to the current runtime object.** @return  the <code>Runtime</code> object associated with the current*          Java application.*/public static Runtime getRuntime() {return currentRuntime;}/** Don't let anyone else instantiate this class */private Runtime() {}}
  • 分析说明

    采用的是饿汉式(静态常量)方式实现,因为肯定会使用,所有不会造成内存浪费

注意事项

  1. 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能

  2. 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new

使用场景

  1. 需要频繁进行创建和销毁的对象
  2. 创建对象耗时过多或者耗费资源过多(重量级对象),但又经常用到的对象
  3. 工具类对象、频繁访问数据库或文件的对象(数据源、session工厂等)

工厂方法模式(Factory method Pattern)

参考链接

https://mp.weixin.qq.com/s/12fLnRxKCYABSItkKvU8Fw

简单工厂模式

  • 概念
    简单工厂模式也叫做静态工厂模式,是由一个工厂对象决定创建出哪一种产品类的实例

  • 实现思路

    定义一个创建对象的类,由这个类来封装实例化对象的行为

  • 类图

  • 案例代码

    //1. 将Pizza 类做成抽象public abstract class Pizza {protected String name; //名字//准备原材料, 不同的披萨不一样,因此,我们做成抽象方法public abstract void prepare();public void bake() {System.out.println(name + " baking;");}public void cut() {System.out.println(name + " cutting;");}//打包public void box() {System.out.println(name + " boxing;");}public void setName(String name) {this.name = name;}}//2. 具体Pizza类public class GreekPizza extends Pizza {@Overridepublic void prepare() {// TODO Auto-generated method stubSystem.out.println(" 给希腊披萨 准备原材料 ");}}//3. 简单工厂类public class SimpleFactory {//更加orderType 返回对应的Pizza 对象public Pizza createPizza(String orderType) {Pizza pizza = null;System.out.println("使用简单工厂模式");if (orderType.equals("greek")) {pizza = new GreekPizza();pizza.setName(" 希腊披萨 ");} else if (orderType.equals("cheese")) {pizza = new CheesePizza();pizza.setName(" 奶酪披萨 ");} else if (orderType.equals("pepper")) {pizza = new PepperPizza();pizza.setName("胡椒披萨");}return pizza;}}//4. 订购pizza类public class OrderPizza {// 构造器//  public OrderPizza() {//     Pizza pizza = null;//      String orderType; // 订购披萨的类型//      do {//          orderType = getType();//           if (orderType.equals("greek")) {//                pizza = new GreekPizza();//                pizza.setName(" 希腊披萨 ");//            } else if (orderType.equals("cheese")) {//                pizza = new CheesePizza();//               pizza.setName(" 奶酪披萨 ");//            } else if (orderType.equals("pepper")) {//                pizza = new PepperPizza();//               pizza.setName("胡椒披萨");//          } else {//              break;//            }//         //输出pizza 制作过程//            pizza.prepare();//          pizza.bake();//         pizza.cut();//          pizza.box();//          //      } while (true);//   }//定义一个简单工厂对象SimpleFactory simpleFactory;Pizza pizza = null;//构造器public OrderPizza(SimpleFactory simpleFactory) {setFactory(simpleFactory);}public void setFactory(SimpleFactory simpleFactory) {String orderType = ""; //用户输入的this.simpleFactory = simpleFactory; //设置简单工厂对象do {orderType = getType(); pizza = this.simpleFactory.createPizza(orderType);//输出pizzaif(pizza != null) { //订购成功pizza.prepare();pizza.bake();pizza.cut();pizza.box();} else {System.out.println(" 订购披萨失败 ");break;}}while(true);}// 写一个方法,可以获取客户希望订购的披萨种类private String getType() {try {BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));System.out.println("input pizza 种类:");String str = strin.readLine();return str;} catch (IOException e) {e.printStackTrace();return "";}}}//相当于一个客户端,发出订购public class PizzaStore {public static void main(String[] args) {// TODO Auto-generated method stub//new OrderPizza();//使用简单工厂模式new OrderPizza(new SimpleFactory());System.out.println("~~退出程序~~");}}
  • 优点
    比较好理解,简单易操作

  • 缺点
    违反了设计模式的开闭原则(ocp),即对扩展开发,对修改关闭。即当我们给类增加新的功能的时候,尽量不修改代码,或者尽可能少修改代码

  • JDK源码应用分析

    1. JDK-Calendar
    private static Calendar createCalendar(TimeZone zone,Locale aLocale){CalendarProvider provider =LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale).getCalendarProvider();if (provider != null) {try {return provider.getInstance(zone, aLocale);} catch (IllegalArgumentException iae) {// fall back to the default instantiation}}Calendar cal = null;if (aLocale.hasExtensions()) {String caltype = aLocale.getUnicodeLocaleType("ca");if (caltype != null) {switch (caltype) {case "buddhist":cal = new BuddhistCalendar(zone, aLocale);break;case "japanese":cal = new JapaneseImperialCalendar(zone, aLocale);break;case "gregory":cal = new GregorianCalendar(zone, aLocale);break;}}}if (cal == null) {// If no known calendar type is explicitly specified,// perform the traditional way to create a Calendar:// create a BuddhistCalendar for th_TH locale,// a JapaneseImperialCalendar for ja_JP_JP locale, or// a GregorianCalendar for any other locales.// NOTE: The language, country and variant strings are interned.if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {cal = new BuddhistCalendar(zone, aLocale);} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"&& aLocale.getCountry() == "JP") {cal = new JapaneseImperialCalendar(zone, aLocale);} else {cal = new GregorianCalendar(zone, aLocale);}}return cal;}

概念

定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类

使用场景

  • 需求一
    披萨项目:客户在点披萨时,可以点不同口味的披萨,比如北京的奶酪pizza、北京的胡椒pizza或者是伦敦的奶酪pizza、伦敦的胡椒pizza

实现思路

  • 方案一
    将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现

  • 类图

实现方式

  • 案例代码

    //将Pizza 类做成抽象public abstract class Pizza {protected String name; //名字//准备原材料, 不同的披萨不一样,因此,我们做成抽象方法public abstract void prepare();public void bake() {System.out.println(name + " baking;");}public void cut() {System.out.println(name + " cutting;");}//打包public void box() {System.out.println(name + " boxing;");}public void setName(String name) {this.name = name;}}//北京奶酪pizzapublic class BJCheesePizza extends Pizza {@Overridepublic void prepare() {// TODO Auto-generated method stubsetName("北京的奶酪pizza");System.out.println(" 北京的奶酪pizza 准备原材料");}}//伦敦胡椒pizzapublic class LDPepperPizza extends Pizza{@Overridepublic void prepare() {// TODO Auto-generated method stubsetName("伦敦的胡椒pizza");System.out.println(" 伦敦的胡椒pizza 准备原材料");}}//订购pizza类public abstract class OrderPizza {//定义一个抽象方法,createPizza , 让各个工厂子类自己实现abstract Pizza createPizza(String orderType);// 构造器public OrderPizza() {Pizza pizza = null;String orderType; // 订购披萨的类型do {orderType = getType();pizza = createPizza(orderType); //抽象方法,由工厂子类完成//输出pizza 制作过程pizza.prepare();pizza.bake();pizza.cut();pizza.box();} while (true);}// 写一个方法,可以获取客户希望订购的披萨种类private String getType() {try {BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));System.out.println("input pizza 种类:");String str = strin.readLine();return str;} catch (IOException e) {e.printStackTrace();return "";}}}//北京订购pizzapublic class BJOrderPizza extends OrderPizza {@OverridePizza createPizza(String orderType) {Pizza pizza = null;if(orderType.equals("cheese")) {pizza = new BJCheesePizza();} else if (orderType.equals("pepper")) {pizza = new BJPepperPizza();}// TODO Auto-generated method stubreturn pizza;}}//伦敦订购pizzapublic class LDOrderPizza extends OrderPizza {@OverridePizza createPizza(String orderType) {Pizza pizza = null;if(orderType.equals("cheese")) {pizza = new LDCheesePizza();} else if (orderType.equals("pepper")) {pizza = new LDPepperPizza();}// TODO Auto-generated method stubreturn pizza;}}//pizza商店测试类public class PizzaStore {public static void main(String[] args) {String loc = "bj";if (loc.equals("bj")) {//创建北京口味的各种Pizzanew BJOrderPizza();} else {//创建伦敦口味的各种Pizzanew LDOrderPizza();}// TODO Auto-generated method stub}}

抽象工厂模式(Abstract Factory Pattern)

参考链接

https://mp.weixin.qq.com/s/zGDzkvftexHzJqaAL5oSAQ

概念

  1. 为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。

  2. 工厂方法是在解决一个产品多个层级方面的事情;而抽象工厂致力于解决多个产品多个层级方面的事情

  3. 将实例化对象代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。符合设计模式的依赖抽象原则

使用场景

  1. 汽车是由很多零件组成的,比如引擎、轮胎、方向盘等等。现在如果我们是轮胎生产方,要生产宝马轮胎和奔驰轮胎,要用工厂方法还是抽象工厂实现呢?答案是:工厂方法。轮胎是一个产品,宝马轮胎和奔驰轮胎是 2 个不同层级的轮胎,所以用工厂方法解决就足够。

  2. 假如现在我们是汽车生产方,要生产宝马汽车和奔驰汽车,汽车又包含轮胎和方向盘等等,要用哪个来实现?既然上面的是工厂方法,那这个就用抽象工厂,因为这涉及到多个产品(轮胎、方向盘等等)和 2 个层级(宝马和奔驰)

实现思路

  • 实现类图

实现方式

  • 案例代码
```java//将Pizza 类做成抽象public abstract class Pizza {protected String name; //名字//准备原材料, 不同的披萨不一样,因此,我们做成抽象方法public abstract void prepare();public void bake() {System.out.println(name + " baking;");}public void cut() {System.out.println(name + " cutting;");}//打包public void box() {System.out.println(name + " boxing;");}public void setName(String name) {this.name = name;}}//北京奶酪pizzapublic class BJCheesePizza extends Pizza {@Overridepublic void prepare() {// TODO Auto-generated method stubsetName("北京的奶酪pizza");System.out.println(" 北京的奶酪pizza 准备原材料");}}//伦敦胡椒pizzapublic class LDPepperPizza extends Pizza{@Overridepublic void prepare() {// TODO Auto-generated method stubsetName("伦敦的胡椒pizza");System.out.println(" 伦敦的胡椒pizza 准备原材料");}}//一个抽象工厂模式的抽象层(接口)public interface AbsFactory {//让下面的工厂子类来 具体实现public Pizza createPizza(String orderType);}//伦敦工厂类public class LDFactory implements AbsFactory {@Overridepublic Pizza createPizza(String orderType) {System.out.println("~使用的是抽象工厂模式~");Pizza pizza = null;if (orderType.equals("cheese")) {pizza = new LDCheesePizza();} else if (orderType.equals("pepper")) {pizza = new LDPepperPizza();}return pizza;}}//北京工厂类public class BJFactory implements AbsFactory {@Overridepublic Pizza createPizza(String orderType) {System.out.println("~使用的是抽象工厂模式~");// TODO Auto-generated method stubPizza pizza = null;if(orderType.equals("cheese")) {pizza = new BJCheesePizza();} else if (orderType.equals("pepper")){pizza = new BJPepperPizza();}return pizza;}}//订购Pizza类public class OrderPizza {AbsFactory factory;// 构造器public OrderPizza(AbsFactory factory) {setFactory(factory);}private void setFactory(AbsFactory factory) {Pizza pizza = null;String orderType = ""; // 用户输入this.factory = factory;do {orderType = getType();// factory 可能是北京的工厂子类,也可能是伦敦的工厂子类pizza = factory.createPizza(orderType);if (pizza != null) { // 订购okpizza.prepare();pizza.bake();pizza.cut();pizza.box();} else {System.out.println("订购失败");break;}} while (true);}// 写一个方法,可以获取客户希望订购的披萨种类private String getType() {try {BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));System.out.println("input pizza 种类:");String str = strin.readLine();return str;} catch (IOException e) {e.printStackTrace();return "";}}}//Pizza订购商店测试类public class PizzaStore {public static void main(String[] args) {// TODO Auto-generated method stub//new OrderPizza(new BJFactory());new OrderPizza(new LDFactory());}}```

注意事项

  1. 创建对象实例时,不要直接new类,而是把这个new类的动作放在一个工厂的方法中,并返回。有的书上说,变量不要直接持有具体类的引用。

  2. 不要让类继承具体类,而是继承抽象类或者是实现interface(接口)

  3. 不要覆盖基类中已经实现的方法

建造者模式(Builder Pattern)

参考链接

https://mp.weixin.qq.com/s/iR4vsDblhFd7cmaM9hrfGg

概念

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

使用场景

盖房子,过程分为打桩、砌墙、封顶;要求盖出不同类型的房子,如大厦、别墅

角色分类

  • Product(产品角色)

    具体产品对象

  • Builder(抽象建造者)

    创建一个Product对象的各个部件指定的接口

  • ConcreteBuilder(具体建造者)

    实现接口,构建和装配各个部件

  • Director(指挥者)

    构建一个使用Builder接口的对象。主要作用是:

    1. 隔离了客户与对象的生产过程

    2. 负责控制产品对象的生产过程

实现方式

传统方式

  • 类图

  • 问题分析

    没有设计缓存层对象,程序的扩展和维护不好,把产品(房子)和创建产品的过程(建房子流程)封装在一起,耦合性增强

  • 案例源码

    public abstract class AbstractHouse {//打地基public abstract void buildBasic();//砌墙public abstract void buildWalls();//封顶public abstract void roofed();public void build() {buildBasic();buildWalls();roofed();}}public class CommonHouse extends AbstractHouse {@Overridepublic void buildBasic() {// TODO Auto-generated method stubSystem.out.println(" 普通房子打地基 ");}@Overridepublic void buildWalls() {// TODO Auto-generated method stubSystem.out.println(" 普通房子砌墙 ");}@Overridepublic void roofed() {// TODO Auto-generated method stubSystem.out.println(" 普通房子封顶 ");}}public class Client {public static void main(String[] args) {// TODO Auto-generated method stubCommonHouse commonHouse = new CommonHouse();commonHouse.build();}}
    

建造者方式

  • 类图

  • 案例源码

        /*** @ClassName House* @Description TODO 建造者模式-产品$* @Author charlesYan* @Date 2020/3/5 11:39* @Version 1.0**/public class House {private String baise;private String wall;private String roofed;public String getBaise() {return baise;}public void setBaise(String baise) {this.baise = baise;}public String getWall() {return wall;}public void setWall(String wall) {this.wall = wall;}public String getRoofed() {return roofed;}public void setRoofed(String roofed) {this.roofed = roofed;}@Overridepublic String toString() {return "House{" +"baise='" + baise + '\'' +", wall='" + wall + '\'' +", roofed='" + roofed + '\'' +'}';}}/*** @ClassName HouseBuilder* @Description TODO 建造者模式-抽象建造者$* @Author charlesYan* @Date 2020/3/5 11:40* @Version 1.0**/public abstract class HouseBuilder {protected House house = new House();public abstract void buildBasic();public abstract void buildWall();public abstract void buildRoofed();public House buildHouse(){return house;}}/*** @ClassName HouseDirector* @Description TODO 建造者模式-指挥者$* @Author charlesYan* @Date 2020/3/5 11:41* @Version 1.0**///指挥者,这里去指定制作流程,返回产品public class HouseDirector {HouseBuilder houseBuilder = null;//构造器传入 houseBuilderpublic HouseDirector(HouseBuilder houseBuilder) {this.houseBuilder = houseBuilder;}//通过setter 传入 houseBuilderpublic HouseBuilder getHouseBuilder() {return houseBuilder;}public void setHouseBuilder(HouseBuilder houseBuilder) {this.houseBuilder = houseBuilder;}//如何处理建造房子的流程,交给指挥者public House constructHouse(){houseBuilder.buildBasic();houseBuilder.buildWall();houseBuilder.buildRoofed();return houseBuilder.buildHouse();}}/*** @ClassName CommonHouse* @Description TODO 建造者模式-具体建造者$* @Author charlesYan* @Date 2020/3/5 11:42* @Version 1.0**/public class CommonHouse extends HouseBuilder {@Overridepublic void buildBasic() {house.setBaise("普通5米地基");}@Overridepublic void buildWall() {house.setWall("普通10cm墙厚");}@Overridepublic void buildRoofed() {house.setRoofed("普通2m屋顶");}}/*** @ClassName HighBuilding* @Description TODO 建造者模式-具体建造者$* @Author charlesYan* @Date 2020/3/5 11:43* @Version 1.0**/public class HighBuilding extends HouseBuilder {@Overridepublic void buildBasic() {house.setBaise("高楼20米地基");}@Overridepublic void buildWall() {house.setWall("高楼100cm墙厚");}@Overridepublic void buildRoofed() {house.setRoofed("高楼10m屋顶");}}/*** @ClassName Client* @Description TODO 建造者模式测试类$* @Author charlesYan* @Date 2020/3/5 12:05* @Version 1.0**/public class Client {public static void main(String[] args) {CommonHouse commonHouseBuild = new CommonHouse();HouseDirector houseDirector = new HouseDirector(commonHouseBuild);House commonHouse = houseDirector.constructHouse();System.out.println(commonHouse.toString());HighBuilding highBuildingBuild = new HighBuilding();houseDirector.setHouseBuilder(highBuildingBuild);House highHouse = houseDirector.constructHouse();System.out.println(highHouse.toString());}}

JDK源码应用分析

  • java.lang.StringBuilder

    public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence{@Overridepublic StringBuilder append(String str) {super.append(str);return this;}}abstract class AbstractStringBuilder implements Appendable, CharSequence {public AbstractStringBuilder append(String str) {if (str == null)return appendNull();int len = str.length();ensureCapacityInternal(count + len);str.getChars(0, len, value, count);count += len;return this;}}public interface Appendable {Appendable append(CharSequence csq) throws IOException;}
  • 角色分析

    1. Appendable接口定义了多个append方法(抽象方法),即Appendable为抽象建造者,定义了抽象方法

    2. AbstractStringBuilder实现了Appendable接口方法,这里的AbstractStringBuilder已经是建造者,只是不能实例化

    3. StringBuilder既充当了指挥者角色,同时充当了具体的建造者,建造方法的实现是由AbstractStringBuilder完成,而StringBuilder继承了AbstractStringBuilder

注意事项

  • 建造者模式实现细节

    1. 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象

    2. 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象

    3. 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程

    4. 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合"开闭原则"

    5. 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式

  • 抽象工厂模式VS建造者模式

    抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品,具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心由什么工厂生产即可

    建造者模式则是要求按照指定的蓝图建造产品,主要目的是通过组装零配件而生产一个新产品

原型模式(Prototype Pattern)

参考链接

https://mp.weixin.qq.com/s/4GLtg5Qrdpltz4C4-KtLZg

概念

用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象

原理

通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()

实现思路

  • 传统方式

    1. 创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低

    2. 总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活

  • 原型模式

    Java中Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力

实现方式

  • 传统方式

    public class NoPrototypeTest {public static void main(String[] args) {for (int i = 1; i <= 10; i ++) {Book book = new Book("娱乐至死", "尼尔波兹曼", "社会科学", "XXXX");System.out.println("复印书籍:" + book.getName() + ",第 " + i + " 本");}}}class Book {private String name;private String author;private String type;private String content;public Book(String name, String author, String type, String content) {this.name = name;this.author = author;this.type = type;this.content = content;System.out.println("实例化书籍:" + this.name);}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public String getType() {return type;}public void setType(String type) {this.type = type;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}}
  • 原型模式

    public class PrototypeTest {public static void main(String[] args) {Book2 book1 = new ConcreteBook("娱乐至死", "尼尔波兹曼", "社会科学", "XXXX");System.out.println("复印书籍:" + book1.getName() + ",第 " + 1 + " 本");for (int i = 2; i <= 10; i ++) {Book2 book2 = (Book2) book1.clone();System.out.println("复印书籍:" + book2.getName() + ",第 " + i + " 本");}}}/*** 抽象类*/abstract class Book2 implements Cloneable {private String name;private String author;private String type;private String content;public Book2(String name, String author, String type, String content) {this.name = name;this.author = author;this.type = type;this.content = content;System.out.println("实例化书籍:" + this.name);}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public String getType() {return type;}public void setType(String type) {this.type = type;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}@Overrideprotected Object clone() {try {return super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}}/*** 具体类*/class ConcreteBook extends Book2 {public ConcreteBook(String name, String author, String type, String content) {super(name, author, type, content);}}

JDK源码应用分析

  • beans.xml

    <bean id="id01" class="com.nbsp.spring.bean.Monster" scope="prototype" />
  • AbstractBeanFactory.java

    protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {String beanName = this.transformedBeanName(name);Object sharedInstance = this.getSingleton(beanName);Object bean;if (sharedInstance != null && args == null) {if (this.logger.isTraceEnabled()) {if (this.isSingletonCurrentlyInCreation(beanName)) {this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");} else {this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);} else {if (this.isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}BeanFactory parentBeanFactory = this.getParentBeanFactory();if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {String nameToLookup = this.originalBeanName(name);if (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}if (args != null) {return parentBeanFactory.getBean(nameToLookup, args);}if (requiredType != null) {return parentBeanFactory.getBean(nameToLookup, requiredType);}return parentBeanFactory.getBean(nameToLookup);}if (!typeCheckOnly) {this.markBeanAsCreated(beanName);}try {RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);this.checkMergedBeanDefinition(mbd, beanName, args);String[] dependsOn = mbd.getDependsOn();String[] var11;if (dependsOn != null) {var11 = dependsOn;int var12 = dependsOn.length;for(int var13 = 0; var13 < var12; ++var13) {String dep = var11[var13];if (this.isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}this.registerDependentBean(dep, beanName);try {this.getBean(dep);} catch (NoSuchBeanDefinitionException var24) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var24);}}}if (mbd.isSingleton()) {sharedInstance = this.getSingleton(beanName, () -> {try {return this.createBean(beanName, mbd, args);} catch (BeansException var5) {this.destroySingleton(beanName);throw var5;}});bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);} else if (mbd.isPrototype()) {var11 = null;Object prototypeInstance;try {this.beforePrototypeCreation(beanName);prototypeInstance = this.createBean(beanName, mbd, args);} finally {this.afterPrototypeCreation(beanName);}bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);} else {String scopeName = mbd.getScope();Scope 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, () -> {this.beforePrototypeCreation(beanName);Object var4;try {var4 = this.createBean(beanName, mbd, args);} finally {this.afterPrototypeCreation(beanName);}return var4;});bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);} catch (IllegalStateException var23) {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", var23);}}} catch (BeansException var26) {this.cleanupAfterBeanCreationFailure(beanName);throw var26;}}if (requiredType != null && !requiredType.isInstance(bean)) {try {T convertedBean = this.getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());} else {return convertedBean;}} catch (TypeMismatchException var25) {if (this.logger.isTraceEnabled()) {this.logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var25);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}} else {return bean;}}

浅拷贝与深拷贝

浅拷贝

  • 介绍

    1. 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象

    2. 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。

    因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另外一个对象的该成员变量的值

    1. 浅拷贝是使用默认的clone()方法来实现

深拷贝

  • 介绍

    1. 复制对象的所有基本数据类型的成员变量值

    2. 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。

    也就是说,对象进行深拷贝要对整个对象进行拷贝

    1. 可以通过重写clone方法,或者对象序列化来实现深拷贝

案例代码

  • 深克隆内部实体类

    import java.io.Serializable;/*** @ClassName InterDeepClone* @Description TODO 深克隆内部实体类$* @Author charlesYan* @Date 2020/3/4 20:29* @Version 1.0**/public class InterDeepClone implements Serializable, Cloneable {private static final long serialVersionUID = 1L;private String cloneName;private String cloneClass;//构造器public InterDeepClone(String cloneName, String cloneClass) {this.cloneName = cloneName;this.cloneClass = cloneClass;}public String getCloneName() {return cloneName;}public void setCloneName(String cloneName) {this.cloneName = cloneName;}public String getCloneClass() {return cloneClass;}public void setCloneClass(String cloneClass) {this.cloneClass = cloneClass;}@Overridepublic String toString() {return "InterDeepClone{" +"hashCode='" + this.hashCode() + '\'' +"cloneName='" + cloneName + '\'' +", cloneClass='" + cloneClass + '\'' +'}';}//因为该类的属性,都是String类型,因此使用默认clone方法@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}}
  • 深克隆原型模式类

    import java.io.*;/*** @ClassName DeepCloneProtoType* @Description TODO 深克隆原型模式$* @Author charlesYan* @Date 2020/3/4 20:34* @Version 1.0**/public class DeepCloneProtoType implements Serializable,Cloneable {private String name;private InterDeepClone interDeepClone;//引用类型public DeepCloneProtoType() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public InterDeepClone getInterDeepClone() {return interDeepClone;}public void setInterDeepClone(InterDeepClone interDeepClone) {this.interDeepClone = interDeepClone;}@Overridepublic String toString() {return "DeepCloneProtoType{" +"hashCode='" + this.hashCode() + '\'' +"name='" + name + '\'' +", interDeepClone=" + interDeepClone +'}';}//深拷贝 - 方式1:使用多层clone方式@Overrideprotected Object clone() throws CloneNotSupportedException {Object deepCloneProtoType = null;//完成对基本数据类型(属性)和String的克隆deepCloneProtoType = super.clone();//对引用类型的属性,进行单独处理DeepCloneProtoType outClone = (DeepCloneProtoType) deepCloneProtoType;InterDeepClone interClone = (InterDeepClone) interDeepClone.clone();outClone.setInterDeepClone(interClone);return outClone;}//深拷贝 - 方式2 通过对象的序列化实现 (推荐)public Object deepClone(){//创建流对象ByteArrayOutputStream bos = null;ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;try {//序列化bos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);oos.writeObject(this);//将当前对象以流的方式输出//反序列化bis = new ByteArrayInputStream(bos.toByteArray());ois = new ObjectInputStream(bis);DeepCloneProtoType deeClone = (DeepCloneProtoType) ois.readObject();return deeClone;} catch (IOException e) {e.printStackTrace();return null;} catch (ClassNotFoundException e) {e.printStackTrace();return null;} finally {//关流try {if (bos != null) {bos.close();}if (oos != null) {oos.close();}if (bis != null) {bis.close();}if (ois != null) {ois.close();}} catch (IOException e) {e.printStackTrace();}}}}
  • 深拷贝测试类

    /*** @ClassName ProtoTypeTest* @Description TODO 原型模式深拷贝测试类$* @Author charlesYan* @Date 2020/3/4 20:54* @Version 1.0**/public class ProtoTypeTest {public static void main(String[] args) throws CloneNotSupportedException {InterDeepClone interClone = new InterDeepClone("CharlesYan","内部类");DeepCloneProtoType outClone1 = new DeepCloneProtoType();outClone1.setInterDeepClone(interClone);outClone1.setName("CharlesYan");//1. 使用clone方式实现深拷贝DeepCloneProtoType outClone2 = (DeepCloneProtoType) outClone1.clone();outClone2.getInterDeepClone().setCloneName("Yan");System.out.println(outClone1);System.out.println(outClone2);//2. 使用序列化方式实现深拷贝DeepCloneProtoType outClone3 = (DeepCloneProtoType) outClone1.deepClone();outClone3.getInterDeepClone().setCloneName("Charles");System.out.println(outClone3);}}

注意事项

  1. 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率

  2. 不用重新初始化对象,而是动态地获得对象运行时的状态

  3. 如果原始对象发生变化(增加或者减少属性),其它克隆对象也会发生相应的变化,无需修改代码

  4. 在实现深克隆的时候可能需要比较复杂的代码

  5. 需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则

Java设计模式之五大创建型模式相关推荐

  1. Java设计模式之五大创建型模式(附实例和详解)

    一.概况 总体来说设计模式分为三大类: (1)创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. (2)结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥 ...

  2. Java经典设计模式:五大创建型模式

    一.概况 总体来说设计模式分为三大类: (1)创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. (2)结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥 ...

  3. 初探Java设计模式1:创建型模式(工厂,单例等)

    Java 设计模式 一直想写一篇介绍设计模式的文章,让读者可以很快看完,而且一看就懂,看懂就会用,同时不会将各个模式搞混.自认为本文还是写得不错的,花了不少心思来写这文章和做图,力求让读者真的能看着简 ...

  4. 【设计模式·Python】创建型模式

    设计模式中,创建型模式主要由以下几种: 工厂方法模式 抽象工厂模式 建造者模式 原型模式 单例模式 简单工厂模式 不直接向客户暴露对象的实现细节,而是通过一个工厂类来负责创建产品的实例. 角色: 工厂 ...

  5. 设计模式之六个创建型模式的相关知识,简单易懂。

    一. 简单工厂模式-Simple Factory Pattern 1) 工厂三兄弟之简单工厂模式(一) 工厂模式是最常用的一类创建型设计模式,通常我们所说的工厂模式是指工厂方法模式,它也是使用频率最高 ...

  6. 初探Java设计模式3:行为型模式(策略,观察者等)

    转自https://javadoop.com/post/design-pattern 行为型模式 策略模式 观察者模式 责任链模式 模板方法模式 状态模式 行为型模式总结 本系列文章将整理到我在Git ...

  7. 《深入设计模式》笔记 -创建型模式三、生成器模式(建造者模式)

    生成器模式 亦称:建造者模式.Builder 意图 生成器模式是一种创建型设计模式, 使你能够分步骤创建复杂对象. 该模式允许你使用相同的创建代码生成不同类型和形式的对象. 问题 假设有这样一个复杂对 ...

  8. 设计模式的艺术 创建型模式之原型模式

    不懂使用为学过,说出用途,绘制结构为了解,不会灵活使用基本等于没学. 前言 西游记中孙悟空拔毛出分身的故事大家都知道,孙悟空根据了自己的形象创建了分身,克隆出了一个和自己几乎一模一样的身外身,设计模式 ...

  9. 设计模式一:创建型模式

    知识储备: 1.接口:若干抽象方法的集合 作用:限制实现接口的类必须按照接口给定的调用方式实现这些方法:对高层模块隐藏了类的内部实现. 2.面向对象设计SOLID原则 开放封闭原则:一个软件实体如类. ...

最新文章

  1. [Android1.5]Android2.0版本以下Activity切换动画效果
  2. 【MyBatis】MyBatis分页插件PageHelper的使用
  3. 《算法导论》读书笔记--第1、2章课后题 (转)
  4. python loadtxt_Python 数据科学入门2:Matplotlib
  5. php min命令,php min函数怎么用 - min
  6. 也许现在的前端,应该了解更多的算法
  7. Docker系列(十)Dockerfile指令
  8. 【Laravel】快速查阅手册
  9. PLC梯形图编程基础知识详解(转自:http://gongkong.ofweek.com/2014-09/ART-310012-11000-28882866_2.html)
  10. html设置鼠标指针的形状,CSS - 鼠标指针样式详解(cursor光标样式、自定义指针图片)...
  11. ASCII码一览表,ASCII码对照表
  12. 网络嗅探器 java_网络嗅探软件的设计与分析
  13. 综合素质计算机的知识考点,综合素质考点知识
  14. QCC3040---UI prompts module
  15. 服务程序占用服务器内存过大处理
  16. 双主机切换下导致的显示器闪动
  17. 创建 DBLink 的方法
  18. 【2020.12】Aspose.words 20.12最新版Crack,word转pdf去水印方法
  19. Google 的封杀与被封杀
  20. 计算机管理损坏的图像,win7系统提示损坏的图像的解决方法

热门文章

  1. php sendmail smtp,Sendmail基础配置和smtp命令学习
  2. 带键盘的java模拟器_Java模拟器v1.0正式版
  3. 思科防火墙ASA5520配置
  4. JavaScript | 正则表达式
  5. 鸿蒙系统浏览器内核,四大浏览器横评出炉:Chromium 内核版 Edge 四项夺冠,优于原生 Chrome...
  6. Recover My Files(文件恢复工具) V5.2.1.1964 汉化绿色特别版
  7. C语言题目:韩信点兵
  8. MATLAB实现卡尔曼滤波器仿真
  9. 免费Web接口调用大全
  10. ssl证书如何安装?常见的四类ssl证书安装方法介绍