Day 3: インターフェース - 解答例

チャレンジ1の解答: プラグインシステム

package main

import (
    "fmt"
    "time"
)

// Logger はロギングのインターフェースです。
type Logger interface {
    Info(message string)
    Error(message string)
    Debug(message string)
}

// ConsoleLogger はコンソールへの出力を行うロガーです。
type ConsoleLogger struct {
    prefix string
}

func (c *ConsoleLogger) Info(message string) {
    fmt.Printf("[%s][INFO] %s\n", c.prefix, message)
}

func (c *ConsoleLogger) Error(message string) {
    fmt.Printf("[%s][ERROR] %s\n", c.prefix, message)
}

func (c *ConsoleLogger) Debug(message string) {
    fmt.Printf("[%s][DEBUG] %s\n", c.prefix, message)
}

// TimestampLogger はタイムスタンプを付加するデコレータです。
type TimestampLogger struct {
    logger Logger
}

func (t *TimestampLogger) Info(message string) {
    t.logger.Info(fmt.Sprintf("[%s] %s", time.Now().Format("15:04:05"), message))
}

func (t *TimestampLogger) Error(message string) {
    t.logger.Error(fmt.Sprintf("[%s] %s", time.Now().Format("15:04:05"), message))
}

func (t *TimestampLogger) Debug(message string) {
    t.logger.Debug(fmt.Sprintf("[%s] %s", time.Now().Format("15:04:05"), message))
}

// App はアプリケーションを表します。
type App struct {
    logger Logger
}

func (a *App) Run() {
    a.logger.Info("アプリケーション起動")
    a.logger.Debug("デバッグ情報")
    a.logger.Error("エラーが発生")
}

func main() {
    // ベースロガー
    base := &ConsoleLogger{prefix: "APP"}

    // タイムスタンプ付きロガー
    logger := &TimestampLogger{logger: base}

    app := &App{logger: logger}
    app.Run()
}

---

チャレンジ2の解答: データストアの抽象化

package main

import (
    "errors"
    "fmt"
    "sync"
)

// KVStore はKey-Valueストアのインターフェースです。
type KVStore interface {
    Get(key string) (string, error)
    Set(key string, value string) error
    Delete(key string) error
    List() []string
}

// MemoryStore はインメモリのストレージ実装です。
type MemoryStore struct {
    mu   sync.RWMutex
    data map[string]string
}

// NewMemoryStore は新しいMemoryStoreを作成します。
func NewMemoryStore() *MemoryStore {
    return &MemoryStore{
        data: make(map[string]string),
    }
}

func (m *MemoryStore) Get(key string) (string, error) {
    m.mu.RLock()
    defer m.mu.RUnlock()

    value, ok := m.data[key]
    if !ok {
        return "", errors.New("key not found")
    }
    return value, nil
}

func (m *MemoryStore) Set(key, value string) error {
    m.mu.Lock()
    defer m.mu.Unlock()

    m.data[key] = value
    return nil
}

func (m *MemoryStore) Delete(key string) error {
    m.mu.Lock()
    defer m.mu.Unlock()

    delete(m.data, key)
    return nil
}

func (m *MemoryStore) List() []string {
    m.mu.RLock()
    defer m.mu.RUnlock()

    keys := make([]string, 0, len(m.data))
    for k := range m.data {
        keys = append(keys, k)
    }
    return keys
}

// CachedStore はキャッシュ付きストアです。
type CachedStore struct {
    cache  KVStore
    source KVStore
}

// NewCachedStore は新しいCachedStoreを作成します。
func NewCachedStore(cache, source KVStore) *CachedStore {
    return &CachedStore{
        cache:  cache,
        source: source,
    }
}

func (c *CachedStore) Get(key string) (string, error) {
    // キャッシュを確認
    value, err := c.cache.Get(key)
    if err == nil {
        return value, nil
    }

    // ソースから取得
    value, err = c.source.Get(key)
    if err != nil {
        return "", err
    }

    // キャッシュに保存
    c.cache.Set(key, value)
    return value, nil
}

