Is deserializing objects an appropriate way to implement a Prototype template in Java?

TL DR

Is it possible to use Java serialization / deserialization using the Serializable , ObjectOutputStream and ObjectInputStream classes, and possibly adding readObject and writeObject to classes that implement Serializable , as a valid implementation for the Prototype template or not?

Note

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


I know the concept of Prototype Pattern (from Wikipedia, my emphasis):

A prototype template is a design creation template for software development. It is used when the type of objects being created is determined by the prototype instance that is cloned to create new objects. This template is used for:

  • Avoid subclassing the object creator in the client application, as the abstract factory template does.

  • avoid the inherent costs of creating a new object in a standard way (for example, using the keyword "new") when it is prohibitively expensive for a given application.

And from this Q / A: Examples of GoF design patterns in major Java libraries , BalusC explains that the prototype pattern in Java is implemented by Object#clone only if the class implements the Cloneable interface (the marker interface is similar to Serializable for serializing / deserializing objects). A problem using this approach is noted in the blog posts / related Q / As questions:

So, another alternative is to use the copy constructor to clone your objects (DIY method), but this does not allow to implement a prototype template for the text, which I emphasized above:

avoid the inherent costs of creating a new object in a standard way (for example, using the keyword "new")

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

So, I just ask if deserialization of objects is used via ObjectOutputStream (and knowing what you are doing, marking the necessary fields as transient and understanding all the consequences of this process), or a similar approach would be the correct implementation of the prototype template.

Note. I do not think that unmarshalling XML documents are the correct implementation of this template, because it calls the class constructor. This probably also happens when unmarshalling the contents of JSON as well.


People will advise using the object constructor, and I would like to keep this option in mind when working with simple objects. This question is more focused on deep copying of complex objects, where I can have 5 levels of objects for cloning. For instance:

 //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 need / need to copy an instance of InvoiceOutboundMessage . I do not think that in this case the copy constructor will be used. In this case, an IMO with a lot of copy constructors doesn't look like a good design.

+6
source share
4 answers

Using Java object serialization directly is not a prototype example, but serialization can be used to implement a template.

The Prototype template is responsible for copying to the object that you want to copy. If you use serialization directly, the client must provide deserialization and serialization code. If you own or plan to write all the classes that need to be copied, it is easy to transfer responsibility to these classes:

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

The class, which should be Prototype , can either implement Prototype itself, or use PrototypeUtility to do the work, or simply extend AbstractPrototype . By doing this, he also advertises that he is Serializable safely.

If you do not own the classes whose instances you want to copy, you cannot follow the Prototype pattern exactly because you cannot transfer the responsibility for copying to these classes. However, if these classes implement Serializable , you can still do the job using serialization directly.

As for copy constructors, this is a great way to copy Java objects whose classes you know, but they do not meet the requirement that the Prototype template does not require the client to know the instance class of the object that it is copying. A client who does not know the instance class, but wants to use its copy constructor, will have to use reflection to find a constructor whose only argument has the same class as the class to which it belongs. This is ugly, and the client could not be sure that the constructor found was a copy constructor. Implementing the interface effectively addresses these issues.

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

Therefore, if you are not working with a third-party library that hides its sensible constructors, forget about this performance. The important points of Prototype are that

  • it allows the client to copy an instance of an object without knowing its class, and
  • it achieves this goal without creating a hierarchy of factories, since it meets the same goal with the AbstractFactory template.
+12
source

I am puzzled by this part of your requirements:

Note. I do not think that unmarshalling XML documents are the correct implementation of this template, because it calls the class constructor. This probably also happens when unmarshalling the contents of JSON as well.

I understand that you may not want to implement a copy constructor, but you will always have a regular constructor. If this constructor is called by the library, then what does it matter? In addition, creating objects in Java is cheap. I used Jackson to sort / untie Java objects with great success. It has high performance and has many amazing features that can be very useful in your case. You can 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, no-arg constructor pairs). For classes that break this pattern, it needs additional customization. One nice thing about this configuration is that you do not need to edit existing classes, so you can clone using JSON without any other part of your code, knowing that JSON is used.

Another reason I like this approach or serialization is more humane debugging (just look at the line to find out what data is). In addition, there are many tools for working with JSON:

While Java serialization tools are small.

One of the drawbacks of this approach is that, by default, duplicated links in the source object will by default be unique in the copied object. 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 of which has its pros and cons, I would say that there is no “right” way to implement a prototype template in Java. The right approach depends a lot on the environment in which you find yourself. If you have constructors that do heavy computing (and can't get around it), then I assume that you have no great choice but to use deserialization. Otherwise, I would prefer the JSON / XML approach. If external libraries were not allowed, and I could modify my beans, then I would use Dave's approach.

+1
source

Your question is really interesting Luigi (I voted for him because the idea is great), it’s a pity, you don’t say what really bothers you. Therefore, I will try to answer what I know and allow you to choose what you consider controversial:

  • Benefits:

    • In terms of memory usage, you get very good memory consumption using serialization, as it serializes your objects in binary format (and not in text like json or worse: xml). You may need to choose a strategy for storing your “template” objects in memory until you need it, and save it in a “less used first saved” or “use first saved” strategy first.
    • The coding is pretty straight forward. There are some rules of respect, but you do not have many complex structures, this remains supported
    • No need for external libraries, it is quite beneficial for institutions with strict security / legal rules (checks for each library that will be used in the program)
    • Unless you need to maintain your objects between program versions / JVM versions. You can profit from each JVM update, since speed is a real problem for java programs, and it is very related to io operations (JMX, memory read / write, nio, etc.). Thus, there is a good chance that in the new versions the memory / memory usage / serialization algorithms will be optimized and you will find that you write / read faster without changing the code.
  • Disadvantages:

    • You will lose all your prototypes if you change any object in the tree. Serialization only works with the same object definition.
    • You need to deserialize the object to see what's inside it: as opposed to the prototype template, which is "self-documenting" if you take it from the Spring / Guice configuration file. Binary objects stored on disk are quite opaque.
    • If you plan to make a reusable library, you impose a fairly strict template on your library users (implementing Serializable for each object or using a transient for permissions that are not serializable). In addition, these restrictions cannot be checked by the compiler, you must run the program to see if there is something wrong (which may not be immediately visible if the object in the tree is null for tests). Naturally, I compare it with other prototyping technologies (for example, Guice had the main feature of compile-time checking, at the same time it was w760).

I think that everything that comes to my mind now, I will add a comment if a new aspect suddenly arises :)

Naturally, I don’t know how fast the object is written in bytes compared to calling the constructor. The answer to this should be mass write / read tests.

But worth considering.

+1
source

There are times when creating a new object using the copy constructor differs from creating a new object in a "standard way". One example is explained in the Wikipedia link in your question. In this example, to create new WordOccurrences using the constructor WordOccurrences (text, word), we need to perform a heavy calculation. If we use the WordOccurrences (wordOccurences) constructor instead, instead, we can immediately get the result of this calculation (Wikipedia uses the clone method, but the principle is the same).

-1
source

Source: https://habr.com/ru/post/969912/


All Articles