As Aristotle wrote, “The more you know, the more you realize you don’t know”. Many of us may have already been using some design patterns in software development perhaps not even realising we are doing so.
Design patterns are simply common architectural approaches observed in software engineering practices. The conviction of Design Patterns was popularized in 1994 by The Gang of Four book and translated to every object-oriented programming language, hence making it universally relevant.
Design patterns are also like templates that are helpful while writing code and reusing it. Design patterns differ by their complexity, level of detail and scale of applicability. Some most common Cocoa design patterns are:
· Creational: Creational patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code.
Singleton, Prototype, Factory Method.
· Structural: Structural patterns explain how to assemble objects and classes into larger structures while keeping these structures flexible and efficient.
Decorator, Adapter, Facade.
· Behavioural: Behavioural design patterns are concerned with algorithms and the assignment of responsibilities between objects.
Observer and Template Method.
Here, we are going to get some basic understanding of these patterns, their real-world cases, the pros and cons.
The Singleton Pattern
Singleton Pattern falls under the creational design pattern, it works by ensuring that a class has only one instance, while providing a global access point to this instance.
Australia has only one official government and anyhow the government forms by different individuals having different personal identities, the title, “The Government of Australia”, is a global point of access that identifies the group of people in charge.
· Ensures that class has only one instance and a global access point to that instance.
· The singleton object is initialized only when it’s requested for the first time.
· Singleton can be overused as it is readily available throughout the code base and since its reference is not completely transparent while passing to different methods, it becomes difficult to track.
· Without writing a fully functional class dedicated to the Singleton, it becomes impossible to test as it might cause issues for writing testable code if the object and the methods associated with it are so tightly.
The Prototype Pattern
Using the Prototype pattern, enable us to copy existing objects without making our code dependent on their classes.
By the name itself, prototypes are used as a scale demo of a full-scale thing we want to make. However, in this case, prototypes don’t participate in any actual production, playing a passive role instead. The prototypes are active in creating copies of itself.
· It eliminates the dependency on the initializer in order to create new objects and can produce complex objects more conveniently.
· It can be useful when we want to avoid continuous initialization that could be expensive.
· Cloning complex objects that have circular references might be very tricky. Also, we can avoid this by determining the need for “shallow copying” or “deep copying”.
The Factory Method
Also known as virtual constructor, the factory method provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created.
Imagine you are creating an activity tracking app. The first version of the app can only track time logs so all the code lives inside logClass. Later, the apps become popular and you want to track the geolocation. Adding locationClass into the app would require making changes to the entire codebase. The Factory Method pattern suggests that you replace direct object construction calls (using the new operator) with calls to a special factory method. Now both log and location classes should implement the Tracking interface, which declares a method called tracks. Each class implements this method differently.
· We can introduce new types of products into the program without breaking existing client code.
· Allows us to hide implementation of an application seam
· The code may become more complicated since we need to introduce a lot of new subclasses to implement the pattern.
The Adapter pattern is a structural design pattern that allows objects with incompatible interfaces to collaborate.
Similar to the adapter that we use to connect to different power socket that has incompatible structure, which differs in some countries. Example, frequency in Australia (50Hz) differs from the frequency in the United States of America (60Hz) so we need a combined plug adapter to use the power socket.
· Allow us to separate the interface or data conversion code from the primary business logic of the program.
· Allow introducing new types of adapters into the program without breaking the existing client code, as long as they work with the adapters through the client interface.
· The overall complexity of the code increases because you need to introduce a set of new interfaces and classes.
The Decorator Pattern
Decorator is a structural design pattern that dynamically modifies the behaviour of a core object without changing its existing class definition.
A real-world example of decorator is the jewelry or accessories we wear. All of these extend our basic behaviour but aren’t part of us and we can easily take those off whenever you don’t need it.
· Flexible alternative to subclassing for extending functionality.
· Add or remove responsibilities from an object at runtime.
· Decorators can result in many small objects in our design, and overuse can be complex
· Decorators can cause issues if the client relies heavily on the components concrete type
· The initial configuration code of layers might look pretty ugly.
The ‘Facade’ pattern defines an object representing a subsystem. This object is an interface that will take the responsibility to interact with the subsystem. A Facade class can often be transformed into a Singleton since a single facade object is sufficient in most cases.
The receptionist in a hotel acts as a façade and help us with all services. This helps us in dealing with different departments of the system and have a single point of interface.
· Code isolation from the complexity of a subsystem.
· One of the drawbacks is that the subsystem methods are connected to the Facade layer. If the structure of the subsystem changes then it will require subsequent change to the Facade layer and client methods.
The Observer Pattern
Observer is a behavioural design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing.
Imagine that you’re a firefighter who is always ready for the emergency call when there’s something aflame. If there’s a call coming from somewhere to your handy talkie, you must prepare your perks and get ready to extinguish the flame.
· We can introduce new subscriber classes without having to change the publisher’s code.
· You can establish relations between objects at runtime.
· Subscribers are notified in random order.
Template Method is a behavioural design pattern that defines the skeleton of an algorithm in the superclass but let’s subclasses override specific steps of the algorithm without changing its structure.
The real-world analogy of template method can be WordPress templates. There are several free templates that are utilised by different people and companies but there it can be slightly changed to make the resulting website a little bit different from others.
· Template Method pattern is very useful when your algorithm is well defined, and you have particular things in it vary while the sequence remains the same.
· You can pull the duplicate code into a superclass.
· Template methods tend to be harder to maintain the more steps they have.
Hence, these are the most common patterns that are in use. However, many programmers would also manage to work without using these patterns, but they might also be implementing them without even knowing because Cocoa itself utilises some of these patterns. Having knowledge of these patterns will help to communicate better in a team and solve all sorts of problems using principles of Object-Oriented designs.