Prerequisite
It will be better if you have a rough understanding of object oriented programming and its concepts like encapsulation, polymorphism and inheritance. If you don't have those then this article may appear a little confusing. If it does then comment back in, I will try my best to explain. All of the example code that I provide here will follow Java syntax.
What are design patterns
From wikipedia -
A design pattern in architecture and computer science is a formal way of documenting a solution to a design problem in a particular field of expertise.
What do you understand from the above two lines ?
If you ask me, I will say - not a darn thing. This is the problem that occurs to all the beginners in design patterns. People and websites who teach these stuff seem to talk in a way that is very hard for the beginners to understand. Rather than pursuing these heavy definitions, let us learn what design patterns are by looking at their applications.
Do you know what are Objects
To simply put, an object is something that has -
- data (or, you can say attributes/properties/states)
- operations that operate on the data (they are also called methods)
Objects are used to perform some operations using their data. An object performs an operation when it receives a request from another object. The requesting object is said to be a
client of the first object. You request an operation of an object by invoking one of its methods with appropriate arguments. In return, that operation performs some job for you and may return you a response.
Enough talk. Let's see some code.
Before you create an object, you need to let the compiler know what data and operations will that object contain. You do this by declaring a class as follows -
public class Person
{
protected string name;
public person(){}
public string getName()
{
return this.name;
}
public void setName(string name)
{
string escapedName = EscapeString.escape(name);
this.name = name;
}
}
You can think of the class as a blue print for compilers which describes what data and operations will an object contain in it.
And now, to create an object, you use the following syntax -
Person aPerson = new Person();
Again I would like to point out that this article is not for those who are absolute beginner in object oriented programming . This article provides a different perspective of those concepts. You should acquire basic concepts from this fine article that Java's official site provides.
Requests are the only way to get an object to execute an operation. Operations are the only way to change an object’s internal data. Because of these restrictions, the object’s internal state is said to be encapsulated.
Meet Encapsulation
Encapsulation means that the data that an object contains cannot be accessed directly from outside that object, and its representation is invisible from outside the object. It is a great way to develop a loosely coupled software components.
What the heck do the above two lines mean ?
It means that you cannot access the name property of the Person object directly. If you need to get that value then you will have to request it through the getName method. Doing it this way gives the designer of the object a lot of flexibility, i.e., he can change the name of the variable to myname and it still won't cause any code to change outside this object. The designer can even choose to store the name value in a Java Map or in an array without much trouble.
Does design patten help us to maintain encapsulation
Yes, it does! There are a lots of design patterns that are targeted specifically for this purpose. One of them is Strategy Pattern. It helps us to encapsulate complex data structures, algorithms, changing behaviors into their own separate class and protect the rest of the system from any change that may occur in them. So, we can say -
Design patterns help us to maintain Encapsulation when we write an application in a true object oriented way.
This is one of the most important use of design patterns.
How should we determine what should be an object
- You can write a problem statement, single out the nouns and verbs, and create corresponding classes and operations.
- Or you can focus on the collaborations and responsibilities in your system.
- Or you can model the real world and translate the objects found during analysis into design.
Do you understand anything from the above three points ?
If you don't, then don't worry. These are part of a broader topic known as system analysis and design, which I will cover later in the future.
What you would like to know whether or not design patterns help you to determine what should be an object in your system, and you will be glad to know that they do. There is a pattern called Facade which describes how to represent subsystems as objects. There is also another pattern known as Flyweight that describes how to support huge number of objects at the finest granularities.
So we can say -
Design patterns help us to decide what should be an object in our system.
This is another important use of design patterns.
Method signature, Object Interface, Types, Subtypes and Supertypes
Every operation declared by an object specifies the operation’s name, the objects it takes as parameters, and the operation’s return value. This is know as the operation’s signature. Don't confuse it with method signature which doesn't include the return type of a method.
Let us consider the Person object discussed earlier. The method signature of its getname operation is -
string getName()
The set of all signatures defined by an object’s operations is called the interface to the object (beware people, it’s not the same as our programming language’s interface construct). There is one thing that you should be aware of. Some OOP languages allow us to declare methods that cannot be accessed from outside that object (i.e., in java using private, protected keywords). Those methods won't be included in the object interface since you cannot request those operations to an object from outside that object.
If we consider the Person object, then its interface is -
{void setName(string name), string getName()}
An object’s interface characterizes the complete set of requests that can be sent to the object. Any request that matches a signature in the object’s interface may be sent to the object.
A type is a name used to denote a particular interface. We speak of an object as having the type X if it accepts all requests for the operations defined in the interface X. An object may have many types, and widely different objects can share a type.
As an example, we can say that the interface that our Person object defines is of type Person.
Interfaces can contain other interfaces as subsets. We say that a type is a subtype of another if its interface contains the interface of its supertype.
Let us consider an example. Suppose that I have a class called Man whose interface is as follows -
{void setName(string name), string getName(), void playCricket(), void playKabadi()}
This object's interface contains all the method signatures that are defined in the Person object's interface. So in this case we can say that Man is a subtype of Person, and Person is a supertype of Man. We can also say that this Man object's interface inherits the Person's object's interface.
Why am I talking about these stuff?
Because interfaces are fundamental in object-oriented systems. Objects are known only through their interfaces. There is no way to know anything about an object or to ask it to do anything without going through its interface. An object’s interface says nothing about its implementation though. Different objects are free to implement requests differently. That means two objects having completely different implementations can have identical interfaces.
Let me clarify the last point with an example. Consider the following definition of the Man object -
public class Man extends Person
{
public Man(){}
public string getName()
{
return EscapeString.escape(this.name);
}
public void setName(string name)
{
this.name = name;
}
public void playCricket()
{
System.out.Print("I am playing cricket");
}
public void playKabadi()
{
System.out.Print("I am playing kabadi");
}
}
This example uses inheritance to make sure that the Man object has the Person object's interface as its subtype. You can learn more about inheritance by reading the Sun Java's article. All I will say is this - since we are inheriting Man class from Person class, then all of the methods that constitute its object interface will also be included in the Man class. The Man class is providing a different implementation to those methods by overriding definition/implementation of those methods. That's all we care about for this article.
Those who find this example confusing, I would like to say to them that Object Interface and Java/C# language's interface aren't the same thing, though they have so much in common. Object interface is the collection of all signatures of all the methods that can be called from outside that object; where as an interface in C#/Java defines a collection of method signatures that some object can implement, thus including those signatures in its Object Interface. In this sense you can say that an interface construct in C#/Java defines an object interface, but it isn't the only way. In those languages we can ensure that an object has a specific Object Interface by one of two ways -
- By extending/inheriting another from another object. In this way the object's Object Interface from which it is being inherited will be included in this object.
- By declaring an interface, which is a collection of method signature, using the interface keyword that those languages provide and implementing that interface.
Probably now you will want to say that aren't these two are the same thing? I will say, no, because an object that doesn't implement any Programming Language's interface still has an Object Interface, which is the collection of all the signatures of all those methods that can be called outside from this object.
Please think about this distinction thoroughly before moving on.
From our definition of interface, type and subtype, we can say that Man class also has Person as its type, because it accepts all the operations defined in the Person interface type. It's supertype is Person, and it's a subtype of Person.
Now, look at the implementation of the getName and setName. They are completely different from the Person's implementation, although they have common interface.
Since these objects have common interface, you can use one in place of another when you're dealing with only that common interface. Let me clarify it with another example. Consider the following method of a certain class -
public string returnProcessedName(Person p)
{
this.nameProcessor = p;
return this.nameProcessor.getName();
}
Now I can call this method like this -
Person newPerson = new Person();
CertainClass c = new CertainClass();
newPerson.setName("Stupid");
string processedName = c.returnProcessedName(newPerson);
Or like this -
Man newMan = new Man();
CertainClass c = new CertainClass();
newMan.setName("Stupid");
string processedName = c.returnProcessedName(newMan);
This code snippet is also valid. Why? Because both the Person and the Man object has a common interface, which we have named Person before. So you can use one object in place of another whenever you are dealing with that interface. If you pass a Person object's reference to the returnProcessedName
method, then it will call the getName
implementation that this class or object provides. Similarly, if you pass a Man object's reference then its implementation will be called. Hence we can say that which implementation of getName will be called will be determined at runtime, which means when the application runs, rather than at compile time, which means when the application is compiled. This runtime association of a request to one of its implementation is known as Dynamic Binding.
Dynamic binding means that issuing a request doesn’t commit you to a particular implementation until run-time. Consequently, you can write programs that expect an object with a particular interface, knowing that any object that has the correct interface will accept the request.
This property is very, very important because Polymorphism depends on it.
So, we have Stumbled Upon Polymorphism
Dynamic binding lets you substitute objects that have an identical interfaces for each other at run-time. This substitutability is known as polymorphism. It lets a client object make few assumptions about other objects beyond supporting a particular interface. As a result, it simplifies the definition of clients, decouples objects from each other and lets them vary their relationships to each other at run-time. These inter-changeable classes are said to display polymorphic behaviors.
Let us discuss this with respect to the last example.
In our last example, the CertainClass is a client of the Person interface. Since both the Person object and Man object have this interface, we have been able to use both of these objects in the CertainClass. The client class doesn't even have to know which implementation it is being provided as long as the implementation or object has this Person interface. This is the true meaning of polymorphism, and this client object CertainClass
is said to display polymorphic behavior because at run-time it can achieve one of two different behaviors by simply switching between the instances of the Person and Man class like this -
Person newPerson = new Person();
CertainClass c = new CertainClass();
newPerson.setName("Stupid");
string processedName = c.returnProcessedName(newPerson);
System.out.println("Current name: " + processedName);
Man newMan = new Man();
newMan.setName("Stupid");
processedName = c.returnProcessedName(newMan);
System.out.println("Current name: " + processedName);
This greatly reduces the complexity of accommodating changes in our application because if you design an application using polymorphism then you can plug in any implementation of this interface in your application without having to test and re-test your application and without breaking any of the existing code.
Does design pattern help us achieve polymorphism
Yes, it does! So we have found another use of design pattern -
Design pattern lets us design a flexible application by helping us maintaining true polymorphism in our
application
This is so far I am going to go in this article. I will soon write the second part of this article. Until then, enjoy :-) .