In software engineering, a design pattern is a general reusable solution to a commonly occurring problem in software design. A design pattern is not a finished design that can be transformed directly into code. It is a description or template for how to solve a problem that can be used in many different situations. Object-oriented design patterns typically show relationships and interactions between classes or objects, without specifying the final application classes or objects that are involved. (source: Wikipedia)
It is always advantageous to move from a design pattern (a loose description of a solution) to an implementation in a library (or a language feature), which would be concrete and directly re-usable. The problem is that this often clashes with the need to keep the solution general enough to maintain its usefulness in different situations.However, if we use a sufficiently high-level language like Timber, we can hope that we can express at least some of the design patterns in Timber code.
Below you are presented with two examples – one design pattern than can be expressed in Timber and another that cannot. You task will be to do the same for several other design patterns.
In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure it operates on. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. (source: Wikipedia)
The essense of a visitor pattern is to add a special method accept to some class C (and hence to all its subclasses). The method accept then takes one argument – an object of a very general interface containing only one method visit, and accept invokes the method visit of its arguement with this (the object of class C) as an argument. The visit method will then perform some work on the object of class C or any of its subclasses, possibly differentiating between them (see the example in Wikipedia).
To begin with, this design pattern clearly appeared since languages like Java do not allow sending functions or methods as arguments, but demand that these are always encapsulated into objects. A Visitor is simply an encapsulation of a method that can operate on different classes (types) of objects. Thus it is possible to implement this pattern in Timber (and any other functional language) by allowing the accept method to take a procedure as an argument (I'm working here with the example from Wikipedia linked above):
wheel = class
... {- state variables and other methods of struct Wheel -}
accept proc = action
proc my_interface -- or some other code that includes "proc"
my_interface = Wheel{..}
result my_interface
Note that if fcn can be such that is requires a Wheel, or a superclass of Wheel, and this will be typechecked by the Timber compiler. We may also need to give a name to the type of objects that have the method accept in their interfaces, and we do this as follows:
struct CanBeVisited a b where
accept :: (a -> Cmd () b) -> Action
struct Wheel < (CanBeVisited Wheel ()) where
... {- other methods defined in Wheel -}
Here the parameters a and b are the type of the argument required by the procedure and its return type.
This implementation exaclty corresponds to one offered by Java, etc. but – unlike Java – it does not confuscate the situation by involving extra objects; however, this is far from the best solution since the accept method needs to be added to each class in advance. In fact, Timber offers another solution that does not require any such preparatory work – namely, typeclasses. Indeed, we can define the classes wheel and other as we like, and if we then decide that we need to add an operation op37 that would operate on objects of the class wheel (alongside a number of other classes), we can easily define a typeclass
typeclass MyTypeClass a where op37 :: a -> Bool
and then we define an instance of MyTypeClass for type Wheel, i.e. we provide an implementation for this new operation which is totally independent of the original class definition but which at the same time will work on any object of the class wheel (and any other class that returns an interface of type Wheel):
instance mtcWheel :: MyTypeClass Wheel where
op37 w = ... {- some implementation -}
In software engineering, the singleton pattern is a design pattern used to implement the mathematical concept of a singleton, by restricting the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. (source: Wikipedia)
The singleton pattern is normally implemented by having a (hidden) global reference to an object and a (visible) static function that returns a reference to it (or constructs it on its first invocation). In Timber there is no global state (including no statically defined objects) and any class definition that is in scope can be used multiple times. Thus it is not possible to express this pattern in Timber.
Note, however, that when an object is created outside the Timber system (e.g. the POSIX environment object that is constructed by an extern function), it is quite possible that multiple invocations of the class return the reference to the same object rather than construct a new one. This, however, is outside Timber semantics and cannot be expressed in the language itself.
Consider Timber implementation for the following design patterns:
You can add any other patterns if you like, but in that case please inform me which patterns you are looking at.