函数式编程中与装饰器设计模式相对应的是什么?
例如,您将如何以函数式风格编写这个特定示例?
函数式编程中与装饰器设计模式相对应的是什么?
例如,您将如何以函数式风格编写这个特定示例?
(defn draw [& args]
; do some stuff
)
我的函数包装器:
; Add horizontal scrollbar
(defn add-horizontal-scrollbar [draw-fn]
(fn [& args]
(draw-horizontal-scrollbar)
(apply draw-fn args)))
; Add vertical scrollbar
(defn add-vertical-scrollbar [draw-fn]
(fn [& args]
(draw-vertical-scrollbar)
(apply draw-fn args)))
; Add both scrollbars
(defn add-scrollbars [draw-fn]
(add-vertical-scrollbar (add-horizontal-scrollbar draw-fn)))
这些函数返回一个新函数,可在任何使用原始绘图函数的地方使用,但也会绘制滚动条。
comp
: (def add-scrollbars (comp add-vertical-scrollbar add-horizontal-scrollbar))
。 - user1804599柯里化函数参数/组合是最接近的等效方法。然而,甚至提出这个问题都是错误的,因为模式存在于补偿宿主语言弱点的情况下。
如果C++/Java/C#或任何其他实际上相同的语言中内置装饰功能,你就不会将其视为一种模式。恰好“模式”是在早期绑定的命令式面向对象语言中用于结构化系统的模式,通常没有自动包装,并且根类的协议相对较少。
编辑:此外,在这些语言中,很多这样的东西被研究为模式,因为没有明显的内置高阶函数、高阶类型和类型系统也相对无用。显然,这不是这些语言的普遍问题,但当这些模式开始被编码时,这些问题存在。
do
符号的语言支持之前,这几乎是一种模式(在某些情况下我仍然会称其为函数模式)。然而,Haskell有一些优点使得它看起来不像是一种模式(操作符支持、类型类和简洁的lambda符号)。 - Ionuț G. Stan; define a collection with some missing (nil) values
(def nums [1 2 3 4 nil 6 7 nil 9])
; helper higher order function to "wrap" an existing function with an alternative implementation to be used when a certain predicate matches the value
(defn wrap-alternate-handler [pred alternate-f f]
(fn [x]
(if (pred x)
(alternate-f x)
(f x))))
; create a "decorated" increment function that handles nils differently
(def wrapped-inc
(wrap-alternate-handler nil? (constantly "Nil found!") inc))
(map wrapped-inc nums)
=> (2 3 4 5 "Nil found!" 7 8 "Nil found!" 10)
这种技术在函数库中被广泛应用。一个很好的例子是使用Ring中间件包装Web请求处理程序 - 所链接的示例将html请求的参数处理包装在任何现有处理程序周围。
类似这样的:
class Window w where
draw :: w -> IO ()
description :: w -> String
data VerticalScrollingWindow w = VerticalScrollingWindow w
instance Window w => Window (VerticalScrollingWindow w) where
draw (VerticalScrollingWindow w)
= draw w >> drawVerticalScrollBar w -- `drawVerticalScrollBar` defined elsewhere
description (VerticalScrollingWindow w)
= description w ++ ", including vertical scrollbars"
Window
类型类的实例,如上所示)确实是可堆叠的。您可以编写类似于do { let { w = SimpleWindow; w1 = VerticalScrollingWindow w; w2 = BorderedWindow w1 }; draw w2; }
的代码。 - rkhayrovdata Window = Window { draw :: IO (), description :: String }
? - C. A. McCannclass Window w where
draw :: w -> IO ()
description :: w -> String
抽象窗口装饰器类
这个有点棘手,因为Haskell没有继承的概念。通常我们根本不会提供这种类型,而是直接让装饰器实现Window
,但让我们完全遵循示例。在这个示例中,一个WindowDecorator
是一个带有窗口参数的构造函数的窗口,让我们增加一个函数来获取装饰后的窗口。
class WindowDecorator w where
decorate :: (Window a) => a -> w a
unDecorate :: (Window a) => w a -> a
drawDecorated :: w a -> IO ()
drawDecorated = draw . unDecorate
decoratedDescription :: w a -> String
decoratedDescription = description . unDecorate
instance (WindowDecorator w) => Window w where
draw = drawDecorated
description = decoratedDescription
Window
的默认实现,它可以被替换,所有WindowDecorator
的实例都会是一个Window
。
装饰器
制作装饰器可以按照以下步骤完成:
data VerticalScrollWindow w = VerticalScrollWindow w
instance WindowDecorator VerticalScrollWindow where
decorate = VerticalScrollWindow
unDecorate (VerticalScrollWindow w ) = w
drawDecorated (VerticalScrollWindow w ) = verticalScrollDraw >> draw w
data HorizontalScrollWindow w = HorizontalScrollWindow w
instance WindowDecorator HorizontalScrollWindow where
decorate = HorizontalScrollWindow
unDecorate (HorizontalScrollWindow w .. ) = w
drawDecorated (HorizontalScrollWindow w ..) = horizontalScrollDraw >> draw w
结束
最后,我们可以定义一些窗口:
data SimpleWindow = SimpleWindow ...
instance Window SimpleWindow where
draw = simpleDraw
description = simpleDescription
makeSimpleWindow :: SimpleWindow
makeSimpleWindow = ...
makeSimpleVertical = VerticalScrollWindow . makeSimpleWindow
makeSimpleHorizontal = HorizontalScrollWindow . makeSimpleWindow
makeSimpleBoth = VerticalScrollWindow . HorizontalScrollWindow . makeSimpleWindow
首先,让我们尝试从面向对象编程的角度找到装饰器模式的所有主要组件。该模式基本上用于装饰,即为现有对象添加额外功能。这是该模式的最简单定义。现在,如果我们试图在FP世界中找到与此定义相同的组件,我们可以说额外功能=新函数,而对象在FP中不存在,而是FP拥有您所谓的数据或以各种形式表示的数据结构。因此,在FP术语中,该模式变为为FP数据结构添加附加函数或使用一些附加功能增强现有函数。
我不是100%确定,但我认为C9高级函数式编程系列讲座非常好地解释了这个问题。
除此之外,你可以在F#中使用完全相同的技术(它支持完全相同的面向对象机制),在这种特殊情况下,我会这样做。
我想这是品味和你试图解决的问题的问题。
function Log(app) {
return function(request) {
var response = app(request);
console.log(request.headers.host, request.path, response.status);
return response;
};
}
var app = Logger(function(request) {
return {
status: 200,
headers: { "Content-Type": "text/plain" },
body: ["hello world"]
};
}
当然,符合规范的中间件可以堆叠起来使用(例如:Lint(Logger(ContentLength(app)))
)
type Window = {Description: string}
type HorizontalScroll = {HPosition: int}
type VerticalScroll = {VPosition: int}
type Feature =
| HorizontalScroll of HorizontalScroll
| VerticalScroll of VerticalScroll
let drawWithFeatures w (f: Feature) =
match f with
| HorizontalScroll h -> {Description= w.Description + "Horizontal"}
| VerticalScroll v -> {Description= w.Description + "Vertical"}
type FeatureTwo = Red of int| Blue of int | Green of int | Feature of Feature
let rec drawWithMoreFeatures (w: Window) (ft:FeatureTwo)=
match ft with
| Red x -> {Description = w.Description + "Red"}
| Green x -> {Description = w.Description + "Green"}
| Blue x -> {Description = w.Description + "Blue"}
| Feature f -> drawWithFeatures w f
let window = {Description = "Window title"}
let horizontalBar = HorizontalScroll {HPosition = 10}
let verticalBar = VerticalScroll {VPosition = 20}
[Red 7; Green 3; Blue 2; Feature horizontalBar; Feature verticalBar] |> List.fold drawWithMoreFeatures window
这是我尝试在 F# 中创建一些有意义的东西,因为你要求了很多例子。我有点生疏,希望没有人会羞辱我 :P。装饰器基本上需要两个部分,新行为和新数据。在函数式语言中,新行为非常容易实现,因为它们只是“另一个函数”,因为函数与对象本质上是解耦的。新数据实际上也同样容易实现,有几种方法可以实现,最简单的方法是使用元组。你可以看到我创建了一个新的数据类型,它是先前数据类型的超集,并且我已经为该现有行为调用了现有函数。因此,我们仍然尊重旧数据和旧行为,但我们也拥有了新行为和新数据。