[create] prototype mode

Posted by johnnyblotter on Thu, 30 Dec 2021 04:41:34 +0100

If the cost of creating objects is relatively large, and there is little difference between different objects of the same class (most fields are the same), in this case, we can create new objects by copying (or copying) existing objects (prototypes), so as to save creation time.

The way to create objects based on prototypes is called Prototype Design Pattern, which is called prototype pattern for short.

In fact, the process of applying for memory contained in the object and assigning values to member variables does not take much time, or for most business systems, this time can be ignored. Applying a complex pattern can only improve the performance a little. This is the so-called over design, and the gain is not worth the loss.

If the data in the object needs to be obtained through complex calculation (such as sorting, calculating hash values), or reading from very slow IO S such as RPC, network, database, file system, etc. in this case, we can use the prototype mode to directly copy from other existing objects, instead of repeating these time-consuming operations every time we create a new object.

 

Requirements:

Suppose that about 100000 pieces of "search keyword" information are stored in the database, and each information includes keywords, the number of times keywords are searched, the time when the information has been updated recently, etc. When system A starts, it will load this data into memory to deal with some other business requirements. In order to find the information corresponding to A keyword conveniently and quickly, we establish A hash table index for the keyword.

However, we also have another system B, which is specially used to analyze search logs, Periodically (for example, every 10 minutes) batch update the data in the database and mark it as a new data version. For example, in the following example figure, we update the data of v2 version to get the data of v3 version. Here, we assume that there are only updates and new key words, and there is no behavior of deleting keywords.

Implementation 1:

public class Demo {
  private ConcurrentHashMap<String, SearchWord> currentKeywords = new ConcurrentHashMap<>();
  private long lastUpdateTime = -1;

  public void refresh() {
    // Fetch update time from database>lastUpdateTime Data, put into currentKeywords in
    List<SearchWord> toBeUpdatedSearchWords = getSearchWords(lastUpdateTime);
    long maxNewUpdatedTime = lastUpdateTime;
    for (SearchWord searchWord : toBeUpdatedSearchWords) {
      if (searchWord.getLastUpdateTime() > maxNewUpdatedTime) {
        maxNewUpdatedTime = searchWord.getLastUpdateTime();
      }
      if (currentKeywords.containsKey(searchWord.getKeyword())) {
        currentKeywords.replace(searchWord.getKeyword(), searchWord);
      } else {
        currentKeywords.put(searchWord.getKeyword(), searchWord);
      }
    }

    lastUpdateTime = maxNewUpdatedTime;
  }

  private List<SearchWord> getSearchWords(long lastUpdateTime) {
    // TODO: Fetch update time from database>lastUpdateTime Data
    return null;
  }
}

 

New special requirements: at any time, all data in system a must be the same version, either version a or version b. there can be neither version a nor version b. The just updated method can't meet this requirement. In addition, we also require that when updating memory data, system a cannot be unavailable, that is, it cannot be stopped to update data.

Conventional solution (non optimal solution): when we want to update the data in memory, we recreate another version data (assuming version b data). After the new version data is built, we can switch the service version from version a to version b at one time. This not only ensures that the data is always available, but also avoids the existence of intermediate state.

public void refresh() {
    HashMap<String, SearchWord> newKeywords = new LinkedHashMap<>();

    // Take out all data from the database and put it into the database newKeywords in
    List<SearchWord> toBeUpdatedSearchWords = getSearchWords();
    for (SearchWord searchWord : toBeUpdatedSearchWords) {
      newKeywords.put(searchWord.getKeyword(), searchWord);
    }

    currentKeywords = newKeywords;
  }

Distinguish between shallow copy and deep copy to avoid unexpected problems

In the above code implementation, the cost of building newKeywords is relatively high. We need to read the 100000 pieces of data from the database, then calculate the hash value and build newKeywords. This process is obviously time-consuming.

In order to improve efficiency, the prototype model comes in handy.

1. First create newKeywords by shallow copy.

2. For the SearchWord object that needs to be updated, we create a new object by deep copy to replace the old object in newKeywords. After all, there is very little data to update.

This method not only makes use of the advantages of shallow copy to save time and space, but also ensures that the data in currentKeywords is the data of the old version. The specific code implementation is as follows. This is also the fastest way to clone hash table in our application scenario mentioned in the title.

public class Demo {
  private HashMap<String, SearchWord> currentKeywords=new HashMap<>();
  private long lastUpdateTime = -1;

  public void refresh() {
    // Shallow copy
    HashMap<String, SearchWord> newKeywords = (HashMap<String, SearchWord>) currentKeywords.clone();

    // Fetch update time from database>lastUpdateTime Data, put into newKeywords in
    List<SearchWord> toBeUpdatedSearchWords = getSearchWords(lastUpdateTime);
    long maxNewUpdatedTime = lastUpdateTime;
    for (SearchWord searchWord : toBeUpdatedSearchWords) {
      if (searchWord.getLastUpdateTime() > maxNewUpdatedTime) {
        maxNewUpdatedTime = searchWord.getLastUpdateTime();
      }
      if (newKeywords.containsKey(searchWord.getKeyword())) {
        newKeywords.remove(searchWord.getKeyword());
      }
      newKeywords.put(searchWord.getKeyword(), searchWord);
    }

    lastUpdateTime = maxNewUpdatedTime;
    currentKeywords = newKeywords;
  }

  private List<SearchWord> getSearchWords(long lastUpdateTime) {
    // TODO: Fetch update time from database>lastUpdateTime Data
    return null;
  }
}