zoukankan      html  css  js  c++  java
  • 一天一个设计模式——Prototype 原型模式

    一、模式说明

       看了比较多的资料,对原型模式写的比较复杂,个人的理解就是模型复制,根据现有的类来直接创建新的类,而不是调用类的构造函数。

      那为什么不直接调用new方法来创建类的实例呢,主要一个原因是如果类的构造函数比较复杂,又需要大量的类实例对象,且这些类比较类似的时候,就可以使用原型模式,调用对象的克隆方法快读得到实例。另一个原因是,使用构造函数创建类时,语句MyClass myInstance = new MyClass();  这个Java语句中,我们使用了要创建的类的名字:MyClass,如此一来,就讲MyClass类和当前语句所在的类紧密耦合在一起了,而且,如果我们还不知道要创建的类的名字,但是想先定义类的创建方式,就需要使用原型模式。

    二、原型模式的类图

    三、原型模式中的角色

    • 原型(Protype)角色:负责定义复制现有实例生成新实例的方法
    • 具体原型(ConcretePrototype)角色:负责实现复制现有实例生成新实例的方法
    • 使用者(Client)角色:负责使用复制实例的方法生成新实例

    四、代码示例

    1.Product类:

    package com.designpattern.cn.protptypepattern.patternframework;
    
    public interface Product extends Cloneable {
        public abstract void use(String s);
        public abstract Product createClone();
    }
    View Code

      上面的Product类很简单,只是继承了java.lang.Clonable接口,该接口中并没有要求实现任何方法,这是一个标记接口,被该接口标记的类可以调用Clone方法来克隆类实例。需要注意的是,Clone方法并不是定义在Clonable接口中,而是定义在java.lang.Object中,另外需要提一点,Clone方法实现的是浅复制。

    2.Manager类:

    package com.designpattern.cn.protptypepattern.patternframework;
    
    import java.util.HashMap;
    
    public class Manager {
        private HashMap showcase = new HashMap();
        public void register(String name, Product product){
            showcase.put(name, product);
        }
        public Product create(String protoname){
            Product p = (Product) showcase.get(protoname);
            return p.createClone();
        }
    }
    View Code

      上面的Manager类提供了register方法,将字符串和Product接口注册保存到showcase中,现在还无法知道Product具体类是什么,但是可以确定这个具体类是实现了Product接口的,因此这个类可以调用use方法和createClone方法创建实例的克隆。

      接下来创建几种不同的Product具体的子类,每个子类都实现了Product接口:

    3-1.MessageBox消息盒子类:

    package com.designpattern.cn.protptypepattern.patterndemostrate;
    
    import com.designpattern.cn.protptypepattern.patternframework.Product;
    
    public class MessageBox implements Product {
        private char decochar;
        public MessageBox(char decochar){
            this.decochar = decochar;
        }
    
        public void use(String s){
            int length = s.getBytes().length;
            for(int i = 0; i < length + 4; i++){
                System.out.print(decochar);
            }
            System.out.println("");
            System.out.println(decochar+" "+s+" "+decochar);
            for(int i = 0; i < length + 4; i++){
                System.out.print(decochar);
            }
            System.out.println("");
        }
    
        public Product createClone(){
            Product p = null;
            try {
                p = (Product) clone();
            }catch (CloneNotSupportedException e){
                e.printStackTrace();
            }
            return p;
        }
    }
    View Code

    3-2UnderlinePen类:

    package com.designpattern.cn.protptypepattern.patterndemostrate;
    
    import com.designpattern.cn.protptypepattern.patternframework.Product;
    
    public class UnderlinePen implements Product {
        private char ulchar;
        public UnderlinePen(char ulchar){
            this.ulchar = ulchar;
        }
    
        public void use(String s){
            int length = s.getBytes().length;
            System.out.println(""" + s + """);
            System.out.println(" ");
            for(int i = 0; i < length + 4; i++){
                System.out.print(ulchar);
            }
            System.out.println("");
        }
    
        public Product createClone(){
            Product p = null;
            try {
                p = (Product) clone();
            }catch (CloneNotSupportedException e){
                e.printStackTrace();
            }
            return p;
        }
    }
    View Code

      从上面两个类可以看出,Manager类的create方法中并没有出现MessageBox和UnderlinePen这些字眼,取而代之使用的是自己定义的字符串,并将这个字符串注册到Manager类中,后续根据字符串来得到类实例(通过原型克隆得到)。如此一来,就将框架和具体的类名解耦开了。

    4.测试类和运行结果:

    原型模式在JavaScript中也有应用,后续再补充这方面的内容,可以对比看下Java和Javascript中实现原型模式的不同。

    ---------------------2019-06-27 00:13 比较晚了,今天加班到10点回家,原型模式还是比较重要的,这篇随笔后续继续补充-----------------------------

    五、JavaScript中的原型模式

      在《Javascript 高级程序设计》一书中,在讲如何在Javascript中创建对象时,是这样介绍原型模式的:prototype是通过调用构造函数而创建的那个对象实例的原型对象。这句话反过来说:我们创建的每个函数都有一个prototype属性,它是一个指针,指向一个对象,这个对象包含了可以由特定类型的所有实例共享的属性和方法。总之:不必在构造函数中定义对象实例信息,而是将这些信息直接添加到原型对象中。

      举例说明:

      上面代码看出,当创建一个函数时(即使函数没有包含任何内容),就会创建一个prototype属性,该属性指向函数的原型对象,所有的原型对象会有一个constructor属性,该属性是一个指针,指向prototype属性所在的函数的指针。上面的例子:Person.prototype.constructor  指向 Person。

      使用原型对象的好处是:所有原型对象的实例都共享原型的属性和方法。换种说法,不必在构造函数中定义对象实例的信息,而是将这些对象信息放到原型对象中。

      由此我们可以想:如果我们自己创建一个对象,作为另一个对象的prototype属性值,然后通过Object.create(prototype, optionalDescriptorObjects)来实现原型继承。

      继续完善上面的代码:

      如果我们要自己实现原型模式:

    六、与原型模式相关的模式

    • Flyweight享元模式:Prototype原型模式用于创建与当前实例完全相同的实例,Flyweight模式可以在不同的地方使用同一个实例
    • Memento备忘录模式:备忘录模式可以保存当前实例的状态,实现快照和撤销功能
    • Composite组合模式
    • Command命令模式:想要复制命令模式中的命令时,可以使用Protype模式。
  • 相关阅读:
    UVALive2287 POJ1047 HDU1313 ZOJ1073 Round and Round We Go【大数+数学计算】
    HDU1559 最大子矩阵【DP】
    51Nod-1050 循环数组最大段和【最大子段和+最小子段和+DP】
    51Nod-1051 最大子矩阵和【最大子段和+DP】
    UVALive2288 POJ1050 HDU1081 ZOJ1074 To The Max【最大子段和+DP】
    UVALive2363 POJ1005 HDU1065 ZOJ1049 I Think I Need a Houseboat【数学计算】
    UVALive6050 Primes【素数筛选+前缀和】
    POJ3978 Primes【素数筛选+前缀和】
    sql里的多行多列转一行多列小技巧
    实体类作为另一个实体类的属性
  • 原文地址:https://www.cnblogs.com/zheng-hong-bo/p/11094496.html
Copyright © 2011-2022 走看看