introduce

The prototype pattern is a creative pattern. The word prototype indicates that the pattern should have an instance of a template from which the user can reproduce an object with the same internal properties and different memory locations, a process commonly known as “cloning.” The copied instance is what we call a “prototype,” which is customizable. The prototype pattern is often used to create complex or time-consuming instances where copying an existing instance makes the program run more efficiently.

define

Specify what kind of objects to create with prototype instances, and create new objects by copying these prototypes.

Usage scenarios

  1. Class initialization consumes a lot of resources, including data, hardware resources, and so on, which can be avoided through prototype replication.
  2. Generating an object from New requires a lot of data preparation and access rights, so you can use prototype mode.
  3. When an object needs to be made available to other objects, and each caller may need to modify its value, consider using the prototype pattern to copy multiple objects for use by the caller, which is protective copying.

UML class diagrams

  • Client: indicates the Client user.
  • Prototype: Abstract class or interface with Clone capability.
  • ConcreatePrototype: Concrete prototype class

A simple implementation of the prototype pattern

Let’s use a simple document copy as an example to demonstrate the prototype pattern.

Demand: there is a document, the document contains the words and pictures, the contents of the user after a long time after editing, intend to make further edits the document, however, the edited documents will be used is uncertain, therefore, to be on the safe side, a user will need to copy the current document, and then modified in a copy of the document.

/*** represents the concrete prototype class */
public class WordDocument implements Cloneable {

    /** * text */
    private String mTxt;

    /** ** ** */
    private List<String> mImagePath = new ArrayList<>();


    public String getmTxt(a) {
        return mTxt;
    }

    public void setmTxt(String mTxt) {
        this.mTxt = mTxt;
    }

    public List<String> getImagePath(a) {
        return mImagePath;
    }

    public void addImagepath(String imagepath) {
        mImagePath.add(imagepath);
    }

    /** * Prints the contents of the document */
    public void println(a){

        System.out.println("---------------- start ----------------");
        
        System.out.println("txt: " + mTxt);
        System.out.println("mImagePath: ");
        for (String path : mImagePath) {
            System.out.println("path: " + path);
        }

        System.out.println("----------------- end ----------------");
    }

    /** * Declare clone capability *@returnThe object of clone */
    @Override
    protected WordDocument clone(a)  {
        try {
            WordDocument document = (WordDocument)super.clone();
            document.mTxt = this.mTxt;
            document.mImagePath = this.mImagePath;
            return document;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null; }}Copy the code

Test:

    @Test
    public  void test4(a){
        //1. Build the document object
        WordDocument wordDocument = new WordDocument();
        //2. Edit the document
        wordDocument.setmTxt("It's a fine day today.");
        wordDocument.addImagepath("/sdcard/image.png");
        wordDocument.addImagepath("/sdcard/image2.png");
        wordDocument.addImagepath("/sdcard/image3.png");
        // Print the contents of the document
        wordDocument.println();


        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- to clone - \ n \ n");

        // Copy the original document
        WordDocument cloneDoc = wordDocument.clone();

        System.out.println("Print a copy and look at the data \n\n");
        // Print a copy to see the data
        cloneDoc.println();

        // Make changes on the copy
        cloneDoc.setmTxt("Vice ran to revise the document: the old Dragon King cried.");
        System.out.println("Print the modified copy \n\n");
        // Print the modified copy
        cloneDoc.println();
        System.out.println("---- see if it will affect the original document -----\n\n");
        // See if the original document is affected ???????
        wordDocument.println();
      System.out.println("Memory address: \nwordDocument:"+wordDocument.toString() +"\n" + "cloneDoc: "+cloneDoc.toString());

    }
Copy the code

Output:

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- TXT: today is a good weather mImagePath: path: / sdcard/image. PNG path: /sdcard/image2.png path: / sdcard/image3. PNG -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- to clone - print copy, Look at the data -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- TXT: today is a good weather mImagePath: path: / sdcard/image. PNG path: /sdcard/image2.png path: / sdcard/image3. PNG -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- to print a copy of the modified -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- TXT: MImagePath: path: /sdcard/image.png path: /sdcard/image2.png path: /sdcard/image2.png path: /sdcard/image2.png path: /sdcard/image2.png / sdcard/image3. PNG -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- will not affect the original document -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - MImagePath: path: /sdcard/image.png path: /sdcard/image2.png path: /sdcard/image2.png path: /sdcard/image2.png / sdcard/image3. PNG -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - memory address: wordDocument: com.devyk.android_dp_code.prototype.WordDocument@48533e64
cloneDoc: com.devyk.android_dp_code.prototype.WordDocument@64a294a6
Copy the code

Clone () is created using wordDocument.clone() and cloneDoc outputs the same data as the original wordDocument. CloneDoc is a copy of wordDocument. Is that the end of it? The clone method of the original object copies the reference address to the clone object. If either of the two objects changes it, the original data will be damaged and the protection of the data will be lost. To solve this problem, read on (note: Clone objects do not execute constructors!).

Shallow copy and deep copy

The implementation of the prototype pattern above is really just a shallow copy, also known as a shadow copy. The copy does not actually reconstruct all the fields of the original document, but rather the fields of the copy document reference the fields of the original document.

We know that A refers to B so we can assume that A and B both refer to the same address, that when you change A,B will change, and when B changes A, A will change. Let’s look directly at the following code example:

