Mixing OOP and FP

So far, we have seen that adding FP capabilities to an OOP language leads to benefits in the OOP design.

In summary, OOP fits perfectly with FP when our objects are as immutable as possible. To make our objects as immutable as possible, we can consider the following principles:

  • Objects should be types that encapsulate related pieces of data
  • Objects can have methods; however, these methods shouldn't change the object and should instead return a new one of the appropriate type
  • All the required state data should be injected into the class's initialization so that it will be ready to use immediately
  • Static methods can be used freely and static variables should be avoided
  • Protocols and generics should be used to avoid code duplicates

These principles not only empower us to employ functional design patterns, but also enrich our object-oriented code.

Problems

There are a few problems in unifying and mixing OOP with FP, which we will cover in the following sections.

Granularity mismatch

FP and OOP operate on different design granularity levels:

  • FP: Function/method programming on small level
  • OOP: Classes / objects / modules programming on large level

To overcome this granularity mismatch, we need to find answers for the following questions:

  • Where do we locate the source of individual functions in an OOP architecture?
  • Where do we relate such individual functions to an OOP architecture?

In Swift, we can place functions inside source files and outside of classes or we can place them as static or class methods.

FP paradigm availability

So far, we explored a lot of different FP paradigms in Swift. Here, we check conceptually whether Swift is a capable language for FP. We will explore the paradigms in the following sections.

First-class values

In an FP language, functions/methods should be first-class citizens. First-class citizen functions will enable us to use most FP paradigms if they satisfy the following rules:

  • Functions/methods should be usable as function/method parameters and arguments
  • Functions/methods can be returned as a result of a function/method
  • Functions can take place in data structures

So far, we have seen an example implementation of all these rules.

Closures

First-class functions/methods should be implemented as closures. For instance, they should be associated with specific private environments.

Swift functions are implemented as closures.

FP-OOP interrelation tools

Standalone functions/methods should be explicitly relatable to the class/object level.

Swift extensions enable us to add methods to existing classes without creating new derived classes.

FP support

FP paradigms should be reinforced by related constructs, predefined definitions, occurrences in standard libraries, and so on.

They should satisfy the following rules:

  • Overloading for generic function types
  • First-class multiple invocation and multicasting
  • Function marshalling and serialization (closures as data structures)

Swift supports the preceding FP paradigms.

Effects of having FP capabilities in OOP

Having FP capabilities in the OOP language causes idiomatic and architectural effects, which are explored in the following sections.

Idiomatic effects

  • Code factoring (abstraction) at a function/method granularity level
  • Generic iterator and loop operations (map)
  • Operation composition and sequence comprehensions (chained function calls)
  • Function partial applications and currying

Architectural effects

  • Reduction of the number of object/class definitions: Avoids cluttering the OOP architecture with new classes
  • Name abstraction at a function method level: Using first-class methods allows parameters to be instantiated by any method satisfying their declared type
  • Operation compositions (and sequence comprehensions)
  • Function partial applications and currying

OOP design patterns - an FP perspective

Design patterns describe recurring solutions to common problems in object-oriented software design. Patterns are categorized into three types:

  • Creational
  • Structural
  • Behavioral

This section introduces some of OOP design patterns on a very high level and introduces the FP counterparts:

  • Strategy
  • Command
  • Observer
  • Proxy
  • Visitor

Strategy pattern

The strategy pattern is a behavioral pattern that lets an algorithm vary independently of clients that use it. In other words, it allows one of a family of algorithms to be selected on-the-fly at runtime.

From an FP perspective, a strategy is just a case of abstracting code at a method level.

Command pattern

The command pattern is a behavioral pattern that encapsulates requests (method calls) as objects so that they can be transmitted, stored, and applied easily.

FP provides closures and first-class functions.

Observer pattern

The observer pattern is a behavioral pattern that allows a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated.

FRP handles this pattern very effectively and declaratively.

Virtual proxy pattern

The virtual proxy pattern is a structural pattern that provides placeholders for other objects in such a way that their data is created/computed only when needed.

FP provides lazy instantiation and evaluation.

Visitor pattern

The visitor pattern is a behavioral pattern that allows us to define new operations without changing the classes of the elements on which they operate.

FP makes functions independent of object changes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset