对象反序列化是在Java中实现Prototype模式的正确方法吗?

Lui*_*oza 7 java serialization design-patterns deserialization prototype-pattern

TL; DR

我可以使用Serializable接口ObjectOutputStream和ObjectInputStream类来使用Java序列化/反序列化,并且可能在实现Prototype模式的有效实现的类中添加readObject和writeObject使用它们Serializable吗?

注意

这个问题不是要讨论使用复制构造函数是否优于序列化/反序列化.


我知道Prototype Pattern概念(来自维基百科,强调我的):

原型模式是软件开发中的创新设计模式.当要创建的对象类型由原型实例确定时使用它,该实例被克隆以生成新对象.此模式用于:

  • 避免客户端应用程序中的对象创建者的子类,就像抽象工厂模式那样.

  • 避免以标准方式创建新对象的固有成本(例如,使用'new'关键字),当它对于给定的应用程序来说过于昂贵时.

从这个Q/A:Java核心库中的GoF设计模式的例子,BalusC解释说,Java中的原型模式Object#clone只有在类实现Cloneable接口(标记接口类似于Serializable序列化/反序列化对象)时才能实现.使用这种方法的问题在博客文章/相关Q/As中注明如下:

因此,另一种方法是使用复制构造函数来克隆您的对象(DIY方式),但这无法实现我上面强调的文本的原型模式:

避免以标准方式创建新对象的固有成本(例如,使用'new'关键字)

AFAIK创建对象而不调用其构造函数的唯一方法是通过反序列化,如此问题的接受答案的示例中所述:在序列化和反序列化期间如何调用构造函数?

所以,我只是询问是否使用对象反序列化ObjectOutputStream(并知道你正在做什么,将必要的字段标记为transient并理解该过程的所有含义)或类似的方法将是Prototype Pattern的正确实现.

注意:我不认为解组XML文档是这种模式的正确实现,因为它调用了类构造函数.也可能在解组JSON内容时也会发生这种情况.


人们会建议使用对象构造函数,在使用简单对象时我会介意这个选项.这个问题更倾向于深度复制复杂的对象,我可能有5个级别的对象要克隆.例如:

//fields is an abbreviation for primitive type and String type fields
//that can vary between 1 and 20 (or more) declared fields in the class
//and all of them will be filled during application execution
class CustomerType {
    //fields...
}

class Customer {
    CustomerType customerType;
    //fields
}

class Product {
    //fields
}

class Order {
    List<Product> productList;
    Customer customer;
    //fields
}

class InvoiceStatus {
    //fields
}

class Invoice {
    List<Order> orderList;
    InvoiceStatus invoiceStatus;
    //fields
}

//class to communicate invoice data for external systems
class InvoiceOutboundMessage {
    List<Invoice> invoice;
    //fields
}
Run Code Online (Sandbox Code Playgroud)

比方说,我想/需要复制一个实例InvoiceOutboundMessage.在这种情况下,我不认为复制构造函数会适用.在这种情况下,拥有大量复制构造函数的IMO似乎不是一个好的设计.

Dav*_*uth 13

直接使用Java对象序列化并不是Prototype模式,但序列化可用于实现模式.

Prototype模式将复制的责任放在要复制的对象上.如果直接使用序列化,则客户端需要提供反序列化和序列化代码.如果您拥有或计划编写所有要复制的类,则可以轻松将责任移交给这些类:

  • 定义一个Prototype扩展Serializable并添加实例方法的接口copy
  • PrototypeUtility使用静态方法定义具体类,该方法copy在一个位置实现序列化和反序列化
  • 定义一个AbstractPrototype实现的抽象类Prototype.使其copy方法委托给PrototypeUtility.copy.

需要成为一个类的类Prototype可以实现Prototype自己并用于PrototypeUtility执行工作,也可以只是扩展AbstractPrototype.通过这样做,它还宣称它是安全的Serializable.

如果您不拥有要复制其实例的类,则无法完全遵循Prototype模式,因为您无法将复制的责任转移到这些类.但是,如果这些类实现Serializable,您仍然可以通过直接使用序列化来完成工作.

关于复制构造函数,这些是复制其知道类的Java对象的好方法,但它们不符合Prototype模式的要求,客户端不应该知道它正在复制的对象实例的类.不知道实例类但希望使用其复制构造函数的客户端必须使用反射来查找构造函数,该构造函数的唯一参数与其所属的类具有相同的类.这很难看,客户端无法确定它找到的构造函数是一个复制构造函数.实现接口可以干净地解决这些问题.

维基百科评论Prototype模式避免了创建新对象的成本似乎是误导我的.(我在"四人帮"的描述中没有看到任何内容.)维基百科创建的对象的示例是一个对象,它列出了文本中单词的出现,当然这些对象的查找成本很高.但是设计程序是愚蠢的,因此获取WordOccurrences实例的唯一方法是实际分析文本,特别是如果您因某种原因需要复制该实例.只需给它一个构造函数,其中包含描述实例整个状态的参数,并将它们分配给它的字段或复制构造函数.

因此,除非您正在使用隐藏其合理构造函数的第三方库,否则请忘记该性能.Prototype的重点是

  • 它允许客户端在不知道其类的情况下复制对象实例,并且
  • 它可以在不创建工厂层次结构的情况下实现该目标,因为使用AbstractFactory模式实现相同的目标.