which GOF Design pattern(s) has entirely different implementation (java vs Scala)

前端 未结 3 1050
终归单人心
终归单人心 2021-01-29 18:59

Recently I read following SO question :

Is there any use cases for employing the Visitor Pattern in Scala? Should I use Pattern Matching in Scala every

3条回答
  •  天涯浪人
    2021-01-29 19:31

    Ok, let's have a brief look at these patterns. I'm looking at all these patterns purely from a functional programming point of view, and leaving out many things that Scala can improve from an OO point of view. Rex Kerr answer provides an interesting counter-point to my own answers (I only read his answer after writing my own).

    With that in mind, I'd like to say that it is important to study persistent data structures (functionally pure data structures) and monads. If you want to go deep, I think category theory basics are important -- category theory can formally describe all program structures, including imperative ones.

    Creational Patterns

    A constructor is nothing more than a function. A parameterless constructor for type T is nothing more than a function () => T, for example. In fact, Scala's syntactical sugar for functions is taken advantage on case classes:

    case class T(x: Int)
    

    That is equivalent to:

    class T(val x: Int) { /* bunch of methods */ }
    object T {
      def apply(x: Int) = new T(x)
      /* other stuff */
    }
    

    So that you can instantiate T with T(n) instead of new T(n). You could even write it like this:

    object T extends Int => T {
      def apply(x: Int) = new T(x)
      /* other stuff */
    }
    

    Which turns T into a formal function, without changing any code.

    This is the important point to keep in mind when thinking of creational patterns. So let's look at them:

    Abstract Factory

    This one is unlikely to change much. A class can be thought of as a group of closely related functions, so a group of closely related functions is easily implemented through a class, which is what this pattern does for constructors.

    Builder

    Builder patterns can be replaced by curried functions or partial function applications.

    def makeCar: Size => Engine => Luxuries => Car = ???
    def makeLargeCars = makeCar(Size.Large) _
    
    def makeCar: (Size, Engine, Luxuries) => Car = ???
    def makeLargeCars = makeCar(Size.Large, _: Engine, _: Luxuries)
    

    Factory Method

    Becomes obsolete if you discard subclassing.

    Prototype

    Doesn't change -- in fact, this is a common way of creating data in functional data structures. See case classes copy method, or all non-mutable methods on collections which return collections.

    Singleton

    Singletons are not particularly useful when your data is immutable, but Scala object implements this pattern is a safe manner.

    Structural Patterns

    This is mostly related to data structures, and the important point on functional programming is that the data structures are usually immutable. You'd be better off looking at persistent data structures, monads and related concepts than trying to translate these patterns.

    Not that some patterns here are not relevant. I'm just saying that, as a general rule, you should look into the things above instead of trying to translate structural patterns into functional equivalents.

    Adapter

    This pattern is related to classes (nominal typing), so it remains important as long as you have that, and is irrelevant when you don't.

    Bridge

    Related to OO architecture, so the same as above.

    Composite

    Lot at Lenses and Zippers.

    Decorator

    A Decorator is just function composition. If you are decorating a whole class, that may not apply. But if you provide your functionality as functions, then composing a function while maintaining its type is a decorator.

    Facade

    Same comment as for Bridge.

    Flyweight

    If you think of constructors as functions, think of flyweight as function memoization. Also, Flyweight is intrinsic related to how persistent data structures are built, and benefits a lot from immutability.

    Proxy

    Same comment as for Adapter.

    Behavioral Patterns

    This is all over the place. Some of them are completely useless, while others are as relevant as always in a functional setting.

    Chain of Responsibility

    Like Decorator, this is function composition.

    Command

    This is a function. The undo part is not necessary if your data is immutable. Otherwise, just keep a pair of function and its reverse. See also Lenses.

    Interpreter

    This is a monad.

    Iterator

    It can be rendered obsolete by just passing a function to the collection. That's what Traversable does with foreach, in fact. Also, see Iteratee.

    Mediator

    Still relevant.

    Memento

    Useless with immutable objects. Also, its point is keeping encapsulation, which is not a major concern in FP.

    Note that this pattern is not serialization, which is still relevant.

    Observer

    Relevant, but see Functional Reactive Programming.

    State

    This is a monad.

    Strategy

    A strategy is a function.

    Template Method

    This is an OO design pattern, so it's relevant for OO designs.

    Visitor

    A visitor is just a method receiving a function. In fact, that's what Traversable's foreach does.

    In Scala, it can also be replaced with extractors.

提交回复
热议问题