func (c *CachedStore) Set(key, value string) error {
    c.cache.Set(key, value)
    return c.source.Set(key, value)
}

func (c *CachedStore) Delete(key string) error {
    c.cache.Delete(key)
    return c.source.Delete(key)
}

func (c *CachedStore) List() []string {
    return c.source.List()
}

// インターフェースを満たすことを確認
var _ KVStore = (*MemoryStore)(nil)
var _ KVStore = (*CachedStore)(nil)

func main() {
    // メモリストア
    memory := NewMemoryStore()
    memory.Set("name", "太郎")

    value, _ := memory.Get("name")
    fmt.Println("Memory:", value)

    // キャッシュ付きストア
    cache := NewMemoryStore()
    source := NewMemoryStore()
    source.Set("age", "25")

    cached := NewCachedStore(cache, source)

    // 最初の取得(ソースから)
    age, _ := cached.Get("age")
    fmt.Println("First get:", age)

    // 2回目の取得(キャッシュから)
    age, _ = cached.Get("age")
    fmt.Println("Second get:", age)
}

---

チャレンジ3の解答: 型アサーションの活用

package main

import (
    "fmt"
    "strings"
)

// ProcessJSON はJSONデータを処理します。
func ProcessJSON(data map[string]interface{}, indent int) {
    prefix := strings.Repeat("  ", indent)

    for key, value := range data {
        fmt.Printf("%s%s: ", prefix, key)
        processValue(value, indent+1)
    }
}

func processValue(value interface{}, indent int) {
    switch v := value.(type) {
    case string:
        fmt.Printf("%s\n", v)

    case float64:
        fmt.Printf("%.2f\n", v)

    case bool:
        if v {
            fmt.Println("Yes")
        } else {
            fmt.Println("No")
        }

    case []interface{}:
        items := make([]string, len(v))
        for i, item := range v {
            items[i] = fmt.Sprintf("%v", item)
        }
        fmt.Printf("%s\n", strings.Join(items, ", "))

    case map[string]interface{}:
        fmt.Println()
        ProcessJSON(v, indent)

    default:
        fmt.Printf("(%T) %v\n", v, v)
    }
}

func main() {
    data := map[string]interface{}{
        "name":   "太郎",
        "age":    25.0,
        "active": true,
        "tags":   []interface{}{"go", "rust", "typescript"},
        "address": map[string]interface{}{
            "city":    "東京",
            "zipcode": "100-0001",
        },
    }

    ProcessJSON(data, 0)
}

---

チャレンジ4の解答: デザインパターンの実装

package main

import (
    "fmt"
)

// Operation は計算操作のインターフェースです。
type Operation interface {
    Execute(a, b float64) float64
    Name() string
}

// Add は加算操作です。
type Add struct{}

func (Add) Execute(a, b float64) float64 { return a + b }
func (Add) Name() string                 { return "加算" }

// Subtract は減算操作です。
type Subtract struct{}

func (Subtract) Execute(a, b float64) float64 { return a - b }
func (Subtract) Name() string                 { return "減算" }

// Multiply は乗算操作です。
type Multiply struct{}

func (Multiply) Execute(a, b float64) float64 { return a * b }
func (Multiply) Name() string                 { return "乗算" }

// Divide は除算操作です。
type Divide struct{}

func (Divide) Execute(a, b float64) float64 {
    if b == 0 {
        panic("ゼロ除算エラー")
    }
    return a / b
}
func (Divide) Name() string { return "除算" }

// Calculator は計算機です。
type Calculator struct {
    operation Operation
}

// SetOperation は操作を設定します。
func (c *Calculator) SetOperation(op Operation) {
    c.operation = op
}

// Calculate は計算を実行します。
func (c *Calculator) Calculate(a, b float64) float64 {
    if c.operation == nil {
        return 0
    }
    return c.operation.Execute(a, b)
}

