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