Magic Memcached(nleach.com)
nleach.com
Magic Memcached
http://nleach.com/post/4664527538/magic-memcached
4 comments
This approach makes caching no longer invisible to the caller. Moreover, all callers, rather than the class, have complete control over the cache activation and expiration. Also, the caller needs to know which methods can safely be cached at all.
So for the whole application, this approach will probably cause more brittle code than caching "by hand", where caching was at least hidden behind well-defined interfaces.
Nevertheless, I think that meta-class magic is the right way to abstract away this caching code duplication. It just shouldn't change class interfaces.
For example, there could be a static attribute that contains the names of all methods that should be cached. And there could be an additional method for invalidating caches, probably with an array argument to facilitate invalidation multiple method caches at once. And, of course, this cache invalidation should mainly be used by the class itself, and ideally not be available to the caller at all.
So for the whole application, this approach will probably cause more brittle code than caching "by hand", where caching was at least hidden behind well-defined interfaces.
Nevertheless, I think that meta-class magic is the right way to abstract away this caching code duplication. It just shouldn't change class interfaces.
For example, there could be a static attribute that contains the names of all methods that should be cached. And there could be an additional method for invalidating caches, probably with an array argument to facilitate invalidation multiple method caches at once. And, of course, this cache invalidation should mainly be used by the class itself, and ideally not be available to the caller at all.
Perl has had this for 12 years or so, and Lisp way before that.
http://perldoc.perl.org/Memoize.html
I suggest you look into the design here. memoize() works on functions, which is a lot more useful than catching missing methods. That way, the library author can memoize functions, and so can the library user, if they are sure the function they want is pure (i.e. no side effects).
I see problems with allowing callers to memoize methods of an object. You're breaking encapsulation when the caller can suddenly decide how a method is to be cached. In principle, every method of an object depends on the object's private state, which could change at any time. So how are callers supposed to know what is and isn't cacheable? Or when to expire a cache?
Also, the average object has methods for writing and reading. It would be pretty silly to allow there to be cacheable writing methods. Again that suggests control over what is cacheable should be in the class.
That said, you've got a good idea here -- just figure out how we can give the class control over what is memoized. I am pretty sure this is doable; perhaps you have a private method _get_memoized_methods() that tells the inherited abstract class Memoizable what can be memoized. It can then make it appear as if $object->fastQuery() exists, when it's just a wrapped around the private method $object->slowQuery(). You can do this, I think, by catching missing methods with a __call function in Memoizable, and, I think, you can throw exactly the same exception as PHP would whenever a method that you don't want to mess with is called.
http://perldoc.perl.org/Memoize.html
I suggest you look into the design here. memoize() works on functions, which is a lot more useful than catching missing methods. That way, the library author can memoize functions, and so can the library user, if they are sure the function they want is pure (i.e. no side effects).
I see problems with allowing callers to memoize methods of an object. You're breaking encapsulation when the caller can suddenly decide how a method is to be cached. In principle, every method of an object depends on the object's private state, which could change at any time. So how are callers supposed to know what is and isn't cacheable? Or when to expire a cache?
Also, the average object has methods for writing and reading. It would be pretty silly to allow there to be cacheable writing methods. Again that suggests control over what is cacheable should be in the class.
That said, you've got a good idea here -- just figure out how we can give the class control over what is memoized. I am pretty sure this is doable; perhaps you have a private method _get_memoized_methods() that tells the inherited abstract class Memoizable what can be memoized. It can then make it appear as if $object->fastQuery() exists, when it's just a wrapped around the private method $object->slowQuery(). You can do this, I think, by catching missing methods with a __call function in Memoizable, and, I think, you can throw exactly the same exception as PHP would whenever a method that you don't want to mess with is called.
[deleted]
(I also wish HN had a more dynamic width for fixed width content and/or had a different way to specify fixed width font than just indentation, since people also use indentation for bulleted lists, which makes them really hard to read, especially on smaller screens).