    @Test
    public void test4(a) {
        //1. Build the document object
        WordDocument wordDocument = new WordDocument();
        //2. Edit the document
        wordDocument.setmTxt("It's a fine day today.");
        wordDocument.addImagepath("/sdcard/image.png");
        wordDocument.addImagepath("/sdcard/image2.png");
        wordDocument.addImagepath("/sdcard/image3.png");
        // Print the contents of the document
        wordDocument.println();


        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- to clone - \ n \ n");

        // Copy the original document
        WordDocument cloneDoc = wordDocument.clone();

        System.out.println("Print a copy and look at the data \n\n");
        // Print a copy to see the data
        cloneDoc.println();

        // Make changes on the copy
        cloneDoc.setmTxt("Vice ran to revise the document: the old Dragon King cried.");
        cloneDoc.addImagepath("/sdcard/ copy changed");
        System.out.println("Print the modified copy \n\n");
        // Print the modified copy
        cloneDoc.println();
        System.out.println("---- see if it will affect the original document -----\n\n");
        // See if the original document is affected ???????
        wordDocument.println();

        System.out.println("Memory address: \nwordDocument:" + wordDocument.toString() + "\n" + "cloneDoc: " + cloneDoc.toString());

    }
Copy the code

Note the copy of the document. I manually called addImagepath to add a new image address. So guess what the original document is going to change? Look at the output below:

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- TXT: today is a good weather mImagePath: path: / sdcard/image. PNG path: /sdcard/image2.png path: / sdcard/image3. PNG -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- to clone - print copy, Look at the data -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- TXT: today is a good weather mImagePath: path: / sdcard/image. PNG path: /sdcard/image2.png path: / sdcard/image3. PNG -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- to print a copy of the modified -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- TXT: MImagePath: path: /sdcard/image.png path: /sdcard/image2.png path: /sdcard/image3.png path: /sdcard/image3.png path: /sdcard/image3.png path: /sdcard/image3.png path: /sdcard/image3.png Copy/sdcard/change -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- will not affect the original document -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- TXT: MImagePath: path: /sdcard/image.png path: /sdcard/image2.png path: /sdcard/image3.png path: /sdcard/image3.png path: /sdcard/image3.png path: Copy/sdcard/change -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - memory address: wordDocument: com.devyk.android_dp_code.prototype.WordDocument@48533e64
cloneDoc: com.devyk.android_dp_code.prototype.WordDocument@64a294a6
Copy the code

Note that the image address we added to the copy did not affect the image address data of the original document, so what happened? For those of you who are familiar with C++, this is because cloneDoc only made a shallow copy of the image list mImagePath, which simply points to this.mImagePath, and does not reconstruct a mImagePath object. As we started with shallow/deep copy, objects A and B actually point to the same address, so any object in A and B that changes the data pointing to the address will change. How do you solve this problem? The answer is to adopt deep copy, that is, when copying objects, the reference type fields should also be copied rather than simply referenced. The clone code is modified as follows:

    /** * Declare clone capability *@returnThe object of clone */
    @Override
    public WordDocument clone(a)  {
        try {
            WordDocument document = (WordDocument)super.clone();
            document.mTxt = this.mTxt;
            // Make a deep copy
            document.mImagePath = (ArrayList<String>) this.mImagePath.clone();
            return document;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
Copy the code

Let’s test the output class capacity again:

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- TXT: today is a good weather mImagePath: path: / sdcard/image. PNG path: /sdcard/image2.png path: / sdcard/image3. PNG -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- to clone - print copy, Look at the data -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- TXT: today is a good weather mImagePath: path: / sdcard/image. PNG path: /sdcard/image2.png path: / sdcard/image3. PNG -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- to print a copy of the modified -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- TXT: MImagePath: path: /sdcard/image.png path: /sdcard/image2.png path: /sdcard/image3.png path: /sdcard/image3.png path: /sdcard/image3.png path: /sdcard/image3.png path: /sdcard/image3.png Copy/sdcard/change -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- will not affect the original document -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- TXT: MImagePath: path: /sdcard/image.png path: /sdcard/image2.png path: /sdcard/image2.png path: / sdcard/image3. PNG -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - memory address: wordDocument: com.devyk.android_dp_code.prototype.WordDocument@48533e64
cloneDoc: com.devyk.android_dp_code.prototype.WordDocument@64a294a6
Copy the code

Deep copy solves this problem by outputting content.

Prototype pattern is a very simple pattern, its core problem is to copy the original object, in the use of this pattern need to pay attention to the problem of deep/shallow copy. In practice, deep copy is recommended to reduce unnecessary hassles.

Here if you are interested in depth copy you can see the nuggets on this article, but is JS code (understand the principle can be), a very hot article worth learning about

Prototype patterns in the source code

conclusion

The prototype pattern is essentially an object copy, similar to the copy constructor in C++, which is prone to problems with deep copy and shallow copy. Using the prototype pattern can solve the resource consumption problem of building complex objects and can improve the efficiency of creating love you objects in some scenarios. Another important use is for protective copying, where an object may be in read-only mode.

Advantages:

The prototype pattern, which is a copy of the binary stream in memory, performs much better than new an object, especially when creating a large number of objects in a loop.

Disadvantages:

This is both a weakness and a weakness. It is copied directly in memory and the constructor is not executed. This potential problem should be considered in actual development.

Article code address

Special thanks to

Android source code design pattern analysis and combat