Using Dynamic proxies to implement memory in Java (1)

Source: Internet
Author: User
Memory is a design model derived from procedural languages such as LISP, Python, and Perl. It can remember the previous computation results. A function that implements the memory function has an explicit cache. Therefore, the computed results can be directly obtained from the cache instead of being computed every time.
Memory can significantly improve the efficiency of code computing, and is a reusable solution.
This article describes how to use this mode in Java and provides a "memory class" that provides the above functions ":
Foo = (FOO) memoizer. memoize (New fooimpl ());
Here, foo is an interface that contains methods that need to be remembered. fooimpl is an implementation of foo. foo is a reference of foo. the method is basically the same as fooimpl. The difference is that the value returned by Foo will be cached. the advantage of a single memory class is that it is very easy to add a memory function for any class: Define an interface that contains methods to be memorized, and then call memoize to implement an instance.

To understand how the memory class is implemented, we will explain it in several steps. first, I will explain why the cache can be implemented in the class that requires it. then, I will test how to add a cache package for a specific class. finally, I will explain how to make a cache package generic to any class.

Add cache for large computing programs
As an example of a large computing program, we consider the example of pibinarydigitscalculator-computing binary data pi. the only public method calculatebinarydigit has a parameter: integer N, representing the number of digits to be accurate. for example, if the value is 1000000, the system returns the 1 million digits after the decimal point. The byte value indicates that each digit is 0 or 1. (algorithm can refer to: http://www.cecm.sfu.ca /~ Pborwein/papers/p123.pdf)

Public class pibinarydigitscalculator {
/**
* Returns the coefficient of 2 ^ N in the binary
* Expansion of pi.
* @ Param n the binary digit of PI to calculate.
* @ Throws validitycheckfailedexception if the validity
* Check fails, this means the implementation is 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 can be achieved by modifying this class: Add a map to save the previously calculated value, as shown below:

Import java. util. hashmap;

Public class pibinarydigitscalculator {

Private hashmap cache = new hashmap ();

Public synchronized byte calculatebinarydigit (
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 whether the key-parameter n is cached in hashmap. If n is found, this value is directly returned. otherwise, the lengthy computation will be performed and the results will be saved to the cache. A small conversion between the original type and the object is required when a hashmap is added.
Although this method is feasible, there are several disadvantages. first, the cached code is not significantly separated from the normal algorithm code. A class is not only responsible for computing, but also responsible for maintaining cache data. in this way, it is very difficult to perform some tests. for example, you cannot write a test program to test the algorithm and continuously return the same value, because the results are obtained directly from the cache from the second time.
Second, when the cache code is no longer needed, it will become difficult to remove it because it is closely integrated with the algorithm block code. therefore, it is difficult to know whether the cache has brought about a high efficiency improvement, because a test program cannot be written separately from the cache data. when you improve your algorithm, the cache may fail-but you do not know it at this time.
Third, the cache code cannot be reused. Although the Code follows a common pattern, it is all in a class-pibinarydigitscalculator.

The preceding two problems can be solved by constructing a cache package.

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

Public interface binarydigitscalculator {

Public byte calculatebinarydigit (final int N );
}

Then define two implementations to take charge of two tasks respectively:

Public class pibinarydigitscalculator
Implements binarydigitscalculator {

Public byte calculatebinarydigit (final int N ){
Return runbbpalgorithm (N );
}

Private byte runbbpalgorithm (final int N ){
// Lengthy routine goes here...
}

}

Import java. util. hashmap;

Public class cachingbinarydigitscalculator implements
Binarydigitscalculator {

Private 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 {
Return B. bytevalue ();
}
}
}


This is a simple refactored version of the previous implementation of pibinarydigitscalculator. cachingbinarydigitscalculator encapsulates binarydigitscalculator handles and adds cache for calculatebinarydigit method call. this method improves code readability and maintainability. you cannot directly use the binarydigitscalculator interface to implement algorithms. Therefore, it is easy to disable cache blocks.
Also, suitable test programs can be easily written. for example, we write a false binarydigitscalculator implementation. Each time calculatebinarydigit is called, the same parameter is assigned and different values are returned. in this way, we can test whether the cache is working, because if the same value is returned every time, it proves that the cache is working properly. this test is not possible in the previous simple implementation.

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.