func main() {
    calc := &Calculator{}

    calc.SetOperation(Add{})
    fmt.Printf("10 + 5 = %.2f (%s)\n", calc.Calculate(10, 5), calc.operation.Name())

    calc.SetOperation(Subtract{})
    fmt.Printf("10 - 5 = %.2f (%s)\n", calc.Calculate(10, 5), calc.operation.Name())

    calc.SetOperation(Multiply{})
    fmt.Printf("10 * 5 = %.2f (%s)\n", calc.Calculate(10, 5), calc.operation.Name())

    calc.SetOperation(Divide{})
    fmt.Printf("10 / 5 = %.2f (%s)\n", calc.Calculate(10, 5), calc.operation.Name())
}

出力

10 + 5 = 15.00 (加算)
10 - 5 = 5.00 (減算)
10 * 5 = 50.00 (乗算)
10 / 5 = 2.00 (除算)

---

発展的な実装例

1. プラグインシステムの拡張版

マルチレベルロギングとフィルタリング

package main

import (
    "fmt"
    "io"
    "os"
    "strings"
    "time"
)

// ログレベルの定義
type LogLevel int

const (
    DEBUG LogLevel = iota
    INFO
    WARNING
    ERROR
)

func (l LogLevel) String() string {
    return []string{"DEBUG", "INFO", "WARNING", "ERROR"}[l]
}

// Logger インターフェースの拡張版
type Logger interface {
    Log(level LogLevel, message string)
    SetLevel(level LogLevel)
}

// WriterLogger は任意のio.Writerに書き込むロガー
type WriterLogger struct {
    writer   io.Writer
    minLevel LogLevel
    prefix   string
}

func NewWriterLogger(w io.Writer, prefix string) *WriterLogger {
    return &WriterLogger{
        writer:   w,
        minLevel: DEBUG,
        prefix:   prefix,
    }
}

func (w *WriterLogger) Log(level LogLevel, message string) {
    if level < w.minLevel {
        return
    }
    timestamp := time.Now().Format("2006-01-02 15:04:05")
    fmt.Fprintf(w.writer, "[%s] [%s] [%s] %s\n",
        timestamp, w.prefix, level, message)
}

func (w *WriterLogger) SetLevel(level LogLevel) {
    w.minLevel = level
}

// MultiLogger は複数のロガーに同時に書き込む
type MultiLogger struct {
    loggers []Logger
}

func NewMultiLogger(loggers ...Logger) *MultiLogger {
    return &MultiLogger{loggers: loggers}
}

func (m *MultiLogger) Log(level LogLevel, message string) {
    for _, logger := range m.loggers {
        logger.Log(level, message)
    }
}

func (m *MultiLogger) SetLevel(level LogLevel) {
    for _, logger := range m.loggers {
        logger.SetLevel(level)
    }
}

// FilterLogger は特定のパターンをフィルタリングする
type FilterLogger struct {
    logger  Logger
    exclude []string
}

func NewFilterLogger(logger Logger, exclude ...string) *FilterLogger {
    return &FilterLogger{
        logger:  logger,
        exclude: exclude,
    }
}

func (f *FilterLogger) Log(level LogLevel, message string) {
    for _, pattern := range f.exclude {
        if strings.Contains(message, pattern) {
            return
        }
    }
    f.logger.Log(level, message)
}

func (f *FilterLogger) SetLevel(level LogLevel) {
    f.logger.SetLevel(level)
}

// ベンチマーク用のNullLogger
type NullLogger struct{}

func (n *NullLogger) Log(level LogLevel, message string) {}
func (n *NullLogger) SetLevel(level LogLevel)            {}

