原型模式(Prototype Pattern)

364 阅读6分钟

在设计模式中按照不同的处理方式共包含三大类:创建型模式、结构型模式和行为模式。

创建型模式

  1. 工厂方法模式(Factory Method Pattern)
  2. 抽象工厂模式(Abstract Factory Pattern)
  3. 建造者模式(Builder Pattern)
  4. 原型模式(Prototype Pattern)
  5. 单例模式(Singleton Pattern)

定义

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

接下来举一个出题试卷的例子

假如一套试卷里面都是选择题。现在要求分为多套试卷的形式。让每个学生得到的全部题目是一样。但是题目顺序和选项顺序不一样。 普通做法是通过创建N个不同的试卷对象来满足要求。这样做的话创建对象的代价就比较大。 采用原型模式时。可以通过初始化创建一个对象。需要获取其他不同的试卷时。则在clone()方法中进行题目的排序,以及选项的排序重组

原型模式代码

/**
 * 选择题
 * @author: yangqiang
 * @date 2021-07-19 19:22
 */
@Data
public class ChoiceQuestion {

    /**
     * 题目
     * */
    private String name;
    /**
     * 选项 A B C D
     * */
    private Map<String, String> option;
    /**
     * 题目答案
     * */
    private String key;

    public ChoiceQuestion() {
    }

    public ChoiceQuestion(String name, Map<String, String> option, String key) {
        this.name = name;
        this.option = option;
        this.key = key;
    }
}
/**
 * 试卷
 * @author yangqiang
 * @date 2021-07-19 19:22
 */
@Data
public class TestPaper implements Cloneable{
    /**姓名*/
    private String name;
    /**学号*/
    private Integer number;
    /**考题*/
    private List<ChoiceQuestion> choiceQuestionList;


    @Override
    protected Object clone() throws CloneNotSupportedException {
        TestPaper testPaper =(TestPaper) super.clone();
        List<ChoiceQuestion> choiceQuestionList = testPaper.getChoiceQuestionList();
        //现在开始打乱顺序
        //题目乱序
        Collections.shuffle(choiceQuestionList);
        reorder(choiceQuestionList);
        return testPaper;
    }
    /**
     * 给试卷的选项重新排序
     * */
    private void reorder(List<ChoiceQuestion> choiceQuestionList) {
        choiceQuestionList.forEach(item->{
            String key = item.getKey();
            Map<String, String> optionMap = item.getOption();
            List<String> keyList = new ArrayList<>(optionMap.keySet());
            Collections.shuffle(keyList);
            String answer = optionMap.get(key);
            int i = 0;
            Collection<String> values = optionMap.values();
            Map<String, String> newOptionMap = new HashMap<>();
            for(String value:values){
                if(value.equals(answer)){
                    item.setKey(keyList.get(i));
                }
                newOptionMap.put(keyList.get(i),value);
                i ++ ;
            }
            item.setOption(newOptionMap);
        });
    }
    /**
     * 预设好题目
     * */
    public TestPaper() {
        List<ChoiceQuestion> choiceQuestionArrayList = new ArrayList<>();

        Map<String, String> map1 = new HashMap<String, String>();
        //  A、2.4 B、24 C、240 D、0.024
        map1.put("A", "2.4");
        map1.put("B", "24");
        map1.put("C", "240");
        map1.put("D", "0.024");
        choiceQuestionArrayList.add(new ChoiceQuestion("1、两数相除商是2.4,如果被除数扩大100倍,除数除以0.01,商是( )。",map1,"A"));
        Map<String, String> map2 = new HashMap<String, String>();
        //  A、(a+b)b B、a(a+b) C、(a+0)b D、(a+1)b
        map2.put("A", "(a+b)b");
        map2.put("B", "a(a+b)");
        map2.put("C", "(a+0)b");
        map2.put("D", "(a+1)b");
        choiceQuestionArrayList.add(new ChoiceQuestion("2、用乘法分配律可以将ab+b改写成( )。",map2,"D"));
        Map<String, String> map3 = new HashMap<String, String>();
        //  A、锐角三角形 B、直角三角形 C、钝角三角形 D、以上答案都不对
        map3.put("A", "锐角三角形");
        map3.put("B", "直角三角形");
        map3.put("C", "钝角三角形");
        map3.put("D", "以上答案都不对");
        choiceQuestionArrayList.add(new ChoiceQuestion("3、一个等腰三角形,一个底角与顶角度数的比是2: 1,则这个等腰三角形也是( )。",map3,"A"));
        Map<String, String> map4 = new HashMap<String, String>();
        //  A、5千米 B、10千米 C、13 千米 D、30千米
        map4.put("A", "5千米");
        map4.put("B", "10千米;");
        map4.put("C", "13 千米");
        map4.put("D", "30千米");
        choiceQuestionArrayList.add(new ChoiceQuestion("4、小明步行3小时走了20千米的路程,骑自行车沿原路返回刚好用1小时。小明往返的平均速度是每小时( )。",map4,"B"));
        this.choiceQuestionList = choiceQuestionArrayList;
    }
    /**
     * 获取clone的试卷
     * */
    public TestPaper getTestPaper(String name,Integer number){
        this.name = name ;
        this.number = number;
        try {
            this.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return this;
    }

}
/**
 * @author yangqiang
 * @date 2021-07-19 20:01
 */
public class PrototypeTest {
    public static void main(String[] args) {
        TestPaper testPaper = new TestPaper();
        TestPaper paper1 = testPaper.getTestPaper("a", 1);
        List<ChoiceQuestion> choiceQuestionList = paper1.getChoiceQuestionList();
        System.out.println(choiceQuestionList);
    }
}

结果

[ChoiceQuestion(name=4、小明步行3小时走了20千米的路程,骑自行车沿原路返回刚好用1小时。小明往返的平均速度是每小时( )。, option={A=10千米;, B=5千米, C=13 千米, D=30千米}, key=A), ChoiceQuestion(name=1、两数相除商是2.4,如果被除数扩大100倍,除数除以0.01,商是( )。, option={A=24, B=240, C=0.024, D=2.4}, key=D), ChoiceQuestion(name=3、一个等腰三角形,一个底角与顶角度数的比是2: 1,则这个等腰三角形也是( )。, option={A=钝角三角形, B=直角三角形, C=以上答案都不对, D=锐角三角形}, key=D), ChoiceQuestion(name=2、用乘法分配律可以将ab+b改写成( )。, option={A=(a+1)b, B=a(a+b), C=(a+b)b, D=(a+0)b}, key=A)]

这样的话可以得到多个不同的对象。但是在创建过程中只创建了一个TestPaper对象

