• Design Pattern: Prototype 模式


    一句话概括:用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。

    您从图书馆的期刊从发现了几篇您感兴趣的文章,由于这是图书馆的书,您不可以直接在书中作记号或写字,所以您将当中您所感兴趣的几个主题影印出来,这下子您就可在影印的文章上画记重点。
    Prototype模式的作用有些类似上面的描述,您在父类别中定义一个clone()方法,而在子类别中重新定义它,当客户端对于所产生的物件有兴趣并想加以利用,而您又不想破坏原来的物件,您可以产生一个物件的复本给它。
    Prototype具有展示的意味,就像是展览会上的原型车款,当您对某个车款感兴趣时,您可以购买相同款示的车,而不是车展上的车。
    在软体设计上的例子会更清楚的说明为何要进行物件复制,假设您要设计一个室内设计软体,软体中有一个展示家具的工具列,您只要点选工具列就可以产生一个家 具复本,例如一张椅子或桌子,您可以拖曳这个复制的物件至设计图中,随时改变它的位置、颜色等等,当您改变设计图中的物件时,工具列上的原型工具列是不会 跟着一起改变的,这个道理是无需解释的。
    下面的 UML 类别图表示了上述的简单概念:

    Prototype

    Prototype模式的重点在于clone(),它负责复制物件本身并传回,但这个clone()本身在实作上存在一些困难,尤其是当物件本身又继承另一个物件时,如何确保复制的物件完整无误,在不同的程式语言中有不同的作法。
    在Java中的作法是透过实作一个Cloneable介面,它只是一个声明的介面,并无规定任何实作的方法,您的目的是改写Object的clone ()方法,使其具备有复制物件的功能,这个方面建议您参考:How to avoid traps and correctly override methods from java.lang.Object
    用一个简单的例子来实作上图中的结构,这个例子利用了Java语言本身的clone特性:

    AbstractFurniture.java

    public abstract class AbstractFurniture 
                                 implements Cloneable {
        public abstract void draw();
        
        // 在Design Pattern上,以下的clone是抽象未实作的
        // 实际上在Java中class都继承自Object
        // 所以在这边我们直接重新定义clone() 
        // 这是为了符合Java现行的clone机制
        protected Object clone() throws CloneNotSupportedException { 
            return super.clone(); 
        }
    }

    CircleTable与SquareTable继承了AbstractFurniture,并实作clone方法,用于传回本身的复制品:

    • CircleTable.java

    import java.awt.*;
    
    public class CircleTable extends AbstractFurniture {
        protected Point center;    
        
        public void setCenter(Point center) {
            this.center = center;
        }
        
        protected Object clone () 
                         throws CloneNotSupportedException { 
            Object o = super.clone(); 
            if(this.center != null) {
                ((CircleTable) o).center = (Point) center.clone();
            }
            
            return o; 
        } 
    
        public void draw() { 
            System.out.println("\t圆桌\t中心:(" + center.getX() 
                                + ", " + center.getY()+ ")");
        } 
    }

    SquareTable.java

    import java.awt.*;
    
    public class SquareTable extends AbstractFurniture {
        protected Rectangle rectangle;    
        
        public void setRectangle(Rectangle rectangle) {
            this.rectangle = rectangle;
        }
        
        protected Object clone () 
                          throws CloneNotSupportedException { 
            Object o = super.clone(); 
            if(this.rectangle != null) { 
                ((SquareTable) o).rectangle = (Rectangle) rectangle.clone();
            }
            
            return o; 
        } 
    
        public void draw() { 
            System.out.print("\t方桌\t位置:(" + rectangle.getX() 
                                + ", " + rectangle.getY()+ ")");
            System.out.println(" / 宽高:(" + 
                             rectangle.getWidth() 
                    + ", " + rectangle.getHeight()+ ")");
        }
    }
    

    House是个虚拟的房屋物件,从Prototype复制出来的物件加入至House中:

    • House.java

    import java.util.*;
    
    public class House { 
        private Vector vector;
    
        public House() { 
            vector = new Vector(); 
        }
    
        public void addFurniture(AbstractFurniture furniture) { 
            vector.addElement(furniture); 
            
            System.out.println("现有家具....");
            
            Enumeration enumeration = vector.elements();
            while(enumeration.hasMoreElements()) { 
                 AbstractFurniture f = 
                     (AbstractFurniture) enumeration.nextElement(); 
                 f.draw(); 
            } 
            System.out.println(); 
        } 
    }

    再来是应用程式本身:

    • Application.java

    import java.awt.*;
    
    public class Application {
        private AbstractFurniture circleTablePrototype;
        
        public void setCircleTablePrototype(
                       AbstractFurniture circleTablePrototype) {
            this.circleTablePrototype = circleTablePrototype;
        }
        
        public void runAppExample() throws Exception {
            House house = new House(); 
            CircleTable circleTable = null;
    
            // 从工具列选择一个家具加入房子中
            circleTable =
                (CircleTable) circleTablePrototype.clone();
            circleTable.setCenter(new Point(10, 10));
            house.addFurniture(circleTable); 
            
            // 从工具列选择一个家具加入房子中
            circleTable = 
                (CircleTable) circleTablePrototype.clone();
            circleTable.setCenter(new Point(20, 30));
            house.addFurniture(circleTable); 
        }
        
        public static void main(String[] args) throws Exception {
            Application application = new Application();
            application.setCircleTablePrototype(
                                new CircleTable());
            application.runAppExample();
        }
    }

    Java中的clone()方法是继承自Object,AbstractFurniture的子类别则override这个clone()方法,以复制其本身并传回。
    下图为Prototype模式的类别结构图:

    Prototype

    在 Gof 的设计模式书中给出一个原型模式的应用:一个通用的图型编辑器 Framework。在这个 Framework中有一个工具列,您可以在上面选择音乐符号以加入乐谱中,并可以随时调整音乐符号的位置等等。
    图型编辑器Framework是通用的,然而它并不知道这些音乐符号的型态,有人或许会想到继承图型编辑器Framework来为每个音乐符号设计一个框 架子类别,但由于音乐符号的种类很多,这会产生相当多的子类别,为了避免这种情况,可以透过Prototype模式来减少子类别的数目,可以设计出以下的 结构:

    Prototype

    依照这个结构,图型编辑器的Framework可以独立于要套用的特定类别之外,虽然不知道被复制传回的对象型态是什么,但总可以按照 Graphics所定义的介面来操作这些物件。

  • 相关阅读:
    LeetCode No41. 缺失的第一个正数
    LeetCode 面试题 01.05. 一次编辑
    LeetCode No45. 跳跃游戏 II
    LeetCode No436. 寻找右区间
    LeetCode No47. 全排列 II
    LeetCode No812. 最大三角形面积
    LeectCode No953. 验证外星语词典
    LeetCode No462. 最少移动次数使数组元素相等 II
    LeetCode No44. 通配符匹配
    LeetCode No43. 字符串相乘
  • 原文地址:https://www.cnblogs.com/rollenholt/p/2466986.html
Copyright © 2020-2023  润新知