7

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:

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.

Community
  • 1
  • 1
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
  • 6
    I seriously doubt that any scheme for creating objects from a prototype is more efficient in time or space than simply using a reasonably appropriate constructor. – Hot Licks May 27 '14 at 15:14
  • The reason a copy constructor is recommended is that using any other means (not invoking any constructor) may fail for some classes (identifiers that are supposed to be unique suddenly have duplicates, and so on). I'd recommend to stick to copy constructors, as they are the safe, conventional way of copying objects in Java. – Chronio May 27 '14 at 15:15
  • @HotLicks question updated. – Luiggi Mendoza May 27 '14 at 15:24
  • @Chronio they are for simple objects. This question is more associated to deep copy of complex objects. – Luiggi Mendoza May 27 '14 at 15:24
  • I would still stick to copy constructors. They're easy to maintain (you define them for each class), and there is little to no difference in speed. Using other means copies the contents in more or less the same way, but it's much harder to maintain, especially for larger, more complex object types (for your reference: Java, C++ and many other OO-languages all use copy constructors to create object copies). – Chronio May 27 '14 at 15:31
  • 1
    @Chronio my question is not at all about copy constructors. I want to know if object deserialization is another option to implement prototype pattern. And you are not addressing that in your comments. And yes, I have large objects to copy, which fields can vary from period to period due to system maintenance. In cases like this, updating lot of classes that can *speed up* the application to have a deep copy in 0.582 ms (for example) is not *better* than using object serialization/deserialization who has showed me to be almost that fast, like 1.042 ms when JIT haven't kicked in. – Luiggi Mendoza May 27 '14 at 15:37
  • @Chronio in short, *having lot of code to maintain is better than using object serialization/deserialization*, this would depend if performance is one of your main issues, but in our case it is not. And again, my question is not about using copy constructors or not. – Luiggi Mendoza May 27 '14 at 15:38
  • This is the misconception: "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." Using any scheme the object must be physically allocated, and using `new` is highly optimized, so there is no "inherent cost" (at least not in Java). Yes, it may be that a particular constructor is inefficient, but unless the inefficiency is inherent (and therefore inevitably present in other schemes) it's probably better to fix the constructor than try to bypass it. – Hot Licks May 27 '14 at 16:41
  • @HotLicks what approach would you use then if this inefficient constructor cannot be *fixed*? – Luiggi Mendoza May 27 '14 at 16:44
  • @LuiggiMendoza - I'd ask "Why not?" – Hot Licks May 27 '14 at 16:51
  • @HotLicks because it is a third party legacy jar that we cannot modify. – Luiggi Mendoza May 27 '14 at 16:52
  • @LuiggiMendoza - Then you do whatever you need to do. But don't try to justify it by claiming it's a "pattern" -- admit that it's a kluge and get on with life. (And note that if you cannot modify the code then you can't implement most of the other schemes -- clone, deserialize, etc -- either, unless the existing code accidentally supports them.) – Hot Licks May 27 '14 at 16:55
  • 1
    @HotLicks I'm not justifying anything. I simply want to know if that's a valid implementation of prototype pattern or not. If you're very sure about your answer, post it. – Luiggi Mendoza May 27 '14 at 16:56
  • Voting to leave open, this is a question that can be answered without resorting to just opinion. – JasonMArcher May 27 '14 at 18:17

4 Answers4

14

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.
Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121
  • It's not really well specified by in my case all these third party library classes implement `Serializable` interface, so I'm able to serialize them. – Luiggi Mendoza May 29 '14 at 22:09
  • I revised, but left in the scenario when you don't own the code so the answer is more generally useful. – Dave Schweisguth May 29 '14 at 22:25
  • *the Prototype pattern puts the responsibility of copying on the object to be copied, which is not the case if the client needs to provide the deserialization and serialization code* in Java, you can add `writeObject` and `readObject` methods in the classes to define the behavior when being serialized/deserialized, so, based on your explanation, this makes serialization/deserialization process even closer to Prototype implementation. – Luiggi Mendoza May 30 '14 at 14:29
  • Yes, close. It's the need for each client to provide serializing/deserializing code that makes plain serialization/deserialization not a clean implementation of Prototype. – Dave Schweisguth May 30 '14 at 15:07
  • When you serialize/deserialize the Object, is the object to be serialiez/deserialized which defines the behavior, not the client, which based on your first paragraph makes the serialization/deserialization process a fine implementation of Prototype, but your conclusion says it isn't. – Luiggi Mendoza May 30 '14 at 15:11
  • It's not just about the ability of the object to be copied. The design pattern gives the object to be copied a simple interface for the client, which do-it-yourself serialization is not. – Dave Schweisguth May 30 '14 at 15:14
  • This answer is very close to what I'm looking for except for how it begins: *the Prototype pattern puts the responsibility of copying on the object to be copied, which is not the case if the client needs to provide the deserialization and serialization code*. Here, you state (or this is what I assume from reading) *client* will provide the code for serialization/deserialization, but this is solved in your proposal by using `PrototypeUtility.copy`. I guess the answer would be *using Java serialization/deserialization directly is not, but it can be used to implement prototype pattern*. – Luiggi Mendoza Jun 04 '14 at 22:15
  • Right -- that's what I was trying to say in my first line. – Dave Schweisguth Jun 04 '14 at 22:16
  • But it's not *that easy* to get the main idea. Remember that this site is for any kind of readers, even newcomers or people who already understand programming but starts learning about design patterns (among other stuff). If you edit it accordingly, then you earned the rep. – Luiggi Mendoza Jun 04 '14 at 22:18
  • Edited; see what you think. – Dave Schweisguth Jun 04 '14 at 22:54
