2. An object consists of –
data
procedures/operations/methods that operate on that data
An object performs an operation when it receives a request
from a client.
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.
3. Encapsulation ensures that the data that an object contains
cannot be accessed directly from outside that object, and its
representation is invisible from outside the object.
Encapsulation is a great way to develop loosely coupled
software components.
That‟s all I have to say about encapsulation (really, how
many times we have read and re-read it ?! But if someone
still has some problem with it, speak up), for now.
4. 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.
5. What do you know, it does!!!!!!
There is a pattern called Facade which describes how to
represent complete subsystems as objects.
There is also a pattern called Flyweight that describes how
to support huge number of objects at the finest granularities.
There are also some other patterns available to help us.
6. 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.
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).
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.
7. 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.
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 (huh, what did you just
say?!?!).
Often we speak of a subtype inheriting the interface of its
supertype.
8. 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.
The run-time association of a request to an object and one of its
operations is known as Dynamic Binding.
9. 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.
A question: Why are we learning this?
Ans.: Because Polymorphism depends on it!!!!!!!
10. 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 can
enable some other class to display polymorphic behaviors.
So what are those POLYMORPHISM stuff that we have
learned till now??? I wonder………..
11. New classes can be defined in terms of existing
classes using Inheritance. When a Subclass inherits
from a Parent Class, it includes the definitions of all the
data and operations that the parent class defines.
Objects that are instances of the subclass will contain
all data defined by the subclass and its parent
classes, and they‟ll be able to perform all operations
defined by this subclass and its parents.
Let„s call this type of inheritance as Class Inheritance
(“nooo, we would like to hear the reason….”) for now.
12. An abstract class is one whose main purpose is to define a
common interface for its subclasses. They are generally
used to define a Base Type to be used in class hierarchy.
Abstract classes will defer some or all of its implementation
to operations defined in subclasses; hence an abstract class
cannot be instantiated.
The operations that an abstract class declares but doesn‟t
implement are called Abstract Operations.
Classes that aren‟t abstract (can be instantiated) are called
Concrete Classes.
13. It‟s really, really important to understand the above
distinction.
An object‟s class defines how the object is
implemented. The class defines the object‟s internal
State and the implementation of its operations.
In contract, an object‟s type only refers to its interface –
the set of requests to which it can respond. An object
can have many types, and objects of different classes
can have the same type.
14. No, they don‟t mean the same thing (which is obvious
now, isn‟t it), but they DO have a close relationship.
Because a class defines the operations an object can
perform, it also defines the object‟s type.
When we say that an object is an instance of a
class, we imply that the object supports the interface
defined by the class (beware of the conventional
Interface…..).
15. Class inheritance defines an object‟s implementation in
terms of another object‟s implementation. In short, it‟s
simply a mechanism for code and representation
sharing.
In contrast, interface inheritance (sometimes called
Subtyping) describes when an object can be used in
place of another (remember Dynamic Binding?)
What do you think? Which one is preferable ?
16. Class inheritance is basically just a mechanism for
extending an application‟s functionality by reusing
functionality in parent classes. It lets you define a new
kind of object rapidly in terms of an old one. It lets you
get new implementations almost for free, inhering most
of what you need from existing classes.
However, implementation reuse is only half the story.
Inheritance‟s ability to define families of objects with
identical interfaces is also important.
Why? Because Dynamic Binding, and in
turn, Polymorphism depends on it (“Huh? How?”).
17. When inheritance is used carefully (some will say
properly), all classes derived from an abstract class will
share its interface. This implies that a subclass merely
adds or overrides operations and does not hide
operations of the parent class.
Consequently, all subclasses can then respond to the
requests in the interface of this abstract class, making
them all subtypes of the abstract class.
As a result, all of these subclasses can be switched
with one another in run-time, resulting in polymorphic
behaviors.
18. There are two major benefits to manipulating objects solely
in terms of the interface defined by abstract classes –
1. Clients remain unaware of the specific types of objects they use, as
long as the objects adhere to the interface that clients expect.
2. Clients remain unaware of the classes that implement these objects.
Clients only know about the abstract classes defining the interface.
(Just think about the implications of the above two points)
This so greatly reduces implementation dependencies
between subsystems that it leads to the following principle of
Reusable Object-Oriented design.
19. Program to an Interface, not to an
Implementation
What does it mean? It means don’t declare variables to be
instances of particular concrete classes.
Instead, commit only to an interface defined by an abstract
class.
20. There are two different techniques that are used to reuse
functionality –
1. Class Inheritance
2. Object Composition
As we have learned before, class inheritance lets us define
new implementation of one class in terms on another‟s.
Reuse by Subclassing in this way is often referred to as
White-box reuse, because with this type of inheritance, the
internals of parent classes are often visible to subclasses.
21. Object composition is an alternative to class
inheritance. Here, new functionalities is obtained by
assembling or composing objects to get more complex
functionality. It requires that the objects being
composed have well-defined interfaces.
This style of reuse is called Black-box reuse, because
no internal details of objects are visible. Objects appear
only as black boxes.
What do you think? Which one is better?
22. Class inheritance is defined statically at compile-time
and is straightforward to use, since it is supported
directly by most of the OO languages.
Class inheritance also makes it easier to modify the
implementation being reused. When a subclass
overrides some but not all of the operations, it can
affect the operations it inherits as well, assuming the
call the overridden operations.
23. First, you cannot change the implementations inherited
from parent classes at run-time, because inheritance is
defined at compile-time, resulting in causing difficulties
for polymorphism.
Second, parent classes often define at least part of
their subclasses‟ physical representation. Because
inheritance exposes a subclass to details of its parent‟s
implementation, it‟s often said that Inheritance Breaks
Encapsulation.
24. Yes, it may sound scary, but it‟s true. The
implementation of a subclass becomes so bound up
with the implementation of its parent class that any
change in the parent‟s implementation will force the
subclass to change. This is sometimes called as
Implementation Dependencies.
Implementation dependencies can cause problems
when you‟re trying to reuse a subclass. Should any
aspect of the inherited implementation not be
appropriate for new problem domains, the parent class
must be rewritten or replaced by something more
appropriate.
25. This dependency limits flexibility and ultimately
reusability.
One cure for this is to inherit from abstract
classes, since they usually provide little or no
implementation.
26. Object composition is defined dynamically at run-time
through objects acquiring references to other
objects, thus being more Polymorphism-friendly.
It requires objects to respect each others‟
interfaces, which in turn requires carefully designed
interfaces that don‟t stop you from using one object
with many others.
But there is a pay-off. Because objects are accessed
solely through their interfaces, we don‟t break
encapsulation. Any object can be replaced at run-time
by another as long as it has the same type.
27. Moreover, because an object‟s implementation will be
written in terms of object interfaces, there are substantially
fewer implementation dependencies.
Favoring object composition over class inheritance helps
you keep each class encapsulated and focused on one task.
Your classes and class hierarchies will remain small and will
be less likely to grow into unmanageable monsters.
28. On the other hand, a design based on object composition
will have more objects ( if fewer classes), and the system‟s
behavior will depend on their interrelationships instead of
being defined in one class.
From the above discussion, we can see that object
composition has very, very few bad effects compared to the
class inheritance. This gives rise to our second OOP
Principle.
29. Favor Object Composition over Class
Inheritance.
Ideally, you shouldn‟t have to create new components to achieve reuse. You should
be able to get all the functionalities you need just by assembling existing
components through object composition. But this is rarely the case, because the
set of available components is never quite rich enough in practice. Reuse by
inheritance makes it easier to make new components that can be composed with
old ones. Inheritance and object composition thus work together.
Nevertheless, statistics say that designers overuse inheritance as a reuse
technique, and designs are made more reusable and simpler by depending more
on object composition.
30. Delegation is a way of making composition as powerful
for reuse as inheritance.
In delegation, two objects are involved in handling a
request –
A receiver object
A delegate object
When an object receives a request, it sends the
request to a delegate object which will process it and
return the result. This is analogous to subclasses
deferring requests to parent classes.
31. The receiver object maintain a reference to the delegated
object to delegate the request. When delegating a
request, the receiver passes a reference to itself(remember
the this pointer?) to the delegated object if the delegated
object needs a reference to the receiver object.
Example-
A Window class
A Rectangle class
A Circle class
Delegation is an extreme example of object
composition.
32. Code reuse
Easy to compose behaviors at run-time and to change
the way they are composed.
i.e., the Window can become circular at run-time by simply replacing its Rectangle instance with a
Circle instance.
33. Dynamic, highly parameterized software is harder to
understand than more static software.
There are also run-time inefficiencies, but the human
inefficiencies are more important in the long run.
Delegation is a good design choice only when it
simplifies more than it complicates.
It works best when used in highly stylized ways – that
is, in standard patterns.
34. Another technique for reusing functionality is through
parameterized types, also known as generics.
This technique lets you define a type without specifying
all the other types it uses. The unspecified types are
supplied as parameters at the point of use.
Example –
List class for all types.
Parameterized types aren‟t needed at all for languages
that doesn‟t have compile-time type checking.
35. So far, we have learned three different ways to put
reuse mechanism at work –
Inheritance
Object Composition
Parameterized Types
Inheritance provides default implementations for
operations and lets subclasses override them.
Parameterized types let you change the types that a
class can use.
36. Neither inheritance nor parameterized types can
change at run-time.
Object composition allows the behavior to be
composed at run-time, but it also requires indirection
and can be less efficient.
37. There are significant differences between run-time and
compile-time code structure of an application.
The code structure is frozen at compile-time; it consists
of classes in fixed inheritance.
A program‟s run-time structure consists of rapidly
changing networks of communicating objects.
38. Let us consider the distinction between object Aggregation
and Acquaintance and how differently they manifest
themselves at compile- and run-times.
Aggregation implies that one object owns or is responsible
for another object. Generally we speak of an object having
or being part of another object. Aggregation implies that an
aggregate object and its owner have identical lifetimes.
Acquaintance implies that an object merely knows of
another object. Sometimes it is called Association or Using
relationship between objects. They may request operations
of each other, but they aren‟t responsible for each other.
39. Acquaintance and aggregation are determined more by
intent than by explicit language mechanism. The distinction
may be hard to see in the compile-time structure, but it‟s
significant.
Why? Because aggregation relationships tend to be fewer
and more permanent than acquaintance. Acquaintances, in
contrast, are made and remade more frequently, sometimes
existing only for the duration of an operation. Acquaintances
are more dynamic as well, making them more difficult to
discern in the source code.
40. With such disparity between a program‟s run-time and compile-
time structure, it‟s clear that code won‟t reveal everything about
how a system will work.
The system‟s run-time structure must be imposed more by the
designer than the language.
The relationships between objects and their types must be
designated with great care, because they determine how good or
bad the run-time structure is.
Many design patterns capture the distinction between compile-
time and run-time structures explicitly
(Composite, Decorator, Observer, Chain of Responsibility etc.).
41. It all started with a simple Duck Simulator. The code
that the architect used was –
Duck – quack(), swim(), display()
MallardDuck – display()
RedHeadDuck – display()
Everything was going well, the ducks were
swimming, quacking perfectly.
42. Now the management wants the ducks to fly. So the
architect added the following method into the supertype
–
Duck – fly()
The designer then thought, “I am Awesome…..”.
But then, one day, he got a call………….
43. In a demonstration, all of the ducks were flying
nicely, even the rubber ducks, which shouldn‟t have
been the case.
The SMART designer thought, “Ok, I could always
override the fly() method in the RubberDuck class to do
nothing”.
But then what will happen if the management wants to
include WOODEN DECOY DUCKS into the
application? They aren‟t supposed to fly or quack…..
44. “I could declare two interfaces called Flyable and
Quackable, then let all the instances implement them.
This way I won‟t have to override those two methods in
every duck class that gets added in the future….”.
I say, this choice creates a new danger to solve an old
one. It completely destroys the code reuse mechanism
for those two functionalities and creates a new
maintenance nightmare (There is a reason we are
calling him capitalized SMART, isn‟t there ?).
So, let the OOP principles rescue him in this situation.
45. So the inheritance didn‟t work out very well, because
the Duck behavior keeps changing across the
subclasses, and it‟s not appropriate for all subclasses
to have those behaviors.
Interfaces didn‟t work out well also because any
change will cause each of the implementations to
change, producing newer bugs along the way.
In this situation, let us think of an OOP principle.
46. The principle says –
Identify the aspects of the application that vary and
separate them from what stays the same.
In other words, take the parts that vary and encapsulate
them, so that later you can alter or extend the parts that vary
without affecting those that don‟t.
As simple it may sound, it forms the basis for almost every
design patterns!!!!!!!! All patterns provide a way to let some
part of a system vary independently of all other parts.
47. So, what should we separate from what? Apparently, the following
two should be separated –
Fly Behavior
Quack Behavior
We are going to separate those methods from the Duck class and
create a new set of classes to represent each Duck Behavior.
To determine what should be separated from what, and what may
cause a change, you will need a good knowledge of the Problem
Domain. That‟s why the Object Oriented System Analysis and
Design has a section called Requirement Analysis. Do it
well, otherwise you will also become capitalized SMART (though it
isn‟t always possible/feasible….)!!!!!!
48. First, we would like to keep things flexible, after all
flexibility was the main issue previously. We might want
to instantiate a specific Duck object and initialize with
specific types of behaviors.
While we are there, why not make sure that we can
change the behavior of a duck dynamically (e.g., at
run-time). Keeping this goal in mind, let us review our
first OOP principle –
Program to an Interface, not to an
implementation.
49. We will use an interface to represent each duck
behavior, and each implementation of a Behavior will
implement one of those interfaces.
So this time it won‟t be the Duck classes that will
implement the flying and quacking interfaces. Instead
we‟ll make a set of classes whose entire reason for
living is to represent a behavior, and it‟s the behavior
class, rather than the Duck class, that will implement
the behavior interface.
50. Public interface Flyable // a strategy (compositor)
{
public void fly();
}
public interface Quackable
{
public void quack();
}
51. Public class FlyWithWings implements Flyable // a ConcreteStrategy
{
public void fly()
{
System.out.Println(“Fly, fly little Starling…..”);
}
}
Public class Quack implements Quackable
{
public void quack()
{
System.out.Println(“Quack, quack, quaaaaaackkk….”);
}
}
52. Public class FlyNoWay implements Flyable
{
public void fly()
{
// do nothing (for rubber and wooden duck)
}
}
Public class MuteQuack implements Quackable
{
public void quack()
{
// do nothing (for wooden duck)
}
}
53. Public abstract class Duck // the Context (Composition)
{
Flyable flyBehavior;
Quackable quackBehavior;
public Duck() {}
public abstract void display();
public void performFly()
{
flyBehavior.fly();
}
public void performQuack()
{
quackBehavior.quack();
}
public void swim()
{
System.out..println ( “ I am swimming!!!! “ );
}
}
54. Public class MallardDuck extends Duck
{
public MallardDuck()
{
quackBehavior = new Quack(); // Wait a sec…..
flyBahavior = new FlyWithWings() // ?!?!?!
}
public void display()
{
// I am a mallard duck
}
}
55. Public class MiniDuckSimulator
{
public static void main(String[] args)
{
Duck mallard = new MallardDuck();
mallard.performQuack();
mallard.performFly();
}
}
56. Add these two methods to the Duck class -
public void setFlyBehavior(Flyable fb)
{
flyBehavior = fb;
}
public void setQuackBehavior(Quackable qb)
{
quackBehavior = qb;
}
57. So, just catch a breathe and think about what you can
do with all these……
We have seen an example of our second OOP
principle also –
Favor Composition over Inheritance
58. So, instead of thinking the duck behaviors as a set of
behaviors, we are thinking of them as a family of
algorithms, each one of them performing a specific task in
various way. This IS the strategy pattern. In BOOKISH term
–
The Strategy Pattern defines a family of
algorithms, encapsulates each one of them, and makes them
interchangeable. Strategy lets the algorithm vary
independently from clients that use it.
It is also known as Policy Pattern.
59. Many related classes differ only in their behaviors.
Strategies provide a way to configure a class with one of
many behaviors.
You need different variants of an algorithm. Strategies can
be used when these variants are implemented as a class
hierarchy of algorithms.
An algorithm uses data that clients (objects that requested it
to do something) shouldn‟t know about. Use this pattern to
avoid exposing complex, algorithm-specific data structures.
60. A class defines many behaviors, and these appear as
multiple conditional statements in its operations. Instead of
many conditionals, move related conditional branches into
their own strategy class.
61. Family of related algorithms.
An alternative to subclassing. We could have subclassed
the Context class directly to give it different behaviors, but
that would have hard-wired the behaviors into Context. It
mixes the algorithm implementation with Context‟s, making
Context harder to understand, maintain and extend.
Elimination of conditional statements.
i.e., void quack(string quack_type){ switch(quack_type){…}}
A choice of implementations.
62. Clients must be aware of different strategies. Clients might
be exposed to implementation issues. Therefore it should be
used only when the variation in behavior is relevant to client.
Communication overhead between Strategy and Context.
Tighter coupling may be needed in this case.
Increased number of objects. Sometimes it may be reduces
by implementing strategies as stateless objects (objects with
no data) that contexts can share.
63. Defining the Strategy and Context interfaces. There are two
ways to facilitate communication between them –
1. Context passes data to Strategy as needed
2. Context passes itself to Strategy
Making strategy objects optional. This has the advantage
that clients don‟t have to deal with Strategy objects at all
unless the don‟t like the default behavior.
64. The famous GoF book
Head First Design Patterns
Wikipedia
Stackoverflow