Вопрос или проблема
Как добавить механизм кэширования в паттерн абстрактной фабрики на 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. Преимущества
- Упрощенный код: Не нужно дублировать логику кеширования для каждой конкретной фабрики и продукта. Все управление кешированием осуществляется на уровне абстрактных фабрик и продуктов.
- Централизованное управление: Изменения в логике кеширования можно вносить в одном месте, что облегчает сопровождение кода и улучшает его читабельность.
Таким образом, добавление механизма кеширования позволяет более эффективно управлять созданием и использованием объектов в соответствии с паттерном Абстрактной Фабрики на языке Go.