Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Object deserialization a proper way to implement Prototype pattern in Java?

TL;DR

Can I use Java serialization/deserialization using Serializable interface, ObjectOutputStream and ObjectInputStream classes, and probably adding readObject and writeObject in the classes implementing Serializable as a valid implementation for Prototype pattern or not?

Note

This question is not to discuss if using copy constructor is better than serialization/deserialization or not.


I'm aware of the Prototype Pattern concept (from Wikipedia, emphasis mine):

The prototype pattern is a creational design pattern in software development. It is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects. This pattern is used to:

  • avoid subclasses of an object creator in the client application, like the abstract factory pattern does.

  • avoid the inherent cost of creating a new object in the standard way (e.g., using the 'new' keyword) when it is prohibitively expensive for a given application.

And from this Q/A: Examples of GoF Design Patterns in Java's core libraries, BalusC explains that prototype pattern in Java is implemented by Object#clone only if the class implements Cloneable interface (marker interface similar to Serializable to serialize/deserialize objects). The problem using this approach is noted in blog posts/related Q/As like these:

  • Copy Constructor versus Cloning
  • Java: recommended solution for deep cloning/copying an instance

So, another alternative is using a copy constructor to clone your objects (the DIY way), but this fails to implement the prototype pattern for the text I emphasized above:

avoid the inherent cost of creating a new object in the standard way (e.g., using the 'new' keyword)

AFAIK the only way to create an object without invoking its constructor is by deserialization, as noted in the example of the accepted answer of this question: How are constructors called during serialization and deserialization?

So, I'm just asking if using object deserialization through ObjectOutputStream (and knowing what you're doing, marking necessary fields as transient and understanding all the implications of this process) or a similar approach would be a proper implementation of Prototype Pattern.

Note: I don't think unmarshalling XML documents is a right implementation of this pattern because invokes the class constructor. Probably this also happens when unmarshalling JSON content as well.


People would advise using object constructor, and I would mind that option when working with simple objects. This question is more oriented to deep copying complex objects, where I may have 5 levels of objects to clone. For example:

//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
}

Let's say, I want/need to copy a instance of InvoiceOutboundMessage. I don't think a copy constructor would apply in this case. IMO having a lot of copy constructors doesn't seem like a good design in this case.

like image 971
Luiggi Mendoza Avatar asked May 27 '14 15:05

Luiggi Mendoza


People also ask

What operations is essential to implement the prototype design pattern?

Prototype patterns are required, when object creation is time consuming, and costly operation, so we create objects with the existing object itself. One of the best available ways to create an object from existing objects is the clone() method. Clone is the simplest approach to implement a prototype pattern.

Which method is used for object deserialization?

The ObjectInputStream class contains readObject() method for deserializing an object.

What is prototype pattern in Java?

Prototype pattern provides a mechanism to copy the original object to a new object and then modify it according to our needs. Prototype design pattern uses java cloning to copy the object.

Why do we need serialization and deserialization in Java?

Serialization in Java allows us to convert an Object to stream that we can send over the network or save it as file or store in DB for later usage. Deserialization is the process of converting Object stream to actual Java Object to be used in our program.


2 Answers

Using Java object serialization directly is not quite the Prototype pattern, but serialization can be used to implement the pattern.

The Prototype pattern puts the responsibility of copying on the object to be copied. If you use serialization directly, the client needs to provide the deserialization and serialization code. If you own, or plan to write, all of the classes that are to be copied, it is easy to move the responsibility to those classes:

  • define a Prototype interface which extends Serializable and adds an instance method copy
  • define a concrete class PrototypeUtility with a static method copy that implements the serialization and deserialization in one place
  • define an abstract class AbstractPrototype that implements Prototype. Make its copy method delegate to PrototypeUtility.copy.

A class which needs to be a Prototype can either implement Prototype itself and use PrototypeUtility to do the work, or can just extend AbstractPrototype. By doing so it also advertises that it is safely Serializable.

If you don't own the classes whose instances are to be copied, you can't follow the Prototype pattern exactly, because you can't move the responsibility for copying to those classes. However, if those classes implement Serializable, you can still get the job done by using serialization directly.