// 使用例
func demonstrateLogging() {
    // ファイルロガー
    file, _ := os.Create("app.log")
    defer file.Close()
    fileLogger := NewWriterLogger(file, "FILE")

    // コンソールロガー
    consoleLogger := NewWriterLogger(os.Stdout, "CONSOLE")

    // マルチロガー(ファイルとコンソールの両方に出力)
    multiLogger := NewMultiLogger(fileLogger, consoleLogger)

    // フィルタロガー("password"を含むログは除外)
    filterLogger := NewFilterLogger(multiLogger, "password", "secret")

    // 使用
    filterLogger.SetLevel(INFO)
    filterLogger.Log(DEBUG, "デバッグ情報")         // 出力されない(レベル不足)
    filterLogger.Log(INFO, "ユーザーがログイン")      // 出力される
    filterLogger.Log(ERROR, "password is wrong") // 出力されない(フィルタされる)
    filterLogger.Log(ERROR, "データベースエラー")     // 出力される
}

2. データストアの完全実装

Redis風の高機能KVストア

package main

import (
    "context"
    "encoding/json"
    "errors"
    "sync"
    "time"
)

// Storage は永続化層のインターフェース
type Storage interface {
    Get(ctx context.Context, key string) ([]byte, error)
    Set(ctx context.Context, key string, value []byte) error
    Delete(ctx context.Context, key string) error
    Exists(ctx context.Context, key string) bool
    Keys(ctx context.Context, pattern string) ([]string, error)
}

// CacheEntry はキャッシュエントリ
type CacheEntry struct {
    Value      []byte
    ExpiresAt  *time.Time
    AccessedAt time.Time
}

// AdvancedKVStore は高機能なKey-Valueストア
type AdvancedKVStore struct {
    mu      sync.RWMutex
    data    map[string]*CacheEntry
    maxSize int
    stats   *Stats
}

// Stats はストアの統計情報
type Stats struct {
    mu          sync.RWMutex
    hits        int64
    misses      int64
    evictions   int64
    expirations int64
}

func NewAdvancedKVStore(maxSize int) *AdvancedKVStore {
    store := &AdvancedKVStore{
        data:    make(map[string]*CacheEntry),
        maxSize: maxSize,
        stats:   &Stats{},
    }

    // バックグラウンドで期限切れエントリをクリーンアップ
    go store.cleanupExpired()

    return store
}

func (s *AdvancedKVStore) Get(ctx context.Context, key string) ([]byte, error) {
    s.mu.RLock()
    defer s.mu.RUnlock()

    entry, exists := s.data[key]
    if !exists {
        s.stats.recordMiss()
        return nil, errors.New("key not found")
    }

    // 有効期限チェック
    if entry.ExpiresAt != nil && time.Now().After(*entry.ExpiresAt) {
        s.stats.recordMiss()
        return nil, errors.New("key expired")
    }

    // アクセス時刻を更新(LRU用)
    entry.AccessedAt = time.Now()
    s.stats.recordHit()

    return entry.Value, nil
}

func (s *AdvancedKVStore) Set(ctx context.Context, key string, value []byte) error {
    s.mu.Lock()
    defer s.mu.Unlock()

    // サイズ制限チェック
    if len(s.data) >= s.maxSize {
        s.evictLRU()
    }

    s.data[key] = &CacheEntry{
        Value:      value,
        AccessedAt: time.Now(),
    }

    return nil
}

func (s *AdvancedKVStore) SetWithTTL(ctx context.Context, key string, value []byte, ttl time.Duration) error {
    s.mu.Lock()
    defer s.mu.Unlock()

    if len(s.data) >= s.maxSize {
        s.evictLRU()
    }

    expiresAt := time.Now().Add(ttl)
    s.data[key] = &CacheEntry{
        Value:      value,
        ExpiresAt:  &expiresAt,
        AccessedAt: time.Now(),
    }

    return nil
}

func (s *AdvancedKVStore) Delete(ctx context.Context, key string) error {
    s.mu.Lock()
    defer s.mu.Unlock()

    delete(s.data, key)
    return nil
}

func (s *AdvancedKVStore) Exists(ctx context.Context, key string) bool {
    s.mu.RLock()
    defer s.mu.RUnlock()

    entry, exists := s.data[key]
    if !exists {
        return false
    }

    if entry.ExpiresAt != nil && time.Now().After(*entry.ExpiresAt) {
        return false
    }

    return true
}

