JAVA设计模式第二讲:创建型设计模式(下)

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Redis 版,经济版 1GB 1个月
简介: JAVA设计模式第二讲:创建型设计模式(下)

8.2、什么时候该用工厂模式?相对于直接 new 来创建对象,用工厂模式来创建究竟有什么好处呢?

概念:工厂模式用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。

使用场景如果创建对象的逻辑并不复杂,那我们直接通过 new 来创建对象就可以了,不需要使用工厂模式用到大量的创建某种、某类或者某批对象时,考虑使用工厂模式

工厂模式的作用将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系解耦。从而提高项目的扩展和维护性

Demo1:简单工厂使用示例(规则校验器)

@Component
public class RuleValidatorFactory {
    @Autowired
    private ArRuleValidator aValidator;
    @Autowired
    private BRuleValidator bValidator;
    @Autowired
    private CRuleValidator cValidator;
    @Autowired
    private DRuleValidator dValidator;
    public IBaseValidator createRuleValidator(ValidatorModeEnum validatorModeEnum) {
        switch (validatorModeEnum) {
            case NAME:
                return aValidator;
            case DETAIL:
                return bValidator;
            case MAIN_IMG:
                return cValidator;
            case CATEGORY_ATTR:
                return dValidator;
        }
        throw new ServiceException("校验模式不存在");
    }
}
//调用该工厂模式的方法
IBaseValidator baseValidator = ruleValidatorFactory.ruleValidator(mode);
List<String> result = baseValidator.validateItemRule(validatorModeEnum);

工厂方法模式

  • 1、普通工厂:建立一个工厂类,对实现了同一接口的一些类进行实例的创建
  • 2、多个工厂方法模式 :在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
  • 3、静态工厂方法:上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
  • 4、抽象工厂模式:围绕一个超级工厂创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
  • 防止工厂过多,对工厂类做了一层抽象

Demo2:使用是单例模式结合工厂模式

解决每次调用工厂类都会创建新对象的问题

public class RuleValidatorFactory {
  private static final Map<String, IBaseValidator> cachedParsers = new HashMap<>();
  // 定义一个静态代码块,存储对象
  static {
    cachedParsers.put(NAME, new ArRuleValidator());
    cachedParsers.put(DETAIL, new BRuleValidator());
    cachedParsers.put(MAIN_IMG, new CRuleValidator());
    cachedParsers.put(CATEGORY_ATTR, new DRuleValidator());
  }
    public IBaseValidator createRuleValidator(ValidatorModeEnum validatorModeEnum) {
            return cachedParsers.get(validatorModeEnum); 
        }
        throw new ServiceException("校验模式不存在");
    }
}
Demo3 工厂模式在JDK-Calendar的应用

Calendar 类提供了大量跟日期相关的功能代码,同时,又提供了一个 getInstance() 工厂

方法,用来根据不同的 TimeZone 和 Locale 创建不同的 Calendar 子类对象

public abstract class Calendar implements Serializable, Cloneable, Comparable<> {
  // ...
  public static Calendar getInstance(){
    return createCalendar(TimeZone.getDefault(),Locale.getDefault(Locale.Category.FORMAT));
  }
  private static Calendar createCalendar(TimeZone zone,Locale aLocale) {
    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;
                }
            }
        }
    }
}
public class SimpleFactory {
  public static void main(String[] args) {
    Calendar cal = Calendar.getInstance();
    // 注意月份下标从0开始,所以取月份要+1
    System.out.println("年:" + cal.get(Calendar.YEAR));
    System.out.println("月:" + (cal.get(Calendar.MONTH) + 1));
    System.out.println("日:" + cal.get(Calendar.DAY_OF_MONTH));
    System.out.println("时:" + cal.get(Calendar.HOUR_OF_DAY));
    System.out.println("分:" + cal.get(Calendar.MINUTE));
    System.out.println("秒:" + cal.get(Calendar.SECOND));
  }
}

使用了工厂模式的组件:DateFormat String

工厂模式最佳实践

都使用抽象工厂模式,按照产品族维度来建立工厂,如果只有一个产品那么工厂中就一个方法,如果有多个产品就多个方法。

重温设计模式之 Factory–阿里对工厂模式的实践

工厂模式一个非常经典的应用场景:依赖注入框架

比如 SpringIOC、Google Guice它用来集中创建、组装、管理对象,跟具体业务代码解耦让程序员聚焦在业务代码的开发上