Regarding copy constructors, those are a fine way to copy Java objects whose classes you know, but they don't meet the requirement that the Prototype pattern does that the client should not need to know the class of the object instance that it is copying. A client which doesn't know an instance's class but wants to use its copy constructor would have to use reflection to find a constructor whose only argument has the same class as the class it belongs to. That's ugly, and the client couldn't be sure that the constructor it found was a copy constructor. Implementing an interface addresses those issues cleanly.

Wikipedia's comment that the Prototype pattern avoids the cost of creating a new object seems misguided to me. (I see nothing about that in the Gang of Four description.) Wikipedia's example of an object that is expensive to create is an object which lists the occurrences of a word in a text, which of course are expensive to find. But it would be foolish to design your program so that the only way to get an instance of WordOccurrences was to actually analyze a text, especially if you then needed to copy that instance for some reason. Just give it a constructor with parameters that describe the entire state of the instance and assigns them to its fields, or a copy constructor.

So unless you're working with a third-party library that hides its reasonable constructors, forget about that performance canard. The important points of Prototype are that

  • it allows the client to copy an object instance without knowing its class, and
  • it accomplishes that goal without creating a hierarchy of factories, as meeting the same goal with the AbstractFactory pattern would.
like image 155
Dave Schweisguth Avatar answered Sep 28 '22 10:09

Dave Schweisguth


I'm puzzled by this part of your requirements:

Note: I don't think unmarshalling XML documents is a right implementation of this pattern because invokes the class constructor. Probably this also happens when unmarshalling JSON content as well.

I understand that you might not want to implement a copy constructor, but you will always have a regular constructor. If this constructor is invoked by a library then what does it matter? Furthermore object creation in Java is cheap. I've used Jackson for marshalling/unmarshalling Java objects with great success. It is performant and has a number of awesome features that might be very helpful in your case. You could implement a deep copier as follows:

import com.fasterxml.jackson.databind.ObjectMapper;

public class MyCloner {

    private ObjectMapper cloner; // with getter and setter

    public <T> clone(T toClone){
        String stringCopy = mapper.writeValueAsString(toClone);
        T deepClone = mapper.readValue(stringCopy, toClone.getClass());
        return deepClone;
    }
}

Note that Jackson will work automatically with Beans (getter + setter pairs, no-arg constructor). For classes that break that pattern it needs additional configuration. One nice thing about this configuration is that it won't require you to edit your existing classes, so you can clone using JSON without any other part of your code knowing that JSON is being used.

Another reason I like this approach vs. serialization is it is more human debuggable (just look at the string to see what the data is). Additionally, there are tons of tools out there for working with JSON:

  1. Online JSON formatter
  2. Veiw JSON as HTML based webpage

Whereas tools for Java serialization isn't great.

One drawback to this approach is that by default duplicate references in the original object will be made unique in the copied object by default. Here is an example:

 public class CloneTest {
     public class MyObject { }
     public class MyObjectContainer {

         MyObject refA;
         MyObject refB;

         // Getters and Setters omitted

     }

     public static void runTest(){
         MyCloner cloner = new MyCloner();
         cloner.setCloner(new ObjectMapper());
         MyObjectContainer container = new MyObjectContainer();
         MyObject duplicateReference = new MyObject();
         MyObjectContainer.setRefA(duplicateReference);
         MyObjectContainer.setRefB(duplicateReference);
         MyObjectContainer cloned = cloner.clone(container);
         System.out.println(cloned.getRefA() == cloned.getRefB()); // Will print false
         System.out.println(container.getRefA() == container.getRefB()); // Will print true
     }

}

Given that there are several approaches to this problem each with their own pros and cons, I would claim there isn't a 'proper' way to implement the prototype pattern in Java. The right approach depends heavily on the environment you find yourself coding in. If you have constructors which do heavy computation (and can't circumvent them) then I suppose you don't have much option but to use Deserialization. Otherwise, I would prefer the JSON/XML approach. If external libraries weren't allowed and I could modify my beans, then I'd use Dave's approach.

like image 29
Spina Avatar answered Sep 28 '22 10:09

Spina