func (s *AdvancedKVStore) Keys(ctx context.Context, pattern string) ([]string, error) {
    s.mu.RLock()
    defer s.mu.RUnlock()

    keys := make([]string, 0)
    for key := range s.data {
        keys = append(keys, key)
    }

    return keys, nil
}

// LRU(最も長くアクセスされていないエントリ)を削除
func (s *AdvancedKVStore) evictLRU() {
    var oldestKey string
    var oldestTime time.Time

    for key, entry := range s.data {
        if oldestKey == "" || entry.AccessedAt.Before(oldestTime) {
            oldestKey = key
            oldestTime = entry.AccessedAt
        }
    }

    if oldestKey != "" {
        delete(s.data, oldestKey)
        s.stats.recordEviction()
    }
}

// 期限切れエントリのクリーンアップ
func (s *AdvancedKVStore) cleanupExpired() {
    ticker := time.NewTicker(1 * time.Minute)
    defer ticker.Stop()

    for range ticker.C {
        s.mu.Lock()
        now := time.Now()
        for key, entry := range s.data {
            if entry.ExpiresAt != nil && now.After(*entry.ExpiresAt) {
                delete(s.data, key)
                s.stats.recordExpiration()
            }
        }
        s.mu.Unlock()
    }
}

// 統計情報の記録
func (st *Stats) recordHit() {
    st.mu.Lock()
    defer st.mu.Unlock()
    st.hits++
}

func (st *Stats) recordMiss() {
    st.mu.Lock()
    defer st.mu.Unlock()
    st.misses++
}

func (st *Stats) recordEviction() {
    st.mu.Lock()
    defer st.mu.Unlock()
    st.evictions++
}

func (st *Stats) recordExpiration() {
    st.mu.Lock()
    defer st.mu.Unlock()
    st.expirations++
}

func (st *Stats) Get() (hits, misses, evictions, expirations int64) {
    st.mu.RLock()
    defer st.mu.RUnlock()
    return st.hits, st.misses, st.evictions, st.expirations
}

// ベンチマーク比較
func BenchmarkStorage() {
    ctx := context.Background()

    // 通常のマップ
    simpleMap := make(map[string][]byte)
    start := time.Now()
    for i := 0; i < 10000; i++ {
        key := fmt.Sprintf("key-%d", i)
        simpleMap[key] = []byte("value")
    }
    fmt.Printf("Simple map: %v\n", time.Since(start))

    // AdvancedKVStore
    advStore := NewAdvancedKVStore(10000)
    start = time.Now()
    for i := 0; i < 10000; i++ {
        key := fmt.Sprintf("key-%d", i)
        advStore.Set(ctx, key, []byte("value"))
    }
    fmt.Printf("AdvancedKVStore: %v\n", time.Since(start))
}

3. 型アサーションの実践的な活用

ジェネリックなデータ処理パイプライン

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

// Processor はデータ処理のインターフェース
type Processor interface {
    Process(data interface{}) (interface{}, error)
}

// TypeConverter は型変換を行うプロセッサ
type TypeConverter struct {
    targetType reflect.Type
}

func (t *TypeConverter) Process(data interface{}) (interface{}, error) {
    sourceValue := reflect.ValueOf(data)
    if sourceValue.Type().ConvertibleTo(t.targetType) {
        return sourceValue.Convert(t.targetType).Interface(), nil
    }
    return nil, fmt.Errorf("cannot convert %T to %v", data, t.targetType)
}

// JSONMarshaler はJSONにシリアライズするプロセッサ
type JSONMarshaler struct{}

func (j *JSONMarshaler) Process(data interface{}) (interface{}, error) {
    return json.Marshal(data)
}

// JSONUnmarshaler はJSONからデシリアライズするプロセッサ
type JSONUnmarshaler struct {
    targetType reflect.Type
}

