Руководство по решениям для интеллектуального кэширования

TL;DR — Caches обсуждает проблемы с кэшированием, такие как связь и удобство обслуживания, и предлагает решения, такие как использование объектного посредника, тестирование сценариев аннулирования и моделирование реальных метафор кэша. В нем делается вывод, что кэши должны быть функциональными и интеллектуальными, а объекты домена не должны кэшироваться.

Проблема

  • Связь
  • Тестируемость
  • Инвалидация кэша
  • Ремонтопригодность
  • Преждевременная оптимизация
  • Неустойчивое поведение
  • Отсутствие прозрачности
  • Недетерминированное поведение

Решение

  1. Если у вас есть убедительный ориентир и вы готовы принять некоторую связь, поместите объект посередине.
  2. Модульное тестирование всех сценариев аннулирования. Опыт показывает, что мы сталкиваемся с ними постепенно.
  3. Найдите метафору реального кэша и смоделируйте ее.

Образец кода

Неправильный

typealias Cache<T> = MutableMap<String, List<T>>

class Book(
    private val cachedBooks: Cache<Book> = mutableMapOf()
) {
    fun getBooks(title: String): List<Book> {
        return if (cachedBooks.containsKey(title)) {
            cachedBooks[title]!!
        } else {
            val booksFromDatabase = getBooksFromDatabase(title)
            cachedBooks[title] = booksFromDatabase
            booksFromDatabase
        }
    }

    private fun getBooksFromDatabase(title: String): List<Book> =
        globalDatabase().selectFrom("Books", "WHERE TITLE = $title")
}

Верно

typealias Cache<T> = MutableMap<String, T>

interface BookRetriever {
    fun bookByTitle(title: String): Book?
}

class Book {
    // Just Book-related Stuff
}

class DatabaseLibrarian : BookRetriever {
    // Go to the database (not global hopefully)
    override fun bookByTitle(title: String): Book? { ... }
}

// We always look for real-life metaphors
class HotSpotLibrarian(
    private val inbox: Inbox,
    private val realRetriever: BookRetriever
) : BookRetriever {
    override fun bookByTitle(title: String): Book? {
        return if (inbox.includesTitle(title)) {
            // We are lucky. Someone has just returned the book copy.
            inbox.retrieveAndRemove(title)
        } else {
            realRetriever.bookByTitle(title)
        }
    }
}

class Inbox(private val books: Cache<Book> = mutableMapOf()) {   
    fun includesTitle(title: String) { ... }
    fun retrieveAndRemove(title: String): Book? { ... }
    fun addBook(title: String, book: Book) { ... }
}

Заключение

Кэши должны быть функциональными и интеллектуальными, позволяющими эффективно управлять аннулированием. Кэши общего назначения лучше всего подходят для объектов низкого уровня, таких как операционные системы, файлы и потоки, тогда как объекты домена не следует кэшировать.

Надеюсь, вам понравилось это путешествие и вы узнали что-то новое. Если вы хотите быть в курсе моих последних мыслей и идей, подпишитесь на мою информационную рассылку. Вы также можете найти меня в LinkedIn или Twitter. Давайте оставаться на связи и продолжать разговор!

Кредиты

Оригинально опубликовано на https://yonatankarp.com.

Спасибо, что дочитали до конца. Пожалуйста, подумайте о том, чтобы подписаться на автора и эту публикацию. Посетите Stackademic, чтобы узнать больше о том, как мы демократизируем бесплатное образование в области программирования во всем мире.