强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

函数式编程艺术 / 13 函数式设计模式

13 函数式设计模式

“设计模式在函数式编程中不是消失了,而是变得如此简单,以至于你可能不会注意到它们。”


13.1 传统模式 vs 函数式模式

许多经典 GoF 设计模式在函数式编程中变得简单甚至不必要。

13.1.1 模式对照表

GoF 模式函数式等价说明
策略函数作为参数高阶函数
命令函数/闭包数据 + 函数
观察者流/响应式Observable
模板方法高阶函数传入变化部分
迭代器迭代器/Stream惰性序列
工厂工厂函数返回值的函数
装饰器函数组合compose
适配器函数适配map, contramap
单例模块/常量顶层绑定
访问者模式匹配代数数据类型
状态State Monad不可变状态
备忘录Memoize纯函数缓存

13.2 策略模式(Strategy)

13.2.1 传统 OOP 策略

// Java 传统策略模式
interface SortStrategy<T> {
    List<T> sort(List<T> items);
}

class QuickSort<T> implements SortStrategy<T> { ... }
class MergeSort<T> implements SortStrategy<T> { ... }
class HeapSort<T> implements SortStrategy<T> { ... }

13.2.2 函数式策略

// 函数式:直接传入排序函数
const sortWith = (compareFn) => (arr) => [...arr].sort(compareFn);

const sortByAge = sortWith((a, b) => a.age - b.age);
const sortByName = sortWith((a, b) => a.name.localeCompare(b.name));
const sortByScore = sortWith((a, b) => b.score - a.score);

// 使用
const users = [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }];
sortByAge(users);   // [{ name: 'Bob', ... }, { name: 'Alice', ... }]
sortByName(users);  // [{ name: 'Alice', ... }, { name: 'Bob', ... }]
-- Haskell:策略就是函数参数
sortBy' :: (a -> a -> Ordering) -> [a] -> [a]
sortBy' = Data.List.sortBy

sortByAge  = sortBy' (comparing age)
sortByName = sortBy' (comparing name)

13.3 装饰器模式(Decorator)

13.3.1 函数式装饰器

// 通过函数组合实现装饰
const withLogging = (fn) => (...args) => {
  console.log(`Calling ${fn.name} with`, args);
  const result = fn(...args);
  console.log(`${fn.name} returned`, result);
  return result;
};

const withTiming = (fn) => (...args) => {
  const start = performance.now();
  const result = fn(...args);
  console.log(`${fn.name} took ${performance.now() - start}ms`);
  return result;
};

const withRetry = (maxRetries) => (fn) => async (...args) => {
  for (let i = 0; i <= maxRetries; i++) {
    try {
      return await fn(...args);
    } catch (err) {
      if (i === maxRetries) throw err;
    }
  }
};

// 组合装饰器
const enhanced = withLogging(withTiming(fetchData));
// 或使用 compose
const enhance = compose(withLogging, withTiming);
const enhancedFetch = enhance(fetchData);
# Python 装饰器(语法糖)
import functools
import time

