Clojure 如何表达 OOP 设计模式

近几年的工作都是写 Clojure ,大多数时间是用函数式编程风格。 OOP 的设计模式在函数式语言里显得非常轻量,有时根本意识不到有些用法是设计模式。简单聊一聊,常用的设计模式在Clojure里可能长什么样子。由于语言类型不同,一些和语言构件相关的模式在 Clojure 里不太需要,比如我在写 Clojure 时就很少需要用迭代器模式。

对象是 OOP 编程语言中的一等公民,在函数式语言中函数是编程中的一等公民,所以下面有些地方我会直接用函数来代替对象,如工厂模式用生产函数来代替生产对象。

策略模式

定义:策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

(defn quack [] (println "quack"))
(defn squeak [] (println "squeak"))

(defn make-sound [duck?]
  (let [sound-f (if duck? quack squeak)]
    (sound-f)))

(make-sound true)

小辨析:

策略模式:在context对象主动指定哪个策略对象来执行动作。

状态模式:在context对象中的状态决定使用哪一个state状态对象来操作,state状态对象在执行操作中反过来会改变context对象中的状态。

单件模式

定义:确保一个类只有一个实例,并提供一个全局访问点。

(let [conn (delay (create-conn))]
 (defn get-instance [] @conn))
(get-instance)

工厂模式

定义: 定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。

(defn create-adder
  [n]
  (fn [x]
    (+ n x)))

(map (fn [x] (create-adder x))
  (range 10))

抽象工厂模式在Clojure中似乎和工厂模式没有太多区别。

装饰者模式

定义:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

Clojure 类型继承很弱,依然以函数来实现。

(defn origin [n] n)

(defn decorator
  "add 5"
  [f]
  (fn [n]
    (+ (f n) 5)))

(def new-f (decorator origin))
(new-f 3)

小辨析:

装饰者: 装饰与被装饰者实现同一个接口,在同一个接口内增强功能。

适配器: 适配与被适配者实现的是两个不同的接口。意图是改变接口,满足客户的期望。

外观模式:用一个接口实现子系统的一群接口。意图是,提供子系统的一个简化接口。

观察者模式

定义:观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

(def subject (atom nil))

(defn subscribe [key ref old-state new-state]
  (print "Current val is " new-state))

(add-watch subject :sub subscribe)
(reset! subject {:foo "bar"})
(remove-watch subject :sub)

命令模式

定义:命令模式:将“请求”封装成对象,调用方可以直接调用对象的execute。命令模式也支持可撤销的操作。

(def command1 (fn [] (print "command1")))
(def command2 (fn [] (print "command2")))

(doseq [cmd [command1 command2]]
  (cmd))

小辨析:

策略模式:在调用方需要传入一致的参数.

命令模式:将不同命令的参数封装在命令对象里,只留一个execute方法。

适配器模式

定义:适配器模式,将一个类的接口,转换成客户期望的另一个接口。

(defn add [x y] (+ x y))

(defn adopter [x y]
  (add (Integer/parseInt x) (Integer/parseInt y)))

(adopter "1" "2")

小辨析:

装饰者: 装饰与被装饰者实现同一个接口,在同一个接口内增强功能。

适配器: 适配与被适配者实现的是两个不同的接口。意图是改变接口,满足客户的期望。

外观模式:用一个接口实现子系统的一群接口。意图是,提供子系统的一个简化接口。

Updated on: Sat February 24, 2024