Demo4 使用工厂模式实现 Spring BeanFactory?

工厂类:负责某个类对象或者某一组相关类对象(继承自同一抽象类或者接口的子类)的创建;

DI 容器:负责的是整个应用中所有类对象的创建。

  • 它的功能:①配置解析 ②对象创建 ③对象生命周期管理

第一步,配置解析

DI 容器来创建的类对象和创建类对象的必要信息放到配置文件中。容器读取配置文件,根据配置文件提供的信息来创建对象。

Spring 容器的配置文件。Spring 容器读取这个配置文件,解析出要创建的两个对象:rateLimiterredisCounter,并且得到两者的依赖关系:rateLimiter 依赖 redisCounter。

public class RateLimiter {
  private RedisCounter redisCounter;
  public RateLimiter(RedisCounter redisCounter) {
    this.redisCounter = redisCounter;
  }
  public void test() {
    System.out.println("Hello World!");
  }
  //...
} 
public class RedisCounter {
  private String ipAddress;
  private int port;
  public RedisCounter(String ipAddress, int port) {
    this.ipAddress = ipAddress;
    this.port = port;
  }
  //...
}

Spring 配置文件beans.xml:

<beans>
  <bean id="rateLimiter" class="com.xzg.RateLimiter">
    <constructor-arg ref="redisCounter"/>
  </bean>
  <bean id="redisCounter" class="com.xzg.redisCounter">
    <constructor-arg type="String" value="127.0.0.1">
    <constructor-arg type="int" value=1234>
  </bean>
</beans>

对象创建

将所有类对象的创建都放到一个BeansFactory工厂类中完成就可以了,通过“反射”机制,它能在程序运行的过程中,动态地加载类、创建对象,不需要事先在代码中写死要创建哪些对象

对象的生命周期管理

简单工厂模式有两种实现方式,一种是每次都返回新创建的对象,另一种是每次都返回同一个事先创建好的对象,也就是单例对象

在 Spring 框架中,①我们可以通过配置 scope 属性,来区分这两种不同类型的对象。scope=prototype 表示返回新创建的对象,scope=singleton 表示返回单例对象

配置对象是否支持懒加载。如果 lazy-init=true,对象在真正被使用到的时候才会被创建。

③配置对象的 init-method (初始化对象) 和 destroy-method (做清理工作)方法

如何使用 BeanFactory

从 classpath 中加载 XML 格式的配置文件,然后通过 BeanConfigParser 解析为统一的 BeanDefinition 格式,然后,BeansFactory 根据 BeanDefinition 来创建对象。

public class Demo {
  public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    RateLimiter rateLimiter = (RateLimiter) applicationContext.getBean("rateLimiter");
    rateLimiter.test();
    //...
  }
}
public interface ApplicationContext {
  Object getBean(String beanId);
}
public class ClassPathXmlApplicationContext implements ApplicationContext {
  private BeansFactory beansFactory;
  private BeanConfigParser beanConfigParser;
  public ClassPathXmlApplicationContext(String configLocation) {
    this.beansFactory = new BeansFactory();
    this.beanConfigParser = new XmlBeanConfigParser();
    loadBeanDefinitions(configLocation);
  } 
  private void loadBeanDefinitions(String configLocation) {
    InputStream in = null;
    try {
      in = this.getClass().getResourceAsStream("/" + configLocation);
      if (in == null) {
        throw new RuntimeException("Can not find config file: " + configLocation);
      }
      // 推荐看 spring 源码
      List<BeanDefinition> beanDefinitions = beanConfigParser.parse(in);
      beansFactory.addBeanDefinitions(beanDefinitions);
    } finally {
      if (in != null) {
        try {
          in.close();
        } catch (IOException e) {
          // TODO: log error
        }
      }
    }
  } 
  @Override
  public Object getBean(String beanId) {
    return beansFactory.getBean(beanId);
  }
}
public class BeanDefinition {
  private String id;
  private String className;
  private List<ConstructorArg> constructorArgs = new ArrayList<>();
  private Scope scope = Scope.SINGLETON;
  private boolean lazyInit = false;
  // 省略必要的getter/setter/constructors
  public boolean isSingleton() {
    return scope.equals(Scope.SINGLETON);
  } 
  public static enum Scope {
    SINGLETON,
    PROTOTYPE
  } 
  public static class ConstructorArg {
    private boolean isRef;
    private Class type;
    private Object arg;
    // 省略必要的getter/setter/constructors
  }
}