def with_logging(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        print(f"Calling {fn.__name__} with {args}")
        result = fn(*args, **kwargs)
        print(f"{fn.__name__} returned {result}")
        return result
    return wrapper

def with_timing(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = fn(*args, **kwargs)
        print(f"{fn.__name__} took {time.time() - start:.3f}s")
        return result
    return wrapper

@with_logging
@with_timing
def process_data(data):
    return sum(data)

13.4 命令模式(Command)

13.4.1 函数即命令

// 命令就是函数 + 数据
const createCommand = (type, payload) => (state) => {
  switch (type) {
    case 'ADD_TODO':
      return { ...state, todos: [...state.todos, payload] };
    case 'TOGGLE_TODO':
      return {
        ...state,
        todos: state.todos.map(t =>
          t.id === payload ? { ...t, done: !t.done } : t
        ),
      };
    case 'SET_FILTER':
      return { ...state, filter: payload };
    default:
      return state;
  }
};

// 使用
let state = { todos: [], filter: 'all' };
state = createCommand('ADD_TODO', { id: 1, text: 'Learn FP', done: false })(state);
state = createCommand('TOGGLE_TODO', 1)(state);
-- Haskell:命令用代数数据类型表示
data Command
  = AddTodo Todo
  | ToggleTodo Int
  | SetFilter Filter

applyCommand :: AppState -> Command -> AppState
applyCommand state (AddTodo todo) = state { todos = todo : todos state }
applyCommand state (ToggleTodo id) = state { todos = map (toggleIf id) (todos state) }
applyCommand state (SetFilter f) = state { filter = f }

-- 命令回放
applyCommands :: AppState -> [Command] -> AppState
applyCommands = foldl applyCommand

13.5 观察者模式(Observer)

13.5.1 响应式替代观察者

// 传统观察者 → 响应式流
const { Subject } = require('rxjs');

// Subject 既是 Observable 也是 Observer
const eventBus$ = new Subject();

// 订阅(观察者)
eventBus$.subscribe(event => {
  if (event.type === 'USER_LOGIN') handleLogin(event.data);
});

eventBus$.subscribe(event => {
  if (event.type === 'USER_LOGOUT') handleLogout(event.data);
});

// 发布
eventBus$.next({ type: 'USER_LOGIN', data: { userId: 1 } });
;; Clojure 的 watch 机制
(def state (atom {}))

;; 添加观察者
(add-watch state :logger
  (fn [key atom old-state new-state]
    (println "State changed:" old-state "->" new-state)))

;; 修改状态会触发观察者
(swap! state assoc :count 1)
;; 输出: State changed: {} -> {:count 1}

13.6 Builder 模式

13.6.1 函数式 Builder

// 使用函数链实现 Builder
const createBuilder = (initial = {}) => {
  const builder = {
    _data: { ...initial },
    name(v) { return createBuilder({ ...this._data, name: v }); },
    age(v) { return createBuilder({ ...this._data, age: v }); },
    email(v) { return createBuilder({ ...this._data, email: v }); },
    build() { return this._data; },
  };
  return builder;
};

// 使用
const user = createBuilder()
  .name('Alice')
  .age(30)
  .email('alice@example.com')
  .build();
-- Haskell:使用 Record 语法
data User = User
  { userName  :: String
  , userAge   :: Int
  , userEmail :: String
  } deriving (Show)

-- 默认值模式
defaultUser :: User
defaultUser = User "" 0 ""

-- 使用 Record update
alice = defaultUser
  { userName = "Alice"
  , userAge = 30
  , userEmail = "alice@example.com"
  }
// Rust:使用 Builder pattern + 函数式方法
#[derive(Default)]
struct UserBuilder {
    name: Option<String>,
    age: Option<u32>,
    email: Option<String>,
}

impl UserBuilder {
    fn new() -> Self { Self::default() }
    fn name(mut self, name: &str) -> Self { self.name = Some(name.into()); self }
    fn age(mut self, age: u32) -> Self { self.age = Some(age); self }
    fn email(mut self, email: &str) -> Self { self.email = Some(email.into()); self }
    fn build(self) -> Result<User, String> {
        Ok(User {
            name: self.name.ok_or("name required")?,
            age: self.age.ok_or("age required")?,
            email: self.email.ok_or("email required")?,
        })
    }
}

let user = UserBuilder::new()
    .name("Alice")
    .age(30)
    .email("alice@example.com")
    .build();

13.7 访问者模式(Visitor)

13.7.1 模式匹配替代访问者

-- 代数数据类型 + 模式匹配完全替代访问者模式
data Expr
  = Num Double
  | Add Expr Expr
  | Mul Expr Expr
  | Neg Expr

-- 每个"访问者"就是一个独立的函数
eval :: Expr -> Double
eval (Num x)   = x
eval (Add a b) = eval a + eval b
eval (Mul a b) = eval a * eval b
eval (Neg a)   = negate (eval a)

prettyPrint :: Expr -> String
prettyPrint (Num x)   = show x
prettyPrint (Add a b) = "(" ++ prettyPrint a ++ " + " ++ prettyPrint b ++ ")"
prettyPrint (Mul a b) = "(" ++ prettyPrint a ++ " * " ++ prettyPrint b ++ ")"
prettyPrint (Neg a)   = "(-" ++ prettyPrint a ++ ")"

-- 添加新的"访问者"不需要修改数据类型
height :: Expr -> Int
height (Num _)   = 1
height (Add a b) = 1 + max (height a) (height b)
height (Mul a b) = 1 + max (height a) (height b)
height (Neg a)   = 1 + height a

13.8 依赖注入

13.8.1 函数式依赖注入

// Reader Monad / 函数参数实现依赖注入

// 方式 1:参数注入(最简单)
const createUserService = (db, mailer) => ({
  register: async (userData) => {
    const user = await db.save(userData);
    await mailer.send(user.email, 'Welcome!');
    return user;
  },
  findById: (id) => db.findById(id),
});

// 方式 2:Reader Monad
class Reader {
  constructor(run) { this.run = run; }
  static of(x) { return new Reader(() => x); }
  map(fn) { return new Reader(env => fn(this.run(env))); }
  flatMap(fn) { return new Reader(env => fn(this.run(env)).run(env)); }
}

const ask = new Reader(env => env);

const register = (userData) =>
  ask.flatMap(env =>
    Reader.of(async () => {
      const user = await env.db.save(userData);
      await env.mailer.send(user.email, 'Welcome!');
      return user;
    })
  );
-- Haskell:Reader Monad
type App a = Reader Config a

getConfig :: App Config
getConfig = ask

register :: User -> App (IO User)
register userData = do
  config <- getConfig
  return $ do
    user <- saveUser (db config) userData
    sendEmail (mailer config) (userEmail user) "Welcome!"
    return user

13.9 重试与熔断

// 重试策略(函数组合)
const withRetry = (strategy) => (fn) => async (...args) => {
  let lastError;
  for (let attempt = 0; attempt < strategy.maxRetries; attempt++) {
    try {
      return await fn(...args);
    } catch (err) {
      lastError = err;
      await strategy.delay(attempt);
    }
  }
  throw lastError;
};

// 策略组合
const exponentialBackoff = {
  maxRetries: 3,
  delay: (attempt) => new Promise(r => setTimeout(r, 2 ** attempt * 1000)),
};

const linearBackoff = {
  maxRetries: 5,
  delay: (attempt) => new Promise(r => setTimeout(r, (attempt + 1) * 1000)),
};

// 熔断器
const withCircuitBreaker = (options) => (fn) => {
  let failures = 0;
  let lastFailure = 0;
  const { threshold, resetTimeout } = options;

  return async (...args) => {
    if (failures >= threshold && Date.now() - lastFailure < resetTimeout) {
      throw new Error('Circuit breaker is open');
    }
    try {
      const result = await fn(...args);
      failures = 0;
      return result;
    } catch (err) {
      failures++;
      lastFailure = Date.now();
      throw err;
    }
  };
};

// 组合
const resilientFetch = compose(
  withRetry(exponentialBackoff),
  withCircuitBreaker({ threshold: 5, resetTimeout: 30000 })
)(fetch);

13.10 业务场景

13.10.1 插件系统

// 函数式插件系统
const createPluginSystem = () => {
  let plugins = [];

  return {
    register: (plugin) => {
      plugins = [...plugins, plugin];  // 不可变
      return createPluginSystem(plugins);  // 返回新系统
    },

    execute: (hook, context) =>
      plugins
        .filter(p => p.hooks && p.hooks[hook])
        .reduce((ctx, plugin) => plugin.hooks[hook](ctx), context),
  };
};

// 插件定义
const loggingPlugin = {
  name: 'logging',
  hooks: {
    beforeRequest: (ctx) => {
      console.log(`[${ctx.method}] ${ctx.url}`);
      return ctx;
    },
    afterResponse: (ctx) => {
      console.log(`Response: ${ctx.status}`);
      return ctx;
    },
  },
};

const authPlugin = {
  name: 'auth',
  hooks: {
    beforeRequest: (ctx) => ({
      ...ctx,
      headers: { ...ctx.headers, Authorization: `Bearer ${getToken()}` },
    }),
  },
};

// 使用
const system = createPluginSystem()
  .register(loggingPlugin)
  .register(authPlugin);

const result = system.execute('beforeRequest', {
  method: 'GET', url: '/api/users', headers: {}
});

13.11 小结

要点说明
策略模式高阶函数,传入函数作为参数
装饰器模式函数组合,包装函数增加行为
命令模式代数数据类型 + 函数
观察者模式响应式流(Observable)
访问者模式模式匹配,穷尽性检查
依赖注入Reader Monad / 参数传递

扩展阅读

  1. Functional Design Patterns — Scott Wlaschin(视频)
  2. Functional Programming Patterns — Michael Bevilacqua-Linn
  3. Design Patterns in Haskell — Haskell Wiki

下一章14 函数式并发