zoukankan      html  css  js  c++  java
  • 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所定义的介面来操作这些物件。

  • 相关阅读:
    ArcGIS Engine开发-三维视图(SceneControl)的刷新
    c#开发基础知识及有用技巧(一)
    ArcGIS Engine开发-取得ArcMap文档缩略图
    RichTextBox中表格不能折行的问题
    Windows WorkFlow Foundation入门(六) 编译工作流
    你还在用 VMware?快试试这款更轻量级的虚拟机!
    RedisJson 横空出世,性能碾压 ES 和 MongoDB!NoSQL 要变天了吗?
    基于 ElasticSearch 实现站内全文搜索,写得太好了!
    团灭!Log4j 1.x 也爆雷了。。。速速弃用!!
    Spring Boot + Redis:抗住 10w 人,秒杀抢单!
  • 原文地址:https://www.cnblogs.com/rollenholt/p/2466986.html
Copyright © 2011-2022 走看看