BeansFactory 的定义 负责根据从配置文件解析得到的 BeanDefinition 来创建对象

JVM 在启动的时候会根据代码自动地加载类、创建对象。至于都要加载哪些类、创建哪些对象,这些都是在代码中写死的,或者说提前写好的。但是,如果某个对象的创建并不是写死在代码中,而是放到配置文件中,我们需要在程序运行期间,动态地根据配置文件来加载类、创建对象(Java反射技术)

public class BeansFactory {
  // 用于保存单例对象  scope == singleton,下次直接从 Map 中取数据
  private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
  private ConcurrentHashMap<String, BeanDefinition> beanDefinitions = new ConcurrentHashMap<>();
  public void addBeanDefinitions(List<BeanDefinition> beanDefinitionList) {
    for (BeanDefinition beanDefinition : beanDefinitionList) {
      this.beanDefinitions.putIfAbsent(beanDefinition.getId(), beanDefinition)
    } 
    for (BeanDefinition beanDefinition : beanDefinitionList) {
      // 非懒加载 且为单例  ---》 饿汉式单例
      if (beanDefinition.isLazyInit() == false && beanDefinition.isSingleton())
      createBean(beanDefinition);
    }
  }
  public Object getBean(String beanId) {
    BeanDefinition beanDefinition = beanDefinitions.get(beanId);
    if (beanDefinition == null) {
      throw new NoSuchBeanDefinitionException("Bean is not defined: " + beanId);
    }
    return createBean(beanDefinition);
  }
  @VisibleForTesting
  protected Object createBean(BeanDefinition beanDefinition) {
    // 单例
    if (beanDefinition.isSingleton() && singletonObjects.containsKey(beanDefinition.getId())) {
      return singletonObjects.get(beanDefinition.getId());
    } 
    Object bean = null;
    try {
      // 非单例 or singletonObjects 不包含时,通过反射加载类,创建对象
      Class beanClass = Class.forName(beanDefinition.getClassName());
      List<BeanDefinition.ConstructorArg> args = beanDefinition.getConstructorArg();
      if (args.isEmpty()) {
        bean = beanClass.newInstance();
      } else {
        Class[] argClasses = new Class[args.size()];
        Object[] argObjects = new Object[args.size()];
        for (int i = 0; i < args.size(); ++i) {
          BeanDefinition.ConstructorArg arg = args.get(i);
          if (!arg.getIsRef()) {
            argClasses[i] = arg.getType();
            argObjects[i] = arg.getArg();
          } else {
            BeanDefinition refBeanDefinition = beanDefinitions.get(arg.getArg());
            if (refBeanDefinition == null) {
              throw new NoSuchBeanDefinitionException("Bean is not defined: " +
            }
            argClasses[i] = Class.forName(refBeanDefinition.getClassName());
            // 递归调用
            argObjects[i] = createBean(refBeanDefinition);
          }
        }
        bean = beanClass.getConstructor(argClasses).newInstance(argObjects);
      }
    } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTarget
      throw new BeanCreationFailureException("", e);
    } 
    if (bean != null && beanDefinition.isSingleton()) {
      singletonObjects.putIfAbsent(beanDefinition.getId(), bean);
      return singletonObjects.get(beanDefinition.getId());
    }
    return bean;
  }
}

好处是:对象创建、组装、管理完全有 DI 容器来负责,跟具体业务代码解耦

Action1:递归调用可能会导致了循环依赖,Spring 如何解决 A 和 B 对象的循环引用?

(1)只能处理单例的、setter 注入的循环依赖,其他的注入模式无法处理;

(2)依赖缓存处理循环依赖,关键思想是,将正在创建中的对象提前暴露一个单例工厂,让其他实例可以引用到。

可以参考这篇文章:Spring 源码学习(五)循环依赖


8.3、Builder 设计模式 将产品和产品建造过程解耦

定义Builder 模式用来创建复杂对象,可以通过设置不同的可选参数,“定制化”地创建不同的对象。将复杂对象的建造过程抽象出来。

Builder模式的四个角色

①Product(产品角色):一个具体的产品对象

②Builder(抽象建造者):创建一个Product对象的各个部件指定的 接口/抽象类

③ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。

④Director(指挥者): 构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。

使用场景

之前的做法:构建对象时必填项使用有参构造函数,非必填属性使用 set() 方法

