By dynamic proxy class to create a universal cache wrapper above the second method only the shortcomings are that the cache wrapper cannot be reused, each time we want to add a cache to a class, we must write a special cache wrapper Give the target interface. This is a very slow, easy error process. JDK1.3 began to support dynamic proxy classes: Special classes can decide which interface to implement in the running period - the usual mode is, which interface is determined in the running period Through this, we may implement a universal cache wrapper, we call it Memoizer, which interface is decided in the runtime. This is no longer needed. It is called: it is called: it is called by CachingBinaryDigitscalculator.
BinaryDigitscalculator Calculator = New CachingBinaryDigitscalculator (New PibinaryDigitscalculator ()); can be rewritten by Memoizer as follows:
BinaryDigitsCalculator calculator = (BinaryDigitsCalculator) Memoizer.memoize (new PiBinaryDigitsCalculator ()); Memoizer code is as follows: [code] import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method ; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; public class Memoizer implements InvocationHandler {public static Object memoize (Object object) {return Proxy.newProxyInstance (object.getClass () getClassLoader (), object.getClass () getInterfaces (), new Memoizer (object)..);} private Object object; private Map caches = new HashMap (); private Memoizer (Object object) {this.object = object;}. public Object invoke (Object proxy, Method method, Object [] args) throws Throwable {if (method.getReturnType () equals (Void. TYPE)) {// don't cache void methods return invoke Method, args);} else {map cache = getCache (Method); list key = arrays.aslist (args); object value = cache.get (key); if (value == null&&! cache.containskey (key) ) {value = invoke (method, args); cache.put (key, value);} return value;}} private Object invoke (Method method, Object [] args) throws Throwable {try {return method.invoke (object, Args);} catch (invocationtargeTexception e) {throw E.GETTARGETEXCEPTION ();}} private synchronized map getcache (MAP Cache = (MAP) Caches.get (m); if (cache == null) {Cache =
Collections.SynchronizedMap (new hashmap ()); caches.put (m, cache);} Return cache;}} [/ code] When calling the static method Memoize, a new proxy instance will be created - i.e., Java .lang.Reflect.Proxy instance. An interface set is implemented. This interface set is determined by Object.getClass (). getInterfaces (). Each proxy instance contains a java.lang.reflect.InvocationHandler instance to handle this proxy instance Calling related methods. In our example, Memoizer is an InvocationHandler instance. When a method is called in the proxy instance, for example, CalculateBinaryDigit, then the invoke method in the Memoizer instance will be called, the relevant information will be passed to Invoke Method to determine which method is called by the Proxy instance. In our example, the java.lang.method parameter that is incoming Memoizer is CalculateBinaryDigit, and the parameter information is PI requires an accurate bit number-integer n. On this basis, Memoizer can further cache operation. In the example (caches is a HashMap, cache is a map) KEY, mainly incoming method information: Method objects and parameter objects. In order to achieve simple With versatility, Memoizer has a HashMap Caches about cache, each Method is a key. The corresponding value is a cache. Then transform the parameter information into a list object, as a Cache's key. It is very convenient to use List, You can also guarantee the equals () method, so it can guarantee that this List is only equal when the parameter information is exactly the same. Once a cache's key is created, then you will find this cache before calculating, if you find it, return Cache The value. Otherwise, if this method with these parameters is not called, then this Method is called by invoke. In our example, the CalculateBinaryDigit method in the instance PibinaryDigitscalculator will be called via Invoke. And the calculation results will be present in cache. When to use Memoizer as a universal rule, Memoizer can use in any of the need for traditional Cache - such as the example mentioned above. Special, each need to use memory in the interface The functional Method needs to meet the following conditions:
1. The return value of this Method is best not to change each time 2. This Method does not have a sub-effect 3. The parameters of this Method are determined, non-Mutable. Obviously, if each call, this Method return value is different, Then Cache is unused. It is also very important to say because Method, which does not be repeated, so this Method does not have a deputy response (Method updates some states). Of course, the Void method except. Similar Method, a Mutable parameter is very dangerous, because it will be a dangerous thing to store these parameters to havehmap. According to the Map definition, when the key in this Map change, MAP is not known. So, after you perform this Method, the relevant information adds MAP, then the parameter is varied (MUTATE), the second time is called, it will get the result of the error. Performance use cache The main purpose is to enhance the speed of your program. However, Reflection is indeed well known (improved in JDK1.4, through the reflection calling method is 1/2 of ordinary call speed, this is fast than JDK1.3) 40 times) .Memoizer mainly relying on Reflection to call the method, so it is not a good way. However, if you can use Cache to give the program speed than the REFLECTION's influence, then use Memoizer It is worth considering. In our test of PibinaRdigitscalculator, the test environment is JDK1.4. When N is less than 10, it is quite appropriate. However, when n is increased, the advantage of using Cache is Start displaying. So, frequently using PibinaryDigitscalculator users, you can consider using cache. Unfortunately, the only way to test if your program requires Cache to compare your program in both cases of operational efficiency. Despite this because A program constructs a cache package is a very easy thing, remove it is also easy, the following suggestions can be used as a reference step: 1. Select the class 2. Run it 3. If the efficiency is Satisfied, Go to 64. Add Memoizer, use cache5. If the efficiency is not significantly improved, the Memoizer6 is transferred to the first. If necessary, try again. Theoretically, you need to analyze a class add memory function on the entire system. Only yourself Clear whether it is worth adding. Some methods, even if it is calculated The amount is very large, but in this system, it is rarely called, so it is not necessary to add a memory function for it. To ensure this, I have developed a more featured Memoizer, which realizes an interface called CacheStatistics, you can It gets the number of cache and invalid cache. You can use it as a scale of judgment. Extending Memoizer modifying the Memoizer class to support different Cache policies is very simple. A relatively normal type is Least-Recently-use (LRU) CAHCE has a fixed number of entrances. This cache ensures that the entrance is not greater than its maximum number. If it exceeds it, it will abandon the oldest cache data. That is, it is possible to get new data from cache. A class can use Lru Cache To prevent a program from keeping a state for a state. You can just pass a parameter to the Memoize method in the cachefactory to select the Cache type you need. The following example, LRU Cache has up to 1000 ports: binarydigitscalculator Calculator = (binarydigitscalculator) Memoizer.Memoize (New pinarydigitscalculator (), new lrucachefactory (1000)); even as simple, Memoizer should also be a useful tool for Java programmers.