Objective C Design Patters1
Objective C Design Patters1
Asfar
Design Principles
Programming To An Interface, Not An
Implementation
Define classes with identical interfaces is important because
polymorphism depends on the interfaces.
In Objective-C, protocol acts like a contract of objects. Implementing a
protocol or inheriting from an abstract class let objects share the same
interfaces
So all objects of the subtypes can respond to the requests in the interface
of the protocol or abstract class.
There are two benefits from the practice:
As long as the objects conform to the interface that clients expect, the clients
shouldnt be aware of the exact types of objects they use.
Clients only know about the protocol(s) or abstract class(es) defining the
interface, so the clients dont know anything about the objects classes.
This leads to the principle of reusable object-oriented software design
according to the Gang of Four book:
Program to an interface, not an implementation.
A common practice in client code is not to declare variables of particular
concrete classes objects. Instead, use only an interface defined by a
protocol or an abstract class.
Object Composition vs. Class Inheritance
Class inheritance or subclassing allows you to define an
implementation of a class in terms of anothers.
Subclassing is often referred to as white-box reuse because the
internal representation and details of parent classes are often
visible to subclasses.
Object composition is an alternative to class inheritance.
Object composition requires that the objects being composed
have well-defined interfaces and that they are defined
dynamically at runtime through references acquired by other
objects.
So you can compose objects within other ones to create more
complex functionality.
Since no internal details of objects are visible to others, they
appear as black boxes and this style of reuse is called black-
box reuse.
class inheritance
Pros:
Class inheritance is straightforward to use because the relationship is defined
statically at compile-time.
It makes it easier to modify the implementation being reused.
Cons:
Because class inheritance is defined at compile-time, you cant change the
inherited implementations from parent classes at runtime.
Part of the representation in subclasses is often defined in parent classes.
Subclasses are exposed to details of parent classes implementation, so it breaks
encapsulation.
Any change in the parents implementation will force its subclasses to change as
well because their implementations are so tied up together.
You need to rewrite the parent class or the inherited implementation because the
inherited implementation becomes obsolete or inappropriate for new problem
contexts.
Reusing a subclass can be problematic due implementation dependencies. One
solution for this is to inherit (subtype) only from protocol(s) or abstract (base)
class(es), as they usually have little or, in a protocol, no implementation.
Object Composition
Pros:
You dont break encapsulation because objects are now accessed only through
their interfaces.
There are substantially fewer implementation dependencies, as the objects
implementation is defined in terms of the interfaces.
You can replace any object at runtime with another of the same type.
It helps keep a class encapsulated so it can focus on one task.
Your classes and their hierarchies will remain small. They will be less likely to grow
into something unmanageable.
Cons:
The design will tend to have more objects.
The behavior of the system will depend on the relationships of different objects
instead of being defined in one class.
Ideally, there is no need to create new components to achieve reuse. It is quite
rare that you should be able to get all the functionality you need just by
assembling existing components through object composition. In practice, the set
of available components is never quite rich enough.
Despites the cons, object composition can provide many benefits on system design.
Those cons can be counter-balanced by using class inheritance in certain areas as it
helps make it easier to make new components from old ones.
favor object composition over class
inheritance
It doesnt mean that you shouldnt use class
inheritance at all. You need to make a clear
judgment on how to reuse classes and objects
in certain situations.
Class inheritance and object composition can
work together if you design them properly for
your systems.
You will see object composition in the design
patterns discussed later.
Design Patterns
Class Clusters -- Abstract Factory
An abstract superclass is declared with
methods that are implemented in nonvisible,
concrete subclasses.
Simplify the complexity of public classes by
revealing only the methods declared in the
abstract parent class.
The abstract superclass is responsible for
providing methods that create instances of
the private subclasses.
Why use class clusters
Two common motivations for taking this design approach:
Performance/Memory
This type of design allows a developer to make a single class that can represent the
creation point for multiple objects that have significantly different memory
requirements.
The classic example of this is the NSNumber class.
NSNumber is a cluster representing all sorts of different number types (Integer, Double,
Long, Float, Char). All of these types can be converted from one to another, and all
share many different methods that act on them. But they all require different amounts
of memory. Therefore, Apple made NSNumber the cluster that handles the
management of all the hidden subclasses that handle the different types.
Simplicity
Creating a class cluster can simplify the interface to a collection of similar classes.
All the different objects contained in NSNumber would be very unwieldy to develop
without the NSNumber wrapper. Operations such as conversion, or arithmetic
operations like division would require developers to be very careful about memory
management. NSNumber, however, shifts this responsibility from the developer to the
class cluster, greatly simplifying the use of these similar classes.
Number Cluster
Number is the abstract superclass that declares in its methods the
operations common to its subclasses. However, it doesnt declare an
instance variable to store a number.
The subclasses declare such instance variables and share in the
programmatic interface declared by Number.
private classes are in gray
Creating Instances
Users of this hierarchy see only the public class, Number, so
how is it possible to allocate instances of the proper
subclass?
The abstract superclass in a class cluster must declare
methods for creating instances of its private subclasses.
Its the superclasss responsibility to dispense an object of
the proper subclass based on the creation method that
you invokeyou cant choose the class of the instance.
In the Foundation framework, you generally create an
object by invoking a +className ... method or the
alloc... and init... methods.
NSNumber example
Foundation frameworks NSNumber class provides these messages
to create number objects:
NSNumber *aChar = [NSNumber numberWithChar:a];
NSNumber *anInt = [NSNumber numberWithInt:1];
NSNumber *aFloat = [NSNumber numberWithFloat:1.0];
NSNumber *aDouble = [NSNumber numberWithDouble:1.0];
Each object returnedaChar, anInt, aFloat, and aDouble - belong to a
different private subclass.
Although each objects class membership is hidden, its interface is
public, being the interface declared by the abstract superclass,
NSNumber.
Although it is not precisely correct, its convenient to consider the
aChar, anInt, aFloat, and aDouble objects to be instances of the NSNumber
class, because theyre created by NSNumber class methods and
accessed through instance methods declared by NSNumber.
Class Clusters with Multiple Public Superclasses
Its also possible, to have two (or possibly more) abstract public classes
that declare the interface for the cluster
NSData
NSData
NSMutableData
NSArray
NSArray
NSMutableArray
NSDictionary
NSDictionary
NSMutableDictionary
NSString
NSString
NSMutableString
In each of these clusters, one public node declares methods
that all cluster objects can respond to, and the other node
declares methods that are only appropriate for cluster
objects that allow their contents to be modified.
This factoring of the clusters interface helps make the
interface more expressive.
For example, imagine an object representing a book that
declares this method:
- (NSString *)title;
The book object could return its own instance variable or create
a new string object and return thatit doesnt matter. Its clear
from this declaration that the returned string cant be modified.
Any attempt to modify the returned object will elicit a compiler
warning.
Class Factory Methods
Class factory methods are implemented by a class
as a convenience for clients. They combine
allocation and initialization in one step and return
the created object.
The client receiving this object does not own the
object and thus (per the object-ownership policy)
is not responsible for releasing it.
These methods are of the form + (type
)className ... (where className excludes any
prefix).
NSDate/ NSData class factory
methods:
+ (id)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;
+ (id)dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)secs;
+ (id)dateWithTimeIntervalSince1970:(NSTimeInterval)secs;
NSData :
+ (id)dataWithBytes:(const void *)bytes length:(unsigned)length;
+ (id)dataWithBytesNoCopy:(void *)bytes length:(unsigned)length;
+ (id)dataWithBytesNoCopy:(void *)bytes length:(unsigned)length
freeWhenDone:(BOOL)b;
+ (id)dataWithContentsOfFile:(NSString *)path;
+ (id)dataWithContentsOfURL:(NSURL *)url;
+ (id)dataWithContentsOfMappedFile:(NSString *)path;
Uses
The allocation can inform the initialization.
As an example, lets say you must initialize a collection object from a
property-list file that encodes any number of elements for the collection
(NSString objects, NSData objects, NSNumber objects, and so on).
Before the factory method can know how much memory to allocate for the
collection, it must read the file and parse the property list to determine how
many elements there are and what object type these elements are.
Implementation of singleton classes.
Although an init... method could verify that only one instance exists at any one
time in a program, it would require the prior allocation of a raw instance
and then, in memory-managed code, would have to release that instance. A
factory method, gives you a way to avoid allocating memory for an object that
you might not use: