首页 > 其他分享 >05 设计模式-创造型模式-原型模式

05 设计模式-创造型模式-原型模式

时间:2024-10-20 15:49:00浏览次数:10  
标签:设计模式 return value Shape 模式 put numberConverts 创造型 public

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

设计模式,最近持续更新中,如需要请关注

如果你觉得我分享的内容或者我的努力对你有帮助,或者你只是想表达对我的支持和鼓励,请考虑给我点赞、评论、收藏。您的鼓励是我前进的动力,让我感到非常感激。

文章目录

1 概要

原型模式

  • 意图: 使用原型实例指定要创建对象的种类,并通过拷贝这些原型创建新的对象。

  • 主要解决: 在运行时动态建立和删除原型。

何时使用

  • 系统应独立于产品的创建、构成和表示。
  • 需要在运行时指定实例化的类,例如通过动态加载。
  • 避免创建与产品类层次平行的工厂类层次。
  • 类的实例只能有几种不同状态组合,克隆原型比手工实例化更方便。

如何解决
通过已有的一个原型对象,快速生成与原型对象相同的实例。

关键代码

  • 实现克隆操作:
    • 在 Java 中,实现 Cloneable 接口,重写 clone() 方法。
    • 在 .NET 中,使用 Object 类的 MemberwiseClone() 方法实现浅拷贝,或通过序列化实现深拷贝。
  • 隔离类对象的使用者和具体类型之间的耦合关系,要求"易变类"拥有稳定的接口。

应用实例

  • 细胞分裂
  • Java 中的 Object.clone() 方法

优点

  • 性能提高
  • 避免构造函数的约束

缺点

  • 配备克隆方法需要全面考虑类的功能,对已有类可能较难实现,特别是处理不支持串行化的间接对象或含有循环结构的引用时。
  • 必须实现 Cloneable 接口。

使用场景

  • 资源优化
  • 类初始化需要消耗大量资源(如数据、硬件资源)
  • 性能和安全要求高的场景
  • 通过 new 创建对象需要复杂的数据准备或访问权限时
  • 一个对象需要多个修改者
  • 对象需提供给其他对象访问并可能被各个调用者修改时
  • 通常与工厂方法模式一起使用,通过 clone 创建对象,然后由工厂方法提供给调用者

注意事项
与直接实例化类创建新对象不同,原型模式通过拷贝现有对象生成新对象。浅拷贝通过实现 Cloneable 实现,深拷贝通过实现 Serializable 读取二进制流实现。

结构
原型模式包含以下几个主要角色:

  • 原型接口(Prototype Interface): 定义一个用于克隆自身的接口,通常包括一个 clone() 方法。

  • 具体原型类(Concrete Prototype): 实现原型接口的具体类,负责实际的克隆操作。这个类需要实现 clone() 方法,通常使用- 浅拷贝或深拷贝来复制自身。

  • 客户端(Client): 使用原型实例来创建新的对象。客户端调用原型对象的 clone() 方法来创建新的对象,而不是直接使用构造函数。

2 实现

我们将创建一个抽象类 Shape 和扩展了 Shape 类的实体类。下一步是定义类 ShapeCache,该类把 shape 对象存储在一个 Hashtable 中,并在请求的时候返回它们的克隆。

PrototypePatternDemo 类使用 ShapeCache 类来获取 Shape 对象。
在这里插入图片描述

3 Demo代码

在这里插入图片描述
1 创建一个实现了 Cloneable 接口的抽象类。

/**
 * 形状抽象类 - 【实现类Cloneable 可以实现浅拷贝】
 */
public abstract class Shape implements Cloneable {
    private String id;
    protected String type;

    public abstract void draw();

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getType() {
        return type;
    }

    /**
     * 只能实现浅拷贝【String类型也可以实现拷贝】
     *
     * @return
     */
    public Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }

    // 测试引用类型拷贝
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Shape{" +
            "id='" + id + '\'' +
            ", type='" + type + '\'' +
            ", user=" + user +
            '}';
    }
}

2 创建扩展了上面抽象类的实体类。

public class Circle extends Shape {
    public Circle(){
        type = "Circle";
    }

    @Override
    public void draw() {
        System.out.println("Inside Circle::draw() method.");
    }
}

public class Rectangle extends Shape {
    public Rectangle() {
        type = "Rectangle";
    }

    @Override
    public void draw() {
        System.out.println("Inside Rectangle::draw() method.");
    }
}

public class Square extends Shape {
    public Square() {
        type = "Square";
    }

    @Override
    public void draw() {
        System.out.println("Inside Square::draw() method.");
    }
}

public class User {
    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
            "name='" + name + '\'' +
            '}';
    }
}

3 创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable 中。

public class ShapeCache {
    private static Hashtable<String, Shape> shapeMap
        = new Hashtable<String, Shape>();

    public static Shape getShape(String shapeId) {
        Shape cachedShape = shapeMap.get(shapeId);
        return (Shape) cachedShape.clone();
    }

    // 对每种形状都运行数据库查询,并创建该形状
    // shapeMap.put(shapeKey, shape);
    // 例如,我们要添加三种形状
    public static void loadCache() {
        Circle circle = new Circle();
        circle.setId("1");
        User user = new User();
        user.setName("www");
        circle.setUser(user);
        shapeMap.put(circle.getId(),circle);

        Square square = new Square();
        square.setId("2");
        shapeMap.put(square.getId(),square);

        Rectangle rectangle = new Rectangle();
        rectangle.setId("3");
        shapeMap.put(rectangle.getId(),rectangle);
    }
}

4 PrototypePatternDemo 使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。

/**
 * 总结:实际上是用来拷贝对象的
 */
public class PrototypePatternDemo {
    public static void main(String[] args) {
        ShapeCache.loadCache();

        Shape clonedShape = (Shape) ShapeCache.getShape("1");
        System.out.println("Shape : " + clonedShape.getType());

        Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
        System.out.println("Shape : " + clonedShape2.getType());

        Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
        System.out.println("Shape : " + clonedShape3.getType());
        System.out.println("=====================================");

        // 测试深拷贝【引用类型拷贝,会有问题】一个地方修改,别的应用的地方也会被修改
        Shape clonedShape11 = (Shape) ShapeCache.getShape("1");
        Shape clonedShape12 = (Shape) ShapeCache.getShape("1");
        Shape clonedShape13 = (Shape) ShapeCache.getShape("1");

        System.out.println("Shape : " + clonedShape11);
        System.out.println("Shape : " + clonedShape12);
        System.out.println("Shape : " + clonedShape13);

        // clone后,应用类型还是使用同一个。属该引用类型里的属性,其他两个的只也会发生改变。
        clonedShape12.getUser().setName("wyh");
        // 基本类型和string clone后修改对别的没有影响
        clonedShape12.setId("222");
        System.out.println("-----------------------------------");

        System.out.println("Shape : " + clonedShape11);
        System.out.println("Shape : " + clonedShape12);
        System.out.println("Shape : " + clonedShape13);
    }
}

5 执行程序,输出结果:
在这里插入图片描述

4 扩展-不同对象相同属性拷贝

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

/**
 * bean属性拷贝器,标准的bean
 */
@Slf4j
public class BeanPropsCopier {
    private static NumberTypeConverts numberTypeConverts = new NumberTypeConverts();

    private BeanCopier copier;

    private Converter converter;

    /**
     * 构造一个同类型的bean之间拷贝器
     *
     * @param clazz bean的类型
     */
    public BeanPropsCopier(Class<?> clazz) {
        Objects.requireNonNull(clazz);

        copier = BeanCopier.create(clazz, clazz, false);
    }

    /**
     * 构造一个不同类型的bean之间拷贝器
     *
     * @param sourceClazz 来源的类型
     * @param targetClazz 目标的类型
     */
    public BeanPropsCopier(Class<?> sourceClazz, Class<?> targetClazz) {
        Objects.requireNonNull(sourceClazz);
        Objects.requireNonNull(targetClazz);

        copier = BeanCopier.create(sourceClazz, targetClazz, true);
        this.converter = numberTypeConverts.getConverter();
    }

    /**
     * 拷贝属性
     *
     * @param from 来源对象
     * @param to 目标对象
     */
    public void copy(Object from, Object to) {
        copier.copy(from, to, converter);
    }

    private static class NumberTypeConverts {
        private Map<String, Function<Object, ? extends Object>> numberConverts = new HashMap<>();

        private List<String> intergalNames = Arrays.asList(Byte.class.getSimpleName(), Short.class.getSimpleName(),
            Character.class.getSimpleName(), Integer.class.getSimpleName(), Long.class.getSimpleName());

        private String floatName = Float.class.getSimpleName();

        private String doubleName = Double.class.getSimpleName();

        private NumberTypeConverts() {
            prepare();
        }

        @SuppressWarnings(value = {"rawtypes", "unchecked"})
        private Converter getConverter() {
            return (Object value, Class fieldClazz, Object setMethodName) -> {
                if (value == null) {
                    return null;
                }
                Class<?> valueClass = value.getClass();
                if (valueClass == fieldClazz || fieldClazz.isAssignableFrom(valueClass)) {
                    return value;
                }
                if (valueClass == Boolean.class && (fieldClazz == Boolean.class || fieldClazz == boolean.class)) {
                    return value;
                }
                String key = value.getClass().getSimpleName() + fieldClazz.getSimpleName().toLowerCase(Locale.ENGLISH);
                Function<Object, ? extends Object> numberConvert = numberConverts.get(key);
                if (null != numberConvert) {
                    return numberConvert.apply(value);
                }
                return null;
            };
        }

        private void prepare() {

            toByte();

            toShort();

            toChar();

            toInt();

            toLong();

            toFloat();

            toDouble();

        }

        private void toDouble() {
            Function<Object, Double> intergal2Double = (Object value) -> {
                // unbox
                long tmp = (long) value;
                return (double) tmp;
            };

            Function<Object, Double> floatal2Double = (Object value) -> {
                // unbox
                return (double) value;
            };

            intergalNames.forEach(intergalName -> numberConverts.put(intergalName + "double", intergal2Double));
            numberConverts.put(floatName + "double", floatal2Double);
            numberConverts.put(doubleName + "double", floatal2Double);
        }

        private void toFloat() {
            Function<Object, Float> intergal2Float = (Object value) -> {
                // unbox
                long tmp = (long) value;
                return (float) tmp;
            };

            Function<Object, Float> floatal2Float = (Object value) -> {
                // unbox
                double tmp = (double) value;
                return (float) tmp;
            };
            intergalNames.forEach(intergalName -> numberConverts.put(intergalName + "float", intergal2Float));
            numberConverts.put(floatName + "float", floatal2Float);
            numberConverts.put(doubleName + "float", floatal2Float);
        }

        private void toLong() {
            Function<Object, Long> intergal2Long = (Object value) -> {
                // unbox
                return (long) value;
            };

            Function<Object, Long> floatal2Long = (Object value) -> {
                // unbox
                double tmp = (double) value;
                return (long) tmp;
            };

            intergalNames.forEach(intergalName -> numberConverts.put(intergalName + "long", intergal2Long));
            numberConverts.put(floatName + "long", floatal2Long);
            numberConverts.put(doubleName + "long", floatal2Long);
        }

        private void toInt() {
            Function<Object, Integer> intergal2Int = (Object value) -> {
                // unbox
                long tmp = (long) value;
                return (int) tmp;
            };

            Function<Object, Integer> floatal2Int = (Object value) -> {
                // unbox
                double tmp = (double) value;
                return (int) tmp;
            };

            intergalNames.forEach(intergalName -> numberConverts.put(intergalName + "int", intergal2Int));
            numberConverts.put(floatName + "int", floatal2Int);
            numberConverts.put(doubleName + "int", floatal2Int);

            intergalNames.forEach(intergalName -> numberConverts.put(intergalName + "integer", intergal2Int));
            numberConverts.put(floatName + "integer", floatal2Int);
            numberConverts.put(doubleName + "integer", floatal2Int);
        }

        private void toChar() {
            Function<Object, Character> intergal2Char = (Object value) -> {
                // unbox
                long tmp = (long) value;
                return (char) tmp;
            };

            Function<Object, Character> floatal2Char = (Object value) -> {
                // unbox
                double tmp = (double) value;
                return (char) tmp;
            };

            intergalNames.forEach(intergalName -> numberConverts.put(intergalName + "character", intergal2Char));
            numberConverts.put(floatName + "character", floatal2Char);
            numberConverts.put(doubleName + "character", floatal2Char);

            intergalNames.forEach(intergalName -> numberConverts.put(intergalName + "char", intergal2Char));
            numberConverts.put(floatName + "char", floatal2Char);
            numberConverts.put(doubleName + "char", floatal2Char);
        }

        private void toShort() {
            Function<Object, Short> intergal2Short = (Object value) -> {
                // unbox
                long tmp = (long) value;
                return (short) tmp;
            };

            Function<Object, Short> floatal2Short = (Object value) -> {
                // unbox
                double tmp = (double) value;
                return (short) tmp;
            };

            intergalNames.forEach(intergalName -> numberConverts.put(intergalName + "short", intergal2Short));
            numberConverts.put(floatName + "short", floatal2Short);
            numberConverts.put(doubleName + "short", floatal2Short);
        }

        private void toByte() {
            Function<Object, Byte> intergal2Byte = (Object value) -> {
                // unbox
                long tmp = (long) value;
                return (byte) tmp;
            };

            Function<Object, Byte> floatal2Byte = (Object value) -> {
                // unbox
                double tmp = (double) value;
                return (byte) tmp;
            };
            intergalNames.forEach(intergalName -> numberConverts.put(intergalName + "byte", intergal2Byte));
            numberConverts.put(floatName + "byte", floatal2Byte);
            numberConverts.put(doubleName + "byte", floatal2Byte);
        }
    }

    /**
     * 功能 : 只复制source对象的非空属性到target对象上
     *
     * @param source 源对象
     * @param target 目标对象
     * @throws BeansException 异常
     */
    public static void copyNoNullProperties(Object source, Object target) throws Exception {
        if (source == null || target == null) {
            log.error("Bean should not be null");
            throw new Exception("");
        }
        Class<?> targetClass = target.getClass();
        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(targetClass);
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            if (propertyDescriptor.getWriteMethod() != null) {
                PropertyDescriptor sourcePropertyDescriptor = BeanUtils.getPropertyDescriptor(source.getClass(),
                    propertyDescriptor.getName());
                if (sourcePropertyDescriptor != null && sourcePropertyDescriptor.getReadMethod() != null) {
                    try {
                        Method descriptorReadMethod = sourcePropertyDescriptor.getReadMethod();
                        if (!Modifier.isPublic(descriptorReadMethod.getDeclaringClass().getModifiers())) {
                            descriptorReadMethod.setAccessible(true);
                        }
                        Object sourceValue = descriptorReadMethod.invoke(source);
                        // 这里判断以下value是否为空 当然这里也能进行一些特殊要求的处理 例如绑定时格式转换等等
                        if (sourceValue != null) {
                            Method writeMethod = propertyDescriptor.getWriteMethod();
                            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                writeMethod.setAccessible(true);
                            }
                            writeMethod.invoke(target, sourceValue);
                        }
                    } catch (Exception e) {
                        log.error("copy bean error.", e);
                        throw new Exception("");
                    }
                }
            }
        }
    }
}

标签:设计模式,return,value,Shape,模式,put,numberConverts,创造型,public
From: https://blog.csdn.net/wyh106115/article/details/143061257

相关文章

  • 基于x86_64汇编语言简单教程5: 寻址模式与gdb调试
    目录寻址模式gdb调试示例文件打断点办法1:给函数名称和行号打上断点办法2:在地址上打断点打印变量layout命令打印十六进制(特殊)countformatsize再次介绍mov寻址模式我们无非就是频繁的跟数据自己打交道,那么问题来了,我们可以如何找到这些数据呢?这就是我们这篇教......
  • 常见的性能效率失效模式及原因
    目录一、在所有负载水平下响应缓慢二、中高负载下反应缓慢三、随着时间的推移,响应降低四、高负载或超高负载下出错处理不充分或粗暴五、高延迟六、低吞吐量七、资源耗尽八、负载下的稳定性差九、扩展性问题在动态测试过程中可以发现许多不同的性能效率失效模式,以下......
  • 用C++实现自己的智能指针:深入探讨内存管理与RAII模式
    解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界C++中的内存管理一直以来是程序员的一个难点,尤其是在处理动态内存分配时。智能指针(如std::unique_ptr和std::shared_ptr)通过RAII(资源获取即初始化)的设计理念,极大地简化了动态内存的管理,减少了内存泄漏的风险。然......
  • 【设计模式系列】命令模式
    目录一、什么是命令模式二、命令模式的角色三、命令模式的典型应用场景四、命令模式在Runnable中的应用一、什么是命令模式命令模式(CommandPattern)是一种行为设计模式,它将一个请求或简单操作封装为一个对象。这个模式提供了一种机制,使得你可以将操作的发出者(发起请求......
  • Java设计模式——适配器模式
    适配器模式(AdapterPattern)是一种结构型设计模式,它用于将一个类的接口转换成客户端希望的另一个接口,使得原本由于接口不兼容而无法一起工作的类可以协同工作。适配器模式的主要目的是兼容性和重用性,特别是在使用一些已有的类,而它们的接口又不符合要求时。适配器模式的核心思想......
  • 【转】[WPF] 关闭模式决定了应用程序何时关闭
    在WPF(WindowsPresentationFoundation)中,关闭模式(ShutdownMode)决定了应用程序何时关闭。可以通过以下方式设置关闭模式:XAML设置:在 App.xaml 文件中,可以通过 ShutdownMode 属性来设置关闭模式。WPF提供了三种关闭模式:OnLastWindowClose:默认值,当最后一个窗口关闭时,......
  • 单模式匹配 KMP 算法 简易版学习笔记
    KMP前缀函数设\(S_i\)为字符串\(S\)的第\(i\)个位置。我们设\(\pi(i)\)表示字符串以\(i\)结尾的前缀的最长公共前后缀的长度。这里的前后缀都指的是真前缀、真后缀。怎么\(O(n)\)求出\(\pi(i)\)。性质:相邻的\(\pi\)至多增加1。因此,若\(s[\pi(i)+1]=s[i+1......
  • 中介者模式 (Mediator Pattern)
    好的,我们来深入探讨中介者模式(MediatorPattern),并结合多个事件通道(EventChannels)和多个事件处理器(EventProcessors)的场景进行详细的讲解。中介者模式的详细讲解:在更复杂的事件驱动架构中,系统中的各个组件可能涉及不同的事件流。每个事件通道(EventChannel)和......
  • 考研数据结构-串(串的模式匹配算法)
             串的基本操作可以参照考研数据结构-串(串的基本操作),除去这些基本操作外,还有一个重要的操作就是串的模式匹配算法。模式匹配算法就是,对于一个串中某个子串的定位操作,这里会讲两种模式匹配算法:简单模式匹配算法和KMP算法。一、简单模式匹配算法   简单......
  • java_day19_线程组、线程池、定时器、InetAddress、网络编程、设计模式
    一、线程组:线程组:将属于同一类的线程划分到同一组中,可以直接对线程组进行设置。ThreadGroup构造方法:ThreadGroup(Stringname)构造一个新的线程组。代码案例:classMyThread1extendsThread{publicMyThread1(){}publicMyThread1(ThreadGr......