1

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.

Spina
  • 8,986
  • 7
  • 37
  • 36
1

Your question is really interesting Luiggi (I voted for it because the idea is great), it's a pitty you don't say what you are really concerned about. So I'll try to answer what I know and let you choose what you find arguable:

  • Advantages :

    • In terms of memory use, you will get a very good memory consumption by using serialization since it serializes your objects in binary format (and not in text as json or worse: xml). You may have to choose a strategy to keep your objects "pattern" in memory as long as you need it, and persist it in a "less used first persisted" strategy, or "first used first persisted"
    • Coding it is pretty direct. There are some rules to respect, but it you don't have many complex structures, this remains maintainable
    • No need for external libraries, this is pretty an advantage in institutions with strict security/legal rules (validations for each library to be used in a program)
    • If you don't need to maintain your objects between versions of the program/ versions of the JVM. You can profit from each JVM update as speed is a real concern for java programs, and it's very related to io operations (JMX, memory read/writes, nio, etc...). So there are big chances that new versions will have optimized io/memory usage/serialization algos and you will find you're writing/reading faster with no code change.
  • Disadvantages :

    • You loose all your prototypes if you change any object in the tree. Serialization works only with the same object definition
    • You need to deserialize an object to see what is inside it: as opposed to the prototype pattern that is 'self documenting' if you take it from a Spring / Guice configuration file. The binary objects saved to disk are pretty opaque
    • If you're planning to do a reusable library, you're imposing to your library users a pretty strict pattern (implementing Serializable on each object, or using transient for dields that are not serializable). In addition this constraints cannot be checked by the compiler, you have to run the program to see if there's something wrong (which might not be visible immediately if an object in the tree is null for the tests). Naturally, I'm comparing it to other prototyping technologies (Guice for example had the main feature of being compile time checked, Spring did it lately too)

I think it's all what comes to my mind for now, I'll add a comment if any new aspect raises suddenly :)

Naturally I don't know how fast is writing an object as bytes compared to invoking a constructor. The answer to this should be mass write/read tests

But the question is worth thinking.

Zied Hamdi
  • 2,400
  • 1
  • 25
  • 40
  • I'd add that the term "prototyping" is not strict enough so we can say for a new approach if it is or not prototyping. We can't even say how much percent of the prototyping pattern is achieved. Finally it is useless to try to obey to the "prototyping" as defined somewhere, because it's just a word to express a use pattern (a ready to use predefined/prefilled/configured object tree). It's the purpose that is targeted, not the implementation details. – Zied Hamdi Jun 04 '14 at 22:30
  • Yes, but my question is about implementation details. And as posted in my question, using serialization/deserialization assumes you know what you're doing and that you take the risks of using it. – Luiggi Mendoza Jun 04 '14 at 22:56
  • Ok so I misunderstood the question: "Is Object deserialization a proper way to implement Prototype pattern in Java?" :) – Zied Hamdi Jun 05 '14 at 16:45
  • My main concern is to know if I can implement this pattern by using object serialization/deserialization or not, and if the implementation details matter. This is covered in the current accepted answer in form of *yes, but not if you use it directly*. The disadvantages are part of the risk you take when using serialization approach. – Luiggi Mendoza Jun 05 '14 at 16:48
-1

There are cases where creating new object using copy constructor is different from creating new object "in a standard way". One example is explained in the Wikipedia link in your question. In that example, to create new WordOccurrences using the constructor WordOccurrences(text, word), we need to perform heavyweight computation. If we use copy constructor WordOccurrences(wordOccurences) instead, we can immediately get the result of that computation (in the Wikipedia, clone method is used, but the principle is the same).

fajarkoe
  • 1,543
  • 10
  • 12
  • 7
    The question asks if object deserialization is a proper implementation of the prototype pattern. You don't address that. – GriffeyDog May 27 '14 at 15:34
  • Sure. I think you can use any approach, as long as it avoids the expensive computation incurred by the standard way of creating new object. I think this is the main idea behind Prototype pattern. – fajarkoe May 27 '14 at 15:45