func (j *JSONUnmarshaler) Process(data interface{}) (interface{}, error) {
    bytes, ok := data.([]byte)
    if !ok {
        return nil, fmt.Errorf("expected []byte, got %T", data)
    }

    result := reflect.New(j.targetType).Interface()
    if err := json.Unmarshal(bytes, result); err != nil {
        return nil, err
    }

    return result, nil
}

// Pipeline は複数のプロセッサを連結
type Pipeline struct {
    processors []Processor
}

func NewPipeline(processors ...Processor) *Pipeline {
    return &Pipeline{processors: processors}
}

func (p *Pipeline) Execute(data interface{}) (interface{}, error) {
    result := data
    var err error

    for i, processor := range p.processors {
        result, err = processor.Process(result)
        if err != nil {
            return nil, fmt.Errorf("processor %d failed: %w", i, err)
        }
    }

    return result, nil
}

// 使用例
func demonstratePipeline() {
    type User struct {
        Name  string `json:"name"`
        Age   int    `json:"age"`
        Email string `json:"email"`
    }

    user := User{
        Name:  "太郎",
        Age:   25,
        Email: "taro@example.com",
    }

    // JSON化するパイプライン
    marshalPipeline := NewPipeline(
        &JSONMarshaler{},
    )

    jsonData, err := marshalPipeline.Execute(user)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Printf("JSON: %s\n", jsonData)

    // JSONから復元するパイプライン
    unmarshalPipeline := NewPipeline(
        &JSONUnmarshaler{targetType: reflect.TypeOf(User{})},
    )

    restored, err := unmarshalPipeline.Execute(jsonData)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Printf("Restored: %+v\n", restored)
}

4. デザインパターンの完全実装

プロダクショングレードのストラテジーパターン

package main

import (
    "context"
    "fmt"
    "sync"
    "time"
)

// PricingStrategy は価格計算戦略のインターフェース
type PricingStrategy interface {
    Calculate(basePrice float64) float64
    Name() string
}

// RegularPricing は通常価格
type RegularPricing struct{}

func (r *RegularPricing) Calculate(basePrice float64) float64 {
    return basePrice
}

func (r *RegularPricing) Name() string {
    return "通常価格"
}

// DiscountPricing は割引価格
type DiscountPricing struct {
    discountRate float64
}

func NewDiscountPricing(rate float64) *DiscountPricing {
    return &DiscountPricing{discountRate: rate}
}

func (d *DiscountPricing) Calculate(basePrice float64) float64 {
    return basePrice * (1 - d.discountRate)
}

func (d *DiscountPricing) Name() string {
    return fmt.Sprintf("割引価格 (%.0f%%)", d.discountRate*100)
}

// SeasonalPricing は季節による価格調整
type SeasonalPricing struct {
    winterRate float64
    summerRate float64
}

func NewSeasonalPricing(winterRate, summerRate float64) *SeasonalPricing {
    return &SeasonalPricing{
        winterRate: winterRate,
        summerRate: summerRate,
    }
}

func (s *SeasonalPricing) Calculate(basePrice float64) float64 {
    month := time.Now().Month()
    if month >= 12 || month <= 2 {
        return basePrice * s.winterRate
    } else if month >= 6 && month <= 8 {
        return basePrice * s.summerRate
    }
    return basePrice
}

func (s *SeasonalPricing) Name() string {
    return "季節価格"
}

// MembershipPricing は会員レベルによる価格
type MembershipPricing struct {
    level    string
    discount float64
}

func NewMembershipPricing(level string, discount float64) *MembershipPricing {
    return &MembershipPricing{
        level:    level,
        discount: discount,
    }
}

func (m *MembershipPricing) Calculate(basePrice float64) float64 {
    return basePrice * (1 - m.discount)
}

func (m *MembershipPricing) Name() string {
    return fmt.Sprintf("%s会員価格", m.level)
}

// CompositePricing は複数の戦略を組み合わせる
type CompositePricing struct {
    strategies []PricingStrategy
}

func NewCompositePricing(strategies ...PricingStrategy) *CompositePricing {
    return &CompositePricing{strategies: strategies}
}

