Functional equivalent of decorator pattern?

后端 未结 10 2107
悲&欢浪女
悲&欢浪女 2021-02-02 06:34

What is the functional programming equivalent of the decorator design pattern?

For example, how would you write this particular example in a functional style?

相关标签:
10条回答
  • 2021-02-02 07:23

    Here's an example using JSGI, a web server API for JavaScript:

    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"]
         };
      }
    

    Compliant middleware can be stacked, of course (e.x. Lint(Logger(ContentLength(app))))

    0 讨论(0)
  • 2021-02-02 07:32

    In functional programming, you would wrap a given function in a new function.

    To give a contrived Clojure example similar to the one quoted in your question:

    My original drawing function:

    (defn draw [& args]
      ; do some stuff 
      )
    

    My function wrappers:

    ; 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)))
    

    These return a new function that can be used anywhere the original drawing function is used, but also draw the scrollbars.

    0 讨论(0)
  • 2021-02-02 07:36

    Currying functional parameters / composition is the closest equivalent. However, it's a mistake to even ask this question, because patterns exist to compensate for weaknesses in the host language.

    If C++/Java/C#/any other practically identical language had a decoration feature built into the language, you wouldn't think of it as a pattern. It just so happens that "patterns" are patterns for structuring systems in early-bound imperative objective-oriented languages, usually without autoboxing, and with relatively thin protocols for the root class.

    Edit: Also a lot of these are studied as patterns in these languages because there's no obvious built in higher order functions, higher order typing, and the type systems are relatively useless. Clearly, that's not a universal problem with these languages, but at the time these patterns started to be codified those issues were present.

    0 讨论(0)
  • 2021-02-02 07:36
    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# attempt

    This is my attempt at creating something sensible in F# since you asked for many examples. I'm a little rusty so hopefully nobody will shame me :P. The decorator basically requires two parts, new behaviors and new data. New behaviors are exceedingly easy in functional languages as they are "just another function", since functions are inherently decoupled from objects. New data is actually similarly easy and there's several ways you can achieve this, the simplest being a tuple. You can see I've created a new datatype that is a superset of the previous, and I've called the existing function for that existing behavior. So we have our old data and old behavior still respected, but we also have new behavior and new data.

    0 讨论(0)
提交回复
热议问题