现在

1、当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数;

2、类的属性之间有一定的依赖关系或者约束条件;

3、如果我们希望创建不可变对象,也就是说,不能在类中暴露 set() 方法。

Demo1:Builder 模式如何使用

public class ResourcePoolConfig {
  private String name;
  // 最大资源数
  private int maxTotal;
  // 最大空闲资源数
  private int maxIdle;
  // 最小空闲资源数
  private int minIdle;
  private ResourcePoolConfig(Builder builder) {
    this.name = builder.name;
    this.maxTotal = builder.maxTotal;
    this.maxIdle = builder.maxIdle;
    this.minIdle = builder.minIdle;
  }
  //...省略getter方法...
  //我们将Builder类设计成了ResourcePoolConfig的内部类。
  public static class Builder {
    private static final int DEFAULT_MAX_TOTAL = 8;
    private static final int DEFAULT_MAX_IDLE = 8;
    private static final int DEFAULT_MIN_IDLE = 0;
    private String name;
    private int maxTotal = DEFAULT_MAX_TOTAL;
    private int maxIdle = DEFAULT_MAX_IDLE;
    private int minIdle = DEFAULT_MIN_IDLE;
    public Builder() {}
    public ResourcePoolConfig build() {
      // 校验逻辑放到这里来做,包括必填项校验、依赖关系校验、约束条件校验等
      if (StringUtils.isBlank(name)) {
        throw new IllegalArgumentException("...");
      }
      if (maxIdle > maxTotal) {
        throw new IllegalArgumentException("...");
      }
      if (minIdle > maxTotal || minIdle > maxIdle) {
        throw new IllegalArgumentException("...");
      } 
      return new ResourcePoolConfig(this);
    } 
    public Builder setName(String name) {
      if (StringUtils.isBlank(name)) {
        throw new IllegalArgumentException("...");
      }
      this.name = name;
      return this;
    } 
    public Builder setMaxTotal(int maxTotal) {
      if (maxTotal <= 0) {
        throw new IllegalArgumentException("...");
      }
      this.maxTotal = maxTotal;
      return this;
    } 
    public Builder setMaxIdle(int maxIdle) {
      if (maxIdle < 0) {
        throw new IllegalArgumentException("...");
      }
      this.maxIdle = maxIdle;
      return this;
    }  
    public Builder setMinIdle(int minIdle) {
      if (minIdle < 0) {
        throw new IllegalArgumentException("...");
      }
      this.minIdle = minIdle;
      return this;
    }
  } 
}
// Builder 模式的使用
// 符合面向对象的封装原则
ResourcePoolConfig config = new ResourcePoolConfig.Builder()
    .setName("dbconnectionpool")
    .setMaxTotal(16)
    .setMaxIdle(10)
    .setMinIdle(12)
    .build();
Demo2 借助 Lombok 中的 @Builder 注解实现建造者模式
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ZcyStandardTransferLog implements Serializable {
    /** 自定义主键*/
    private Long id;
    /** 迁移相关id spuId*/
    private Long relatedId;
    /** 枚举值: */
    private Integer type;
    /**枚举值:0 offLine 下架;1 freeze 冻结;2 delete 删除 3 上架 online*/
    private Integer operateType;
    /** 日志详情*/
    private String detail;
    /** 创建人id*/
    private Long creatorId;
    /** 创建时间*/
    private Date createdAt;
}

创建了一个名为 ZcyStandardTransferLogBuilder 的静态内部类, 并且具有和实体类相同的属性(称为构建器).

1: 对于目标类中的所有的属性, 都会在构建器中创建对应的属性.

2: 创建一个无参的default构造方法.

3: 对于实体类中的每个参数, 都会对应创建类似于setter方法, 但是方法名是与该参数名是相同的, 并且返回值是构建器本身(便于链式调用).

4: 一个build方法, 调用此方法, 就会根据设置的值进行创建对象实例.

5: 同时也会生成一个toString() 方法.

6: 会创建一个builder()方法, 它的目的是用来创建构建器.

补充:builder中的常用注解

Demo3 Builder 模式在JDK的应用和源码分析

java.lang.StringBuilder中的建造者模式,将append()逻辑放在抽象父类中,然后返回this

