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 but can be easily converted to C# just by one or two minor tweaks.
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 with all beginners in design patterns. People and websites who teach this stuff seem to talk in a way that is very hard for 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 Objects Are
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 that object will 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 = escapedName;
}
}
You can think of the class as a blueprint for compilers which describe what data and operations an object will 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 beginners 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 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 uses 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 is 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 known 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 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 this stuff?
Because interfaces are fundamental in object-oriented systems. Objects are known only through their interface
s. 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 interface
s.
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; whereas 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 the implementation of getName
which 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 implementations 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 identical interface
s 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 to 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 .