  • 以上的实际场景模拟了原型模式在开发中重构的作用,但是原型模式的使用频率确实不是很高。如果有一些特殊场景需要使用到,也可以按照此设计模式进行优化。
  • 另外原型设计模式的优点包括;便于通过克隆方式创建复杂对象、也可以避免重复做初始化操作、不需要与类中所属的其他类耦合等。但也有一些缺点如果对象中包括了循环引用的克隆,以及类中深度使用对象的克隆,都会使此模式变得异常麻烦。
  • 终究设计模式是一整套的思想,在不同的场景合理的运用可以提升整体的架构的质量。永远不要想着去硬凑设计模式,否则将会引起过渡设计,以及在承接业务反复变化的需求时造成浪费的开发和维护成本。
  • 初期是代码的优化,中期是设计模式的使用,后期是把控全局服务的搭建。不断的加强自己对全局能力的把控,也加深自己对细节的处理。可上可下才是一个程序员最佳处理方式,选取做合适的才是最好的选择。 扩展

深克隆与浅克隆

浅克隆:只克隆对象不克隆对象的引用对象属性 。造成克隆的对象的引用对象属性改变时 。原来的对象也跟着改变

深克隆:调用当前对象的clone()方法时也调用引用对象的clone()方法 这样克隆对象的引用对象和原来的引用对象不是一个。

浅克隆举例

/**
 * @author yangqiang
 * @date 2021-07-20 15:06
 */
@Data
public class Teacher implements Cloneable{
    private String name;
    private Student student;

    public Teacher(String name, Student student) {
        this.name = name;
        this.student = student;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
/**
 * @author yangqiang
 * @date 2021-07-20 15:06
 */
@Data
@AllArgsConstructor
public class Student {
    private String name;
}
/**
 * @author yangqiang
 * @date 2021-07-20 15:07
 */
public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher = new Teacher("teacher1",new Student("student1"));
        Teacher clone = (Teacher) teacher.clone();
        clone.getStudent().setName("student2");
        System.out.println(teacher);
        System.out.println(clone);
    }
}

结果

 Teacher(name=teacher1, student=Student(name=2))  
 Teacher(name=teacher1, student=Student(name=2))

浅克隆举例

/**
 * @author yangqiang
 * @date 2021-07-20 15:06
 */
@Data
public class Teacher implements Cloneable{
    private String name;
    private Student student;

    public Teacher(String name, Student student) {
        this.name = name;
        this.student = student;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Teacher teacher = (Teacher)super.clone();
        teacher.setStudent((Student)teacher.getStudent().clone());
        return teacher;
    }
}
/**
 * @author yangqiang
 * @date 2021-07-20 15:06
 */
@Data
@AllArgsConstructor
public class Student implements Cloneable{
    private String name;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

结果

Teacher(name=teacher1, student=Student(name=student1))
Teacher(name=teacher1, student=Student(name=student2))