prototype

第五章 原型模式(Prototype Pattern)

1 概要

原型模式(Prototype Pattern)是一种创建模式(Creational Design Patterns)。原型模式用于简化创建对象的过程。在面向对象程序设计中,开发人员可以使用构造函数(Constructor),工厂模式(Factory Pattern)抽象工厂模式(Abstract Factory Pattern)建造者模式(Builder Pattern)来创建对象。但是,与之不同的是,原型模式能够帮助开发人员快速创建大量的、内容相同的对象。

在大型项目中,有时创建对象是很昂贵的。例如,可能需要在数据库中查询关键数据,或者从远程服务器中获取相关文件。如果反复执行这些操作,会给系统造成额外的负担。因此,开发人员可以利用原型模式,将这些关键数据存放在原型对象中。在构建新的对象时,可以直接从原型对象中获取。在原型模型下,开发人员可根据业务需求决定使用浅复制(Shallow Copy)或者深度复制(Deep Copy)来创建新的对象。点击这里查看浅复制和深度复制的区别。

2 原型模型的结构

在原型模式中,有五个参与方。

  1. Prototype定义了创建对象的接口。
  2. ProductPrototype实现了Prototype接口,用于实现创建具体对象的逻辑。
  3. ProductBase定义了产品的接口。
  4. ConcreteProduct则是具体产品的实现类。
  5. 客户端(Client)则使用Prototype接口创建新产品对象。

图一 原型模式结构

图一 原型模式结构。

3 原型模式示例

在本例中,我们将使用产品发布消息对象(Release Message)为例,来解释原型模式的使用方法。AbstractReleaseMessage表达的是一条产品发布消息。当有新版本发布时,开发人员往往会在产品说明手册中附上一条消息,说明新产品的名称、版本号和发布日期。所以,AbstractReleaseMessage类中包含了三个成员变量,分别是productName、version和releaseDate。

getReleaseStatement()方法用于创建一个与当前对象内容相同的新对象。getMessage()方法则生成相应的发布消息。

public abstract class AbstractReleaseMessage implements Cloneable {
    private String productName = null;
    private String version = null;
    private String releaseDate = null;

    public String getProductName() {
        return productName;
    }

    public String getVersion() {
        return version;
    }

    public String getReleaseDate() {
        return releaseDate;
    }

    public abstract AbstractReleaseMessage getReleaseStatement();
    public abstract String getMessage();
}

假设小水滴公司正好开发完成新的Web应用程序,亟待发布。那么,开发人员可能会创建一个新类,命名为WebApplicationReleaseMessage。它继承自AbstractReleaseMessage。在getReleaseStatement()方法中,因为WebApplicationReleaseMessage未定义任何成员变量,所以,它可以直接调用基类的clone()方法,复制出一个内容相同的对象。getMessage()方法则生成Web应用程序的发布消息。

public class WebApplicationReleaseMessage extends AbstractReleaseMessage {
    public WebApplicationReleaseMessage(String version, String releaseDate) {
        super("web application", version, releaseDate);
    }

    @Override
    public AbstractReleaseMessage getReleaseStatement() {
        try {
            return (WebApplicationReleaseMessage)super.clone();
        } catch (CloneNotSupportedException ex) {
            System.err.println(ex.getMessage());
        }
        return null;
    }

    @Override
    public String getMessage() {
        return String.format("The web application (version %s) was released on %s", this.getVersion(), this.getReleaseDate());
    }
}

上述的对象是由ReleaseMessageManager管理维护的。ReleaseMessageManager定义了管理AbstractReleaseMessage对象的接口。register()方法允许向管理器ReleaseMessageManager注册具体的AbstractReleaseMessage对象,而get()方法则返回对应的AbstractReleaseMessage对象。Prototype模式的运用就封装在get()方法的实现中。

public interface ReleaseMessageManager {
    public void register(String label, AbstractReleaseMessage message);
    public AbstractReleaseMessage get(String label);
}

ReleaseMessageManagerImpl是ReleaseMessageManager接口的一个实现。在内部,它使用一个Map对象prototypes来保存所有注册的AbstractReleaseMessage对象。每当register()方法被调用时,就会向prototypes成员变量中插入一个新的<Label, AbstractReleaseMessage>对。当get()方法被调用时,在对象存在的情况下,会调用clone()方法创建出一个新的对象,并将其返回。

public class ReleaseMessageManagerImpl implements ReleaseMessageManager {
    private Map<String, AbstractReleaseMessage> prototypes = null;

    public ReleaseMessageManager() {
        prototypes = new HashMap<String, AbstractReleaseMessage>();
    }

    public void register(String label, AbstractReleaseMessage message) {
        this.prototypes.put(label, message);
    }

    public AbstractReleaseMessage get(String label) {
        AbstractReleaseMessage message = this.prototypes.get(label);
        if (message != null) {
            try {
                return (AbstractReleaseMessage)message.clone();
            } catch (CloneNotSupportedException ex) {
                System.err.println(ex.getMessage());
            }
        }
        return null;
    }
}

因此,当使用ReleaseMessageManager创建对象时,开发人员需要首先创建出原型对象,并将其注册到ReleaseMessageManager中。在此之后,开发人员就能够快速的创建出大量的、内容相同的对象了。

public class PrototypeClient {
    public static void main(String[] args) {
        WebApplicationReleaseMessage message = new WebApplicationReleaseMessage("1.0", "2020-11-02");

        ReleaseMessageManager messageManager = new ReleaseMessageManagerImpl();
        messageManager.register("latest-webapp", message);

        ...

        AbstractReleaseMessage releaseMessage = messageManager.get("latest-webapp");
        if (releaseMessage != null) {
            System.out.println(releaseMessage.getMessage());
        }
    }
}

4 工业应用

原型模式虽然提供了快速、高效的创建大量对象的一种方法,但是,它的缺点也非常明显。在大型项目中,对象之间的关系往往较为复杂,正确使用和实现clone()方法的难度也非常大。而且,现代计算机的处理速度已非常快,对象创建过程往往并不是应用程序的性能瓶颈,因此,原型模式并没有其他创建模式那样应用广泛。

5 小结

本章介绍了原型模式的结构和使用方法。原型模式并不复杂,它的主要优点是原型对象内部保存了一份创建对象所需的数据。当需要创建大量对象,并且创建过程比较昂贵的时候,直接从原型对象中获取这些关键信息,能够有效的提升系统的整体性能。

上一章
下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.