Как добавить механизм кэширования в шаблон Abstract Factory на Go

Вопрос или проблема

Как добавить механизм кэширования в паттерн абстрактной фабрики на Golang

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

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

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

Ниже приведен мой код:

type HardwareAbstractionLayer interface {
    GetProcessor() CentralProcessor
}

type CentralProcessor interface {
    GetMaxFreq() int64
}

type AbstractHardwareAbstractionLayer struct{}

func (l *AbstractHardwareAbstractionLayer) GetProcessor() CentralProcessor {
    return nil
}
type LinuxHardwareAbstractionLayer struct {
    *AbstractHardwareAbstractionLayer
}

func (l *LinuxHardwareAbstractionLayer) GetProcessor() CentralProcessor {
    cpu := new(LinuxCentralProcessor)
    return cpu
}

type AbstractCentralProcessor struct{}

func (l *AbstractCentralProcessor) GetMaxFreq() int64 {
    return -1
}

type LinuxCentralProcessor struct {
    *AbstractCentralProcessor
}
func (p *LinuxCentralProcessor) GetMaxFreq() int64 {
    log.Println("queryMaxFreq вызван")
    return rand.Int63()
}

type memoize struct {
    sync.RWMutex
    value          interface{}
    expirationTime time.Time
}

func Memoize(original func() interface{}) func() interface{} {
    return MemoizeWithExpiration(original, -1)
}

func MemoizeWithExpiration(original func() interface{}, ttl time.Duration) func() interface{} {
    m := &memoize{}

    return func() interface{} {
        m.RLock()
        if ttl >= 0 && time.Now().Before(m.expirationTime) && m.value != nil {
            defer m.RUnlock()
            return m.value
        }
        m.RUnlock()
        m.Lock()
        defer m.Unlock()

        if ttl >= 0 && time.Now().Before(m.expirationTime) && m.value != nil {
            return m.value
        }

        m.value = original()
        if ttl >= 0 {
            m.expirationTime = time.Now().Add(ttl)
        } else {
            m.expirationTime = time.Time{}
        }

        return m.value
    }
}

func DefaultExpiration() time.Duration {
    return time.Minute
}

Ответ или решение

Для добавления механизма кеширования в паттерн Абстрактной Фабрики на языке Go, можно воспользоваться подходом мемоизации, который будет применен к методам получения конкретных продуктов. Таким образом, мы сможем добиться единого управления кешированием как для всех фабрик, так и для разных фабрик, производящих один и тот же продукт.

1. Обновление кода

Для начала, давайте немного изменим вашу структуру, добавив кеширование в метод GetProcessor внутри фабрики. Мы будем применять мемоизацию к методам, которые создают конкретные продукты.

2. Интеграция механизма кеширования

Мы будем использовать механизм мемоизации, который вы уже реализовали. Вот как это может выглядеть:

package main

import (
    "log"
    "math/rand"
    "sync"
    "time"
)

type HardwareAbstractionLayer interface {
    GetProcessor() CentralProcessor
}

type CentralProcessor interface {
    GetMaxFreq() int64
}

type AbstractHardwareAbstractionLayer interface {
    GetProcessorMemoized() CentralProcessor
}

type AbstractHardwareFactory struct{}

func (f *AbstractHardwareFactory) GetProcessor() CentralProcessor {
    return nil
}

// Добавим новый метод для кеширования
func (f *AbstractHardwareFactory) GetProcessorMemoized() CentralProcessor {
    return Memoize(func() interface{} {
        return f.GetProcessor()
    })().(CentralProcessor)
}

type LinuxHardwareAbstractionLayer struct {
    *AbstractHardwareFactory
}

func (l *LinuxHardwareAbstractionLayer) GetProcessor() CentralProcessor {
    cpu := new(LinuxCentralProcessor)
    return cpu
}

type AbstractCentralProcessor struct{}

func (l *AbstractCentralProcessor) GetMaxFreq() int64 {
    return -1
}

type LinuxCentralProcessor struct {
    *AbstractCentralProcessor
}

func (p *LinuxCentralProcessor) GetMaxFreq() int64 {
    log.Println("queryMaxFreq called")
    return rand.Int63()
}

type memoize struct {
    sync.RWMutex
    value          interface{}
    expirationTime time.Time
}

func Memoize(original func() interface{}) func() interface{} {
    return MemoizeWithExpiration(original, -1)
}

func MemoizeWithExpiration(original func() interface{}, ttl time.Duration) func() interface{} {
    m := &memoize{}

    return func() interface{} {
        m.RLock()
        if ttl >= 0 && time.Now().Before(m.expirationTime) && m.value != nil {
            defer m.RUnlock()
            return m.value
        }
        m.RUnlock()
        m.Lock()
        defer m.Unlock()

        if ttl >= 0 && time.Now().Before(m.expirationTime) && m.value != nil {
            return m.value
        }

        m.value = original()
        if ttl >= 0 {
            m.expirationTime = time.Now().Add(ttl)
        } else {
            m.expirationTime = time.Time{}
        }

        return m.value
    }
}

func DefaultExpiration() time.Duration {
    return time.Minute
}

func main() {
    factory := &LinuxHardwareAbstractionLayer{&AbstractHardwareFactory{}}

    // Вызов метода получения процессора с кешированием
    processor := factory.GetProcessorMemoized()
    maxFreq := processor.GetMaxFreq()
    log.Println("Максимальная частота процессора:", maxFreq)

    // Повторный вызов должен использовать кешированное значение
    maxFreqCached := processor.GetMaxFreq() 
    log.Println("Максимальная частота процессора из кеша:", maxFreqCached)
}

3. Объяснение изменений

  • Мы добавили метод GetProcessorMemoized в абстрактную фабрику. Этот метод использует функцию Memoize, чтобы кешировать инстансы процессоров.
  • При повторном вызове GetMaxFreq на одном и том же экземпляре процессора, вы должны увидеть, что кешированные значения использовались, что существенно сокращает время выполнения, если процессор создается заново.

4. Преимущества

  1. Упрощенный код: Не нужно дублировать логику кеширования для каждой конкретной фабрики и продукта. Все управление кешированием осуществляется на уровне абстрактных фабрик и продуктов.
  2. Централизованное управление: Изменения в логике кеширования можно вносить в одном месте, что облегчает сопровождение кода и улучшает его читабельность.

Таким образом, добавление механизма кеширования позволяет более эффективно управлять созданием и использованием объектов в соответствии с паттерном Абстрактной Фабрики на языке Go.

Оцените материал
Добавить комментарий

Капча загружается...