Technique: Java uses dynamic proxy class to realize memory function

Source: Internet
Author: User
Tags define contains final implement integer interface require wrapper
Dynamic | tips

Memory is a design pattern derived from Lisp,python, and Perl and other procedural languages, which can be used to memorize the results of previous calculations. A function that implements the memory function, with explicit cache, so that the results that have been computed can be obtained directly from the cache, without having to compute each time.

Memory can significantly increase the efficiency of large computational code. And it's a reusable solution.

This article explains how to use this pattern in Java and provides a "memory class" that can provide the above functionality:

Foo foo = (foo) memoizer.memoize (new Fooimpl ());

Here, Foo is an interface, and it contains methods that require memory. Fooimpl is an implementation of Foo. Foo is a reference to Foo. The method is basically the same as the Fooimpl, except that the value returned by Foo is cached. The advantage of a single memory class is that it is simple to add memory to any class: Define an interface that contains methods that require memory. Then call Memoize to implement an instance.

To understand how memory classes are implemented, we'll explain in a few steps. First, let me explain why caching can be implemented in classes that need it. Then I'll test how to add a cache wrapper to a particular class. Finally, let me explain how you can make a cache wrapper universal to any class.

Adding caching for programs that have large amounts of computation

As an example of a large computational program, we consider pibinarydigitscalculator This example-compute the binary data pi. The only public method Calculatebinarydigit with an argument: integer n, Represents the number of digits that need to be precise. For example, 1000000 will return 1 million digits after the decimal point and return with a byte value of 0 or 1 per digit. (The algorithm can refer to: http://www.cecm.sfu.ca/~pborwein/PAPERS/P123.pdf)

       
        
         
        public class Pibinarydigitscalculator {/** * Returns "coefficient of 2^n in" binary * expansion of pi. * @param n T He binary digit of pi to calculate.  * @throws Validitycheckfailedexception if the validity * check fails, this means the implementation was buggy * or n is too Large for sufficient precision to be * retained. * * Public byte calculatebinarydigit (final int n) {return runbbpalgorithm (n);} private byte runbbpalgorithm (final int n) {///lengthy routine goes here ...} }
       
        

The simplest and most straightforward way to cache the return value is by modifying the class to add a map to hold the previously computed values, as follows:

       
        
         
        Import Java.util.HashMap; public class Pibinarydigitscalculator {private HashMap cache = new HashMap (), public synchronized byte Calculatebinarydig It (final int n) {final Integer n = new integer (n); byte B = (byte) cache.get (N); if (b = = null) {byte b = runbbpalgorithm (n); Cache.put (n, new byte (b)); return B.} else {return B.bytevalue ();}} Private byte runbbpalgorithm (final int n) {//lengthy routine goes here ...}}
       
        

The Calculatebinarydigit method first checks to see if the keyword-parameter n is cached in the HashMap, and if found, returns the value directly. Otherwise, this lengthy calculation is performed and the result is saved to the cache. When you add a hashmap, There is a small conversion between the original type and the object.

Although this method is feasible, there are a few drawbacks. First, the code to cache is not significantly separate from the normal algorithm code. A class that is responsible for the maintenance of cached data, not just for calculations. This makes it difficult to do some testing. You can't write a test program to test that the algorithm continues to return the same value because, from the second start, the results are obtained directly from the cache.

Second, when the cached code is no longer needed, it becomes difficult to remove it, because it is closely combined with the algorithm block code. So it's hard to know if caching is bringing a high efficiency boost because it's not possible to write a test program that is separate from the cached data. When you improve your algorithm, The cache may fail-but you don't know at this time.

Third, cached code cannot be reused. Although the code follows a common pattern, it is in a class-pibinarydigitscalculator.

The previous two problems can be solved by constructing a cache wrapper.

Cache Wrapper

By using the decorator mode, it is easy to separate code and cache code. First, define an interface that defines the basic method.

       
        
         
        Public interface Binarydigitscalculator {public byte calculatebinarydigit (final int n);}
       
        

You then define two implementations, each responsible for two tasks:

 
        
          public class P Ibinarydigitscalculator implements Binarydigitscalculator {public byte calculatebinarydigit (final int n) {return RUNBBP Algorithm (n); } Private byte runbbpalgorithm (final int n) {//lengthy routine goes here ...}} Import Java.util.HashMap; public class Cachingbinarydigitscalculator implements Binarydigitscalculator {private Binarydigitscalculator Binarydigitscalculator; Private HASHMAP cache = new HashMap (); Public Cachingbinarydigitscalculator (Binarydigitscalculator calculator) {this.binarydigitscalculator = calculator;} Public synchronized byte calculatebinarydigit (int n) {final Integer n = new integer (n); byte B = (byte) cache.get (N); if (b = = null) {byte b = binarydigitscalculator.calculatebinarydigit (n); Cache.put (n, new byte (b)); return B;} else {re Turn b.bytevalue (); } } }
      
        

This is a very simple refactored version of the previous implementation of Pibinarydigitscalculator. The cachingbinarydigitscalculator wraps the Binarydigitscalculator handle and adds a cache for the Calculatebinarydigit method call. This method improves the readability and maintainability of the code. Users can not use the Binarydigitscalculator interface directly to implement the algorithm, so if you need to close the cache block, it will be easy to implement.

Also, the appropriate test procedures are easy to write. For example, we write a false binarydigitscalculator implementation, each time the calculatebinarydigit is invoked, giving the same parameters, returning a different value. In this way, we can test that the cache is working, because if you return the same value each time, it proves that the cache is working properly. This kind of test is not possible before the simple implementation.



Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.