Week 6… well this time it was a lot of very interesting content and I am not sure one single week would be enough to do it justice. The aim was to look at Design Patterns and Architecture, ranging from fundamental design patterns, to intermediate ones.
We looked at architectures and design patterns that are used repeatedly so as to avoid re-inventing the wheel unnecessarily. This helps us be quick tackling already solved problems. It provides a framework solution to a problem which people can pickup and go with.
A design pattern is a general repeatable solution to a commonly occurring problem in software design. They are not finished designs that can be transformed directly into code. Instead they are a description or template for how to solve a problem that can be used in many different situations. We talked about three types of patterns:
- Structural: Combining and composing objects. Examples: MVC, MVVM, facade
- Behavioural: Communicating bewteen objects. Examples: delegation, strategy and observer
- Creational: Instantiating objects. Examples: builder, singleton and prototype
Used in UIKit development. It separates things into three types:
- Model – hold on to application data
- View – display visual elements and controls on the screen
- View Controller – coordinate between models and views
Use as a starting point for iOS apps, particularly with UIKit
MVVM separates objects into three distinct groups.
- Models – hold on to app data. They are usually structs or classes
- Views – display visual elements and controls on screen
- Viewmodels – transform model information into value that can be display in the View. They are usually classes as they can be passed by reference. View controllers do exist in the pattern, but their use is minimised
Use this pattern when:
- Transforming models into view representation
- Complements MVC well by moving transformation logic out of view controllers
- Has three parts:
- An object needing a delegate or delegate object. It is the object that has a delegate.
- A delegate protocol, which defined the methods a delegate may or should implement
- The delegate, which is the helper object that implements the delegate protocol
- By relying on a delegate protocol, instead of a concrete object, the implementation is much more flexible
- This pattern is used when
- Breaking up large classes or creating reusable components
- Datasources and delegates both use this pattern
- Detasources provide data
- Delegates receive data
- Defines a family of interchangeable objects that can be set or switched at runtime. It has three parts:
- The object using a Strategy – most often a view controller in iOS dev. It can be any object that needs interchangeable behaviour
- The Strategy Protocol defines methods every strategy must implement
- The strategies are objects that conform to the Strategy Protocol
- Used when we have:
- Two or more interchangeable behaviours
- Family of objects, instead of one
- Easily changeable at runtime
- It restricts a class to only one instance. Every reference to the class refers to the same underlying instance.
- Use a singleton pattern when:
- Having more than one instance would cause problems
- Use singleton plus (allows for other instances to be created) if a shared instance is useful most of the time, but we also want custom instances
It has three parts
- The originator is the object to be saved restored
- The memento represents a stored state
- The caretaker requests a save from the originator. It is responsible for persisting the memento, and later on, providing the memento back to the originator to restore the originator’s state
They may use encoders and decoders. Mementos are used:
- To save and restore an object
- For example, to implement a “save game” system
- By persisting an array of mementos, can implement undo/redo stacks
Lets one object observe changes on another object. Supported from Swift 5.1 with the addition of publisher and the combine framework. It involves three types
- The subscriber is the observer object and received updates
- The publisher is the observable object and send updates
- Value is the underlying object that is changed
- Receiving change made on another object
- In MVC, controller observes model changes
Enables us to create complex objects by providing inputs step by step. Contrast this to an initialiser, which requires inputs upfront. Builder has three parts:
- Director accepts inputs and coordinates with the builder. Usually a View Controller or a helper class that’s used but the VC
- The product is the complex object to be created. This can be either a struct or a class
- The builder accepts step by step inputs and handles the creation of the product
A creational pattern that provides a way to make objects without expulsion, the creation logic. It involves two types:
- The factory creates objects
- The products are the objects that are created
The aim is to isolate object creation within its own construct. It is used when:
- Separating product creation logic out
- Creating multiple, polymorphic types
- Creating complex product with multiple dependencies
A behavioural pattern that allows incompatible types to work together. It involves four components:
- An object using an adapter. It is the object that depends on a the new protocol
- A new protocol – the desired protocol to use
- A legagy object – an object that existed before the protocol was made and cannot be modified to conform to the new protocol
- An adapter is what is created to conform to the protocol and passes calls onto the legacy object
Use it when:
- Can’t modify a class, module or function
- Create adapter using an extension or anew adapter class
A behavioural pattern that provides a standard way to loop through a collection. It involves two types:
- A protocol that can be iterated using a for loop
- A custom object – an object that we want to make iterable
Used when we want to make our container type iterable – so we can iterate through its objects using a
for in loop. Prefer adopting
Sequence to get high-order functions for free.
A creational patterns that allows an object to copy itself. It involves two types:
- A copying protocol that declares copy methods
- A prototype class that conformes to the copying protocol
Use it to enable an object to copy itself.
NSCopying exists, but it doesn’t work great in Swift and results in more boilerplate
Creating a custom
Copying protocol may be more beneficial.
A behavioural pattern that allows an object to change its behaviour at runtime. It does so by changing its current state. Here, state means the set of data that describes how a given object should behave at a given time. It involves three types:
- The context is the object that has a current state and whose behaviour changes
- The state protocol defines required methods and properties. Developers commonly substitute a base state class in place of the protocol. By doing so, they can define stored properties in the base which isn’t possible using a protocol. Even if a class is used, it still isn’t intended to be instantiated directly. It is defined for the sole purpose of being subclassed.
- Concrete states conform to the state protocol or if a base class is used instead, they subclass the base.
- Creating a system with two or more states that change during lifetime
- Creating both open- or closed-set states
- Trying to eliminate if/else and switch statements
A behavioural pattern that is a variation of the delegate pattern. It allows the creation of one-to-many delegate relationships, instead of 1:1 relationships in a simple delegate. It involves 4 types:
- An object needing a delegate, aka the delegating object. It is the object that has one or more delegates
- The delegate protocol defines the methods a delegate may or should implement
- The delegates are objects that implement the protocol
- The multicast delegate is a helper class that holds onto delegate and allows us to notify each delegate whenever a delegate or the event happens
The difference with the delegate pattern is the presence of a multicast delegate helper class.
Use this pattern to:
- Create one-to-many delegate relationships
- Info multiple consumers whenever a change has happened
A structural pattern that provides a simple interface to a complex system. It involves 2 types:
- The facade provides simple methods to interact with the system. This allows consumers to use the facade instead of knowing about and interacting with multiple classes in the system
- The dependencies are objects owned by the facade. Each dependency performs a small part pf a complex task.
Use this pattern:
- Whenever you have a system of components that each perform a small part of a larger task
- Example isa product ordering system with multiple pieces: Customer management, inventory, shipping, etc
For an update on the status of Jelly Belly, see Part 2 of this post.
Also published on Medium.