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、統計情報
- データ処理パイプライン: 型安全な変換と処理の連結
- 高度な戦略パターン: 複合戦略、スレッドセーフ、実務的な価格計算
これらの実装は実際のプロダクションコードで使用できる品質を目指しています。