Posted on • Originally published atyonatankarp.com on
Kotlin Code Smell 33 - Caches
TL;DR - Caches discusses the problems with caching, such as coupling and maintainability, and proposes solutions like using an object mediator, testing invalidation scenarios, and modeling real-world cache metaphors. It concludes that caches should be functional, and intelligent, and domain objects shouldn't be cached.
Problem
Coupling
Testability
Cache Invalidation
Maintainability
Premature Optimization
Erratic Behavior
Lack of Transparency
Non-Deterministic Behavior
Solution
If you have a conclusive benchmark and are willing to accept some coupling, put an object in the middle.
Unit test all your invalidation scenarios. Experience shows that we face them in an incremental way.
Look for a real-world cache metaphor and model it.
Sample Code
Wrong
typealiasCache<T>=MutableMap<String,List<T>>classBook(privatevalcachedBooks:Cache<Book>=mutableMapOf()){fungetBooks(title:String):List<Book>{returnif(cachedBooks.containsKey(title)){cachedBooks[title]!!}else{valbooksFromDatabase=getBooksFromDatabase(title)cachedBooks[title]=booksFromDatabasebooksFromDatabase}}privatefungetBooksFromDatabase(title:String):List<Book>=globalDatabase().selectFrom("Books","WHERE TITLE = $title")}
Right
typealiasCache<T>=MutableMap<String,T>interfaceBookRetriever{funbookByTitle(title:String):Book?}classBook{// Just Book-related Stuff}classDatabaseLibrarian:BookRetriever{// Go to the database (not global hopefully)overridefunbookByTitle(title:String):Book?{...}}// We always look for real-life metaphorsclassHotSpotLibrarian(privatevalinbox:Inbox,privatevalrealRetriever:BookRetriever):BookRetriever{overridefunbookByTitle(title:String):Book?{returnif(inbox.includesTitle(title)){// We are lucky. Someone has just returned the book copy.inbox.retrieveAndRemove(title)}else{realRetriever.bookByTitle(title)}}}classInbox(privatevalbooks:Cache<Book>=mutableMapOf()){funincludesTitle(title:String){...}funretrieveAndRemove(title:String):Book?{...}funaddBook(title:String,book:Book){...}}
Conclusion
Caches should be functional and intelligent, allowing for effective management of invalidation. General-purpose caches are best suited for low-level objects like operating systems, files, and streams, while domain objects should not be cached.
I hope you enjoyed this journey and learned something new. If you want to stay updated with my latest thoughts and ideas, feel free to register for mynewsletter. You can also find me onLinkedIn orTwitter. Let's stay connected and keep the conversation going!
Credits
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse