Fine-tuning Abstraction
by Samudra Gupta
In the past two articles about
application design, I explained
three vital design techniques: the
Open-Closed Principle,
Liskov’s Substitution Principle, and the
Dependency Inversion Principle. These principles
address the creation of a more flexible design level capable of
withstanding the growing pressure of scalability.
In discussing those principles
we arrived at design solutions that are based on abstraction.
Thus, one of the salient features of a well-designed system is the abstract
coupling of different objects interacting with each other. A few points
about abstraction remain to be discussed.
Namely:
- To what level should abstraction take place?
- When is the correct level achieved?
I'll try to answer these questions utilizing the Composite Reuse
Principle and the Interface Segregation Principle.
Composite Reuse Principle
The Composite Reuse Principle (CRP) is a source of never-ending
debate amongst designers. The basic idea of this principle is to favor
composition over inheritance as a way of achieving polymorphism.
Polymorphism, in Object Oriented terminology, means that any
particular class can exist as multiple distinct sub-classes (sub-types). The
following is an example of polymorphism:
abstract class Animal {
abstract void talk();
}
Class Dog extends Animal {
public void talk() {
System.out.println("Scooby dooby Doo");
}
}
Class Cat extends Animal {
public void talk() {
System.out.println("Meow....");
}
}
Animal is the super class, and it can exist in the
form of either a Dog or a Cat. When objects exist in the real world,
they exhibit some behavior to the external world. Thus,
the Dog and Cat objects expose the behavior of talking and, as they
talk differently, they are polymorphic.
The above example demonstrates how
polymorphism can easily be achieved with inheritance. However,
inheritance based polymorphism often is ineffective, as an explosion
of subtypes may cause the system to run out of flexibility. Take a
look at the following example:
Imagine that you have been assigned the task of designing a payroll system for
an organization. As Christmas is near, your first job is to make some
provision for employee bonus payments . The
company has three types of employees: Permanent, Temporary
and Part-time.
In your initial design, you have considered each
employee to be a polymorph of the base type Employee. You have also determined
that the bonus for all employees is calculated in a similar manner and
defined the calculateBonus() method in the Employee class. The other
operations, such as leave and insurance premium calculations, are
specific to the type of Employee. Thus
those methods are declared as abstract
in the super class and provided the implementation in the specific Employee
classes. The initial design is shown in Figure 1.
Figure 1: The initial Employee class hierarchy
Later, the project manager changed the rate at which
part-time employee bonuses should be calculated. To accomplish
that, simply override the calculateBonus()
method in the PartTime class.
The manager is impressed. Then he asks that the company
consultants get bonuses, calculated at the same rate as
part-time employees.
- You
could create a new class called Consultant and make it a sub-class of
PartTime to inherit the calculateBonus() implementation. However, this
causes a problem with the class hierarchy, because Consultants are also
Employees. The class hierarchy will always say that Consultants are part time
employees, which they aren't. In addition, if on a future date, the Consultant receives a
Permanent employee bonus, the hierarchy will not withstand the
change.
- Another possibility
would be to override the calculateBonus()
method in the Consultant class, and copy the same implementation of
PartTime class there. However,
duplicate code is not the reusability we
desire.
The original class structure is highly limiting
when new parameters are added. Lets now examine
the following solution and explanation.
To date, we've made a
fundamental assumption that the bonus calculation is a frozen arithmetic function and
pushed it to the super class. In reality, the calculation of bonus can
change algorithms more frequently, and each type of employee can
have a unique bonus calculation algorithm. Inheritance is only applicable in
the context of a generalized relationship where the sub-type is a super-type.
Or, in other words, there is an
ISA relationship. In Liskov’s
Substitution Principle, the main criterion for the ISA
relationship is whether the sub-class exposes the same behavior as the
super-class. Each time one has to override the methods from
the super class, one violates this principle and the
super-class becomes a specialized version of the sub-class rather than a
generalized version of the sub-class. Further, each time
that occurs one runs the risk of having the same problem we are faced
with in the previous example.
The elegant solution is to define an abstract
BonusCalculator and attach the appropriate BonusCalculator instance to each
Employee instance. The following design depicts the proposed solution
(Figure 2):
Figure 2: The CRP
based Employee class hierarchy
The diagram above demonstrates that the BonusCalculator is a composite
of all the different Employee classes, and
thus an extremely flexible polymorphism has been achieved.
The algorithm can be changed at any time for bonus calculation by attaching a different
implementation of the BonusCalculator to any of the Employee
objects.
This is where Composition is
a superior choice.
Inheritance ties you to
a particular implementation or forces you to give up
the original idea of generalization by having to constantly override the super-class
methods in each sub-class. This is, however, not exactly a limitation of
inheritance but more of an issue with how the inheritance is applied. As you gain
experience, you will find that utilizing CRP is a safer approach.
Samudra Gupta has six years of Java related application development
experience. He has been involved in various research based projects
in Java including e-commerece based and application
design and development projects. He is based in the United Kingdom.
In his freetime, he is a columnist in different Java Magazines and
Journals and loves to play contract bridge.
New on the Java Boutique:
New Review:
Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling
API boasts simplicity, ease-of-integration, a well-rounded feature
set, and it's free!
New Applet:
Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA
sequences into three useful formats.
Elsewhere on internet.com:
WebDeveloper Java
Lots of Java information on webdeveloper.com
WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.
ScriptSearch Java
Hundreds of free Java code files to download.
jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.
|