func (c *CompositePricing) Calculate(basePrice float64) float64 {
    result := basePrice
    for _, strategy := range c.strategies {
        result = strategy.Calculate(result)
    }
    return result
}

func (c *CompositePricing) Name() string {
    return "複合価格戦略"
}

// PriceCalculator は価格計算機
type PriceCalculator struct {
    mu       sync.RWMutex
    strategy PricingStrategy
}

func NewPriceCalculator(strategy PricingStrategy) *PriceCalculator {
    return &PriceCalculator{strategy: strategy}
}

func (p *PriceCalculator) SetStrategy(strategy PricingStrategy) {
    p.mu.Lock()
    defer p.mu.Unlock()
    p.strategy = strategy
}

func (p *PriceCalculator) Calculate(basePrice float64) float64 {
    p.mu.RLock()
    defer p.mu.RUnlock()
    return p.strategy.Calculate(basePrice)
}

func (p *PriceCalculator) StrategyName() string {
    p.mu.RLock()
    defer p.mu.RUnlock()
    return p.strategy.Name()
}

// 使用例
func demonstratePricing() {
    basePrice := 10000.0

    // 通常価格
    calc := NewPriceCalculator(&RegularPricing{})
    fmt.Printf("%s: %.0f円\n", calc.StrategyName(), calc.Calculate(basePrice))

    // 20%割引
    calc.SetStrategy(NewDiscountPricing(0.2))
    fmt.Printf("%s: %.0f円\n", calc.StrategyName(), calc.Calculate(basePrice))

    // ゴールド会員(10%割引)
    calc.SetStrategy(NewMembershipPricing("ゴールド", 0.1))
    fmt.Printf("%s: %.0f円\n", calc.StrategyName(), calc.Calculate(basePrice))

    // 複合戦略:季節価格 + 会員割引
    composite := NewCompositePricing(
        NewSeasonalPricing(1.2, 0.8),
        NewMembershipPricing("プラチナ", 0.15),
    )
    calc.SetStrategy(composite)
    fmt.Printf("%s: %.0f円\n", calc.StrategyName(), calc.Calculate(basePrice))
}

---

パフォーマンス比較とベンチマーク

インターフェース vs 具象型

package main

import (
    "testing"
)

type Operation interface {
    Execute(a, b int) int
}

type AddOp struct{}

func (AddOp) Execute(a, b int) int { return a + b }

type DirectAdd struct{}

func (DirectAdd) Add(a, b int) int { return a + b }

// インターフェース経由
func BenchmarkInterface(b *testing.B) {
    var op Operation = AddOp{}
    for i := 0; i < b.N; i++ {
        _ = op.Execute(10, 20)
    }
}

// 直接呼び出し
func BenchmarkDirect(b *testing.B) {
    op := DirectAdd{}
    for i := 0; i < b.N; i++ {
        _ = op.Add(10, 20)
    }
}

// インライン化可能な関数
func BenchmarkInline(b *testing.B) {
    add := func(a, b int) int { return a + b }
    for i := 0; i < b.N; i++ {
        _ = add(10, 20)
    }
}

ベンチマーク結果の例:

BenchmarkInterface-8    1000000000    0.25 ns/op
BenchmarkDirect-8       1000000000    0.23 ns/op
BenchmarkInline-8       1000000000    0.21 ns/op

分析: インターフェース経由の呼び出しは若干のオーバーヘッドがありますが、実用上は無視できるレベルです。設計の柔軟性の方が重要です。

---

まとめ

この解答例では、以下の実践的なパターンを網羅しました:

  • 拡張性の高いロガー実装: マルチ出力、フィルタリング、レベル制御
  • プロダクショングレードのKVストア: TTL、LRU、統計情報
  • データ処理パイプライン: 型安全な変換と処理の連結
  • 高度な戦略パターン: 複合戦略、スレッドセーフ、実務的な価格計算

これらの実装は実際のプロダクションコードで使用できる品質を目指しています。