我们学习设计模式的时候学了原型模式,原型模式在我们的实际开发中使用场景也是很多的,我在实际开发中主要有以下两大场景:
1、在我们希望接下来这个类的操作与之前这个类的操作之间相互不影响;
2、通常在异步操作的时候避免多线程问题,在调用方法的时候不希望被调用方法对对象的操作影响本层的剩余业务逻辑处理;
在需要这样的时候我可以直接new一个对象自己赋值不采用原型模式呢?
当然可以,只要你不嫌麻烦,前辈们为我们提供了原型模式肯定是因为原型模式对我们有大大的好处的,一般来说原型模式给我们带来了以下的好处:
1、程序运行效率:可以节省new创建对象的时间,
2、代码开发效率:可以一个clone()方法搞定,无需复杂的赋值操作
3、代码的稳定性:依次一个一个的进行代码赋值,会不会因为一个Cv操作失误导致一个细微难察觉的代码bug发布出来了呢?
那么首先我们来看看要通过clone实现原型模式的代码吧
//第一步:对象实现Cloneable接口 //第二步:重写clone方法; public Object clone() throws CloneNotSupportedException{ Student student=(Student)super.clone(); student.setStudentFiles((StudentFiles)studentFiles.clone()); return student; } //当我们需要进行深克隆的时候可以通过序列化的方式实现;
但是在实际开发中就会发现当我们需要使用原型模式的时候还需要去对对象model进行clone这种方式真的不是很友好;
what? 不友好吗?
当然,首先,这不符合开闭原则,我们在使用前并不知道这个需要使用原型模式,当需要使用的时候还需要去改老代码,这不是不符合开闭原则吗?
其次,我只是需要一个克隆对象,还需要去实现一堆复杂的浅克隆,深克隆代码吗?是不是有点太耗费功夫了呢?是的,作为程序员的我就是这么懒;
那么我们不使用clone,怎么快速友好,非侵入的实现原型模式呢?
这个时候我们的猪脚就上场了,而且还是两个
org.springframework.beans.BeanUtils
org.apache.commons.beanutils.BeanUtils
按照惯例,我们分析就从源码进入:
org.springframework.beans.BeanUtils
//使用无参数构造函数实例化类的便利方法。 public static <T> T instantiate(Class<T> clazz) throws BeanInstantiationException { Assert.notNull(clazz, "Class must not be null"); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { return clazz.newInstance(); } catch (InstantiationException ex) { throw new BeanInstantiationException(clazz, "Is it an abstract class?", ex); } catch (IllegalAccessException ex) { throw new BeanInstantiationException(clazz, "Is the constructor accessible?", ex); } } //使用无参数的构造函数实例化一个类。 public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException { Assert.notNull(clazz, "Class must not be null"); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { return instantiateClass(clazz.getDeclaredConstructor()); } catch (NoSuchMethodException ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } //将给定源bean的属性值复制到目标bean中。 public static void copyProperties(Object source, Object target) throws BeansException { copyProperties(source, target, null, (String[]) null); } //将给定源bean的属性值复制到给定目标bean中, //只设置在给定的“可编辑”类(或接口)中定义的属性。 public static void copyProperties(Object source, Object target, Class<?> editable) throws BeansException { copyProperties(source, target, editable, (String[]) null); } //将给定源bean的属性值复制到给定目标bean中,忽略给定的“ignoreProperties”。 public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException { copyProperties(source, target, null, ignoreProperties); } //真正的属性拷贝处理方法: //将给定源bean的属性值复制到给定目标bean中。 private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties) throws BeansException { Assert.notNull(source, "Source must not be null"); Assert.notNull(target, "Target must not be null"); Class<?> actualEditable = target.getClass(); if (editable != null) { if (!editable.isInstance(target)) { throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]"); } actualEditable = editable; } PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null); for (PropertyDescriptor targetPd : targetPds) { Method writeMethod = targetPd.getWriteMethod(); if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) { PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); if (sourcePd != null) { Method readMethod = sourcePd.getReadMethod(); if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { try { if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { readMethod.setAccessible(true); } Object value = readMethod.invoke(source); if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); } writeMethod.invoke(target, value); } catch (Throwable ex) { throw new FatalBeanException( "Could not copy property '" + targetPd.getName() + "' from source to target", ex); } } } } } }
org.apache.commons.beanutils.BeanUtils
//根据可用的属性getter和setter克隆一个bean,即使bean类本身没有实现Cloneable。 public static Object cloneBean(Object bean) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException { return BeanUtilsBean.getInstance().cloneBean(bean); } //对于所有属性名相同的情况,将属性值从原始bean复制到目标bean。 public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException { BeanUtilsBean.getInstance().copyProperties(dest, orig); } //将指定的属性值复制到指定的目标bean,执行所需的任何类型转换。 public static void copyProperty(Object bean, String name, Object value) throws IllegalAccessException, InvocationTargetException { BeanUtilsBean.getInstance().copyProperty(bean, name, value); } //返回指定bean提供读方法的整个属性集。 public static Map describe(Object bean) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return BeanUtilsBean.getInstance().describe(bean); } //以字符串数组的形式返回指定bean的指定数组属性的值。 public static String[] getArrayProperty(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return BeanUtilsBean.getInstance().getArrayProperty(bean, name); } //以字符串的形式返回指定bean的指定索引属性的值。 public static String getIndexedProperty(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return BeanUtilsBean.getInstance().getIndexedProperty(bean, name); } //以字符串的形式返回指定bean的指定索引属性的值。 public static String getIndexedProperty(Object bean, String name, int index) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return BeanUtilsBean.getInstance().getIndexedProperty(bean, name, index); } //以字符串的形式返回指定bean的指定索引属性的值。 public static String getMappedProperty(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return BeanUtilsBean.getInstance().getMappedProperty(bean, name); } //以字符串的形式返回指定bean的指定索引属性的值。 public static String getMappedProperty(Object bean, String name, String key) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return BeanUtilsBean.getInstance().getMappedProperty(bean, name, key); } //为指定bean返回指定名称(可能是嵌套的)属性的值,作为字符串。 public static String getNestedProperty(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return BeanUtilsBean.getInstance().getNestedProperty(bean, name); } //以字符串的形式返回指定bean的指定属性的值,无论使用哪种属性引用格式。 public static String getProperty(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return BeanUtilsBean.getInstance().getProperty(bean, name); } //返回转换为字符串的指定bean的指定简单属性的值。 public static String getSimpleProperty(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return BeanUtilsBean.getInstance().getSimpleProperty(bean, name); } //根据指定的名称/值对填充指定bean的javabean属性。 public static void populate(Object bean, Map properties) throws IllegalAccessException, InvocationTargetException { BeanUtilsBean.getInstance().populate(bean, properties); } //设置指定的属性值,根据需要执行类型转换以符合目标属性的类型。 public static void setProperty(Object bean, String name, Object value) throws IllegalAccessException, InvocationTargetException { BeanUtilsBean.getInstance().setProperty(bean, name, value); }
使用推荐:
spring 与apache的BeanUtils相比,在性能上spring有极大的优势,因此在简单做属性赋值的时候推荐使用spring的,经过测试在大规模的赋值操作中spring性能可以达到apace性能的100倍;