4 min read

In this article by Kyle Mew, author of the book Android Design Patterns and Best Practices, we will discuss how the prototype design pattern performs similar tasks to other creational patterns, such as builders and factories, but it takes a very different approach. Rather than relying heavily on a number of hardcoded subclasses, the prototype, as its name suggests, makes copies of an original, vastly reducing the number of subclasses required and preventing any lengthy creation processes.

(For more resources related to this topic, see here.)

Setting up a prototype

The prototype is most useful when the creation of an instance is expensive in some way. This could be the loading of a large file, a detailed cross-examination of a database, or some other computationally expensive operation. Furthermore, it allows us to decouple cloned objects from their originals, allowing us to make modifications without having to reinstantiate each time. In the following example, we will demonstrate this using functions that take considerable time to calculate when first created, the nth prime number and the nth Fibonacci number.

Viewed diagrammatically, our prototype will look like this:

We will not need the prototype pattern in our main app as there are very few expensive creations as such. However, it is vitally important in many situations and should not be neglected. Follow the given steps to apply a prototype pattern:

  1. We will start with the following abstract class:
    public abstract class Sequence implements Cloneable {
    
       protected long result;
    
       private String id;
    
       public long getResult() {
    
           return result;
    
       }
    
       public String getId() {
    
           return id;
    
       }
    
       public void setId(String id) {
    
           this.id = id;
    
       }
    
       public Object clone() {
    
           Object clone = null;
    
           try {
    
               clone = super.clone();
    
           } catch (CloneNotSupportedException e) {
    
               e.printStackTrace();
    
           }
    
           return clone;
    
       }
    
    }
  2. Next, add this cloneable concrete class, as shown:
    // Calculates the 10,000th prime number
    
    public class Prime extends Sequence {
    
       public Prime() {
    
           result = nthPrime(10000);
    
       }
    
       public static int nthPrime(int n) {
    
           int i, count;
    
           for (i = 2, count = 0; count < n; ++i) {
    
               if (isPrime(i)) {
    
                   ++count;
    
               }
    
           }
    
           return i - 1;
    
       }
    
       // Test for prime number
    
       private static boolean isPrime(int n) {
    
           for (int i = 2; i < n; ++i) {
    
               if (n % i == 0) {
    
                    return false;
    
               }
    
           }
    
           return true;
    
       }
    
    }
  3. Add another Sequence class, like so: for the Fibonacci numbers, like so:
    // Calculates the 100th Fibonacci number
    
    public class Fibonacci extends Sequence {
    
       public Fibonacci() {
    
           result = nthFib(100);
    
       }
    
       private static long nthFib(int n) {
    
           long f = 0;
    
           long g = 1;
    
           for (int i = 1; i <= n; i++) {
    
               f = f + g;
    
               g = f - g;
    
           }
    
           return f;
    
       }
    
    }
  4. Next, create the cache class, as follows:
    public class SequenceCache {
    
       private static Hashtable<String, Sequence> sequenceHashtable = new Hashtable<String, Sequence>();
    
       public static Sequence getSequence(String sequenceId) {
    
           Sequence cachedSequence = sequenceHashtable.get(sequenceId);
    
           return (Sequence) cachedSequence.clone();
    
       }
    
           public static void loadCache() {
    
           Prime prime = new Prime();
    
           prime.setId("1");
    
           sequenceHashtable.put(prime.getId(), prime);
    
           Fibonacci fib = new Fibonacci();
    
           fib.setId("2");
    
           sequenceHashtable.put(fib.getId(), fib);
    
       }
    
    }
  5. Add three TextViews to your layout, and then, add the code to your Main Activity’s onCreate() method.
  6. Add the following lines to the client code:
    // Load the cache once only
    
    SequenceCache.loadCache();
    
    // Lengthy calculation and display of prime result
    
    Sequence prime = (Sequence) SequenceCache.getSequence("1");
    
    primeText.setText(new StringBuilder()
    
           .append(getString(R.string.prime_text))
    
           .append(prime.getResult())
    
           .toString());
    
    // Lengthy calculation and display of Fibonacci result
    
    SSequence fib = (Sequence) SequenceCache.getSequence("2");
    
    fibText.setText(new StringBuilder()
    
           .append(getString(R.string.fib_text))
    
           .append(fib.getResult())
    
           .toString());

As you can see, the preceding code creates the pattern but does not demonstrate it. Once loaded, the cache can create instant copies of our previously expensive output. Furthermore, we can modify the copy, making the prototype very useful when we have a complex object and want to modify just one or two properties.

Applying the prototype

Consider a detailed user profile similar to what you might find on a social media site. Users modify details such as images and text, but the overall structure is the same for all profiles, making it an ideal candidate for a prototype pattern.

To put this principle into practice, include the following code in your client source code:

// Create a clone of already constructed object

Sequence clone = (Fibonacci) new Fibonacci().clone();

// Modify the result

long result = clone.getResult() / 2;

// Display the result quickly

cloneText.setText(new StringBuilder()

       .append(getString(R.string.clone_text))

       .append(result)

       .toString()); 

The prototype is a very useful pattern in many occasions, where we have expensive objects to create or when we face a proliferation of subclasses. Although this is not the only pattern that helps reduce excessive sub classing, it leads us on to another design pattern, decorator.

Summary

In this article we explored the vital pattern, the prototype pattern and saw how vital it can be whenever we have large files or slow processes to contend with.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here