@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}
// StringBuilder 的父类
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;
}
// AbstractStringBuilder 的接口
// 因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象
public interface Appendable {
  Appendable append(CharSequence csq) throws IOException;
  Appendable append(CharSequence csq, int start, int end) throws IOException;
  Appendable append(char c) throws IOException;
}
// StringBuilder 的使用
stringBuilder.append("属性项【").append(entry.getKey()).append("】长度不允许超出 (").append(entry.getValue()).append(")字符,");

工厂模式和 Builder 模式的区别?

  • 工厂模式是用来创建不同但是相关类型的对象,由给定的参数决定创建哪种类型的对象;
  • Builder 模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象

补充 不可变模式

不可变模式定义

普通的不变模式指的是,对象中包含的引用对象是可以改变的

深度不变模式指的是,对象包含的引用对象也不可变

它们两个之间的关系,有点类似之前讲过的浅拷贝和深拷贝之间的关系

使用场景:如果一个对象符合创建之后就不会被修改这个特性,那我们就可以把它设计成不变类。

因为数据不变,所以不存在并发读写问题,因此不变模式常用在多线程环境下,来避免线程加锁。

// 普通不变模式
public class User {
  private String name;
  private int age;
  private Address addr;
  public User(String name, int age, Address addr) {
    this.name = name;
    this.age = age;
    this.addr = addr;
  }
  // 只有getter方法,无setter方法...
} 
public class Address {
  private String province;
  private String city;
  public Address(String province, String city) {
    this.province = province;
    this.city= city;
  }
  // 有getter方法,也有setter方法...
}
// 深度不变模式
public class User {
  private String name;
  private int age;
  private Address addr;
  public User(String name, int age, Address addr) {
    this.name = name;
    this.age = age;
    this.addr = addr;
  }
  // 只有getter方法,无setter方法...
} 
public class Address {
  private String province;
  private String city;
  public Address(String province, String city) {
    this.province = province;
    this.city= city;
  }
  // 只有getter方法,无setter方法..
}
```java
public class ImmutableDemo {
  public static void main(String[] args) {
    List<String> originalList = new ArrayList<>();
    originalList.add("a");
    originalList.add("b");
    originalList.add("c");
    List<String> jdkUnmodifiableList = Collections.unmodifiableList(originalList);
    List<String> guavaImmutableList = ImmutableList.copyOf(originalList);
    //jdkUnmodifiableList.add("d"); // 抛出UnsupportedOperationException
    // guavaImmutableList.add("d"); // 抛出UnsupportedOperationException
    originalList.add("d");
    print(originalList); // a b c d
    print(jdkUnmodifiableList); // a b c d
    print(guavaImmutableList); // a b c
  } 
  private static void print(List<String> list) {
        String join = Joiner.on(",").skipNulls().join(list);
        System.out.println(join);
    }
}

集合中的对象不会增删,但是对象的成员变量(或叫属性值)是可以改变的

原因如下:JDK 的不变集合相当于对原集合采用装饰者模式,即通过组合方式限制掉原集合的写操作,所以在原始集合类发生改变的时候它也会改变,而 Google Guava 的不变集合,是重新创建了一个原始集合对象的副本,所以改变原始类并不能改变它的数据,也更加符合语义。在日常使用时,需要注意这一点。


8.4、原型设计模式的深拷贝和浅拷贝是什么,并写出深拷贝的两种方式的源码

概念如果对象的创建成本比较大(复杂的RPC/IO计算),而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式,来创建新对象,以达到节省创建时间的目的这种基于原型来创建对象的方式就叫作原型模式

Java中:Java中Object类是所有类的根类, Object类提供了一个clone()方法,该方法可以

将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,

该接口表示该类能够复制且具有复制的能力。 (浅拷贝,不实用

原型模式的实现方式–深拷贝和浅拷贝:浅拷贝只会复制对象中基本数据类型数据和引用对象的内存地址,不会递归地复制引用对象,以及引用对象的引用对象……而深拷贝得到的是一份完完全全独立的对象。所以,深拷贝比起浅拷贝来说,更加耗时,更加耗内存空间。

深拷贝实现方案:1、使用Spring的BeanUtils工具类(原理是Java的反射语法) 2、使用Json序列化工具(推荐)

Demo1:重写Object的clone方法实现深拷贝,使用序列化来实现深拷贝

public class DeepProtoType implements Serializable, Cloneable {
  public String name; //String 属性
  public DeepCloneableTarget deepCloneableTarget;// 引用类型
  public DeepProtoType() {
    super();
  }
  //深拷贝 - 方式 1 使用clone 方法
  @Override
  protected Object clone() throws CloneNotSupportedException {
    Object deep = null;
    //这里完成对基本数据类型(属性)和String的克隆
    deep = super.clone(); 
    //对引用类型的属性,进行单独处理
    DeepProtoType deepProtoType = (DeepProtoType)deep;
    deepProtoType.deepCloneableTarget  = (DeepCloneableTarget)deepCloneableTarget.clone();
    return deepProtoType;
  }
  //深拷贝 - 方式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);
      DeepProtoType copyObj = (DeepProtoType)ois.readObject();
      return copyObj;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    } finally {
      //关闭流
      try {
        bos.close();
        oos.close();
        bis.close();
        ois.close();
      } catch (Exception e2) {
        // TODO: handle exception
        System.out.println(e2.getMessage());
      }
    }
  } 
}
//深拷贝工具1 springframework BeanUtil  原理:反射
BeanUtils.copyProperties(source, target, "id", "updatedAt", "updatedId", "updatedName");
//深拷贝工具2 Dozer工具
List<AttachmentDTO> attachmentDtos = DozerBeanUtil.convertList(xxx.getAttachments(), attachmentDTO.class);
//深拷贝工具3 AnyBeanCopy工具  原理:json序列化   推荐
Person personCopy = AnyBeanCopyUtils.convert(person, Person.class);

2、请使用UML类图画出原型模型核心角色? 原理结构图

1)原型类,声明一个克隆自己的接口

2) ConcretePrototype: 具体的原型类, 实现一个克隆自己的操作

3) Client: 让一个原型对象克隆自己,从而创建一个新的对象(属性一样)

Action:原型设计模式和 Spring 原型区别在哪?面试题

区别 Spring GOF
对象类型 根据Bean定义来创建对象 用原型实例指定创建对象类型
创建方式 根据Bean定义创建对象 通过拷贝原型创建对象
友好方式 非侵入式 侵入式

Demo2 Spring 框架哪些地方使用了原型模式,并对源码进行分析?

beans.xml

<bean id="id01" class="com.spring.bean.Monster" scope="prototype"/>
public void main (){
  ApplicationContext applicationContext = newClassPathXmlApplicationContext("beans.xml");
  //获取monster[通过id获取monster]
  Object bean = applicationContext.getBean("id01");
  System.out.println("bean" + bean);
}
// 在源码的 doGetBean 方法里面进行了判断
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);
}

原型模式使用踩坑

1、不要使用Common包里面的BeanUtils工具类

2、在日常开发中,注意对象里面的字段被修改的情况,使用深拷贝避免该问题。

创建型设计模式总结:

Action:使用双重锁校验的单例模式时,使用需要在成员变量前加上 volatile 关键字?

  • 暂时在成员变量里加上 volatile,防止指令重排,确保没有问题。
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2天前
|
设计模式 安全 Java
从零开始:Java开发者的设计模式指南
从零开始:Java开发者的设计模式指南
|
8天前
|
设计模式 缓存 安全
Java设计模式的单例模式应用场景
Java设计模式的单例模式应用场景
21 4
|
6天前
|
设计模式 Java
设计模式在Java项目中的实际应用
设计模式在Java项目中的实际应用
|
4天前
|
设计模式 消息中间件 负载均衡
实现可扩展和可靠的分布式系统的Java设计模式
实现可扩展和可靠的分布式系统的Java设计模式
|
2天前
|
设计模式 算法 搜索推荐
Java中的设计模式及其在实际项目中的应用
Java中的设计模式及其在实际项目中的应用
|
6天前
|
设计模式 缓存 算法
编写高效的Java工具类:实用技巧与设计模式
编写高效的Java工具类:实用技巧与设计模式
|
3天前
|
设计模式 Java 数据安全/隐私保护
Java中的设计模式:从入门到精通
Java中的设计模式:从入门到精通
|
5天前
|
设计模式 缓存 算法
编写高效的Java工具类:实用技巧与设计模式
编写高效的Java工具类:实用技巧与设计模式
|
6天前
|
设计模式 Java 容器
Java多线程编程中的设计模式与挑战
Java多线程编程中的设计模式与挑战
|
9天前
|
设计模式 缓存 Java
Java设计模式:享元模式实现高效对象共享与内存优化(十一)
Java设计模式:享元模式实现高效对象共享与内存优化(十一)