2. Object Oriented Programming
“Am e tho d o f pro g ram m ing base d o n a hie rarchy
o f classe s, and we ll-de fine d and co o pe rating
o bje cts. ”
3. Object Oriented Design
It’s a process of planning a software system
where objects will interact with each other to
solve specific problems.
The saying goes, "Proper Object oriented
design makes a developer's life easy, whereas
bad design makes it a disaster."
4. How does anyone start?
When anyone starts creating software design,
their intentions are good. They try to use their
existing experience to create an elegant and
clean design.
5. Where it heads?
Over time software starts to rot. With every
feature request or change software design
alters its shape and eventually the simplest
changes to application requires a lot of effort
and, more importantly, creates higher chances
for more bugs.
6. Who is to Blame?
Software solves real life business problems and since
business processes keep evolving, software keeps on
changing.
Changes are an integral part of the software world. Obviously
because clients are paying they will demand for what they are
expecting. So we cannot blame "change" for the degradation
of software design. It is our design which is at fault.
One of the biggest reasons for the damaging of software
design is the introduction of unplanned dependencies into
the system. Every part of the system is dependant on some
other part and so changing one part will affect another part. If
we are able to manage those dependencies we will easily
maintain the software system and software quality too.
7. How comes to rescue?
Principles, Design Patterns and Software architecture
Software architecture like MVC, 3-Tier, MVP tells us how
overall projects are going to be structured.
Design pattern allows us to reuse the experience or rather,
provides reusable solutions to commonly occurring problems.
Example – an object creation problem, an instance
management problem, etc.
Principles tell us, do these and you will achieve this. How you
will do it is up to you. Everyone defines some principles in
their lives like, "I never lie" or "I never drink …." etc. He/she
follow these principles to make his/her life easy, but how will
he/she stick to these principles is up to the individual.
9. SOLID - What is SOLID?
For those who haven't heard about it, SOLIDis
acronym composed from principles names.
(There are 5 of them.)
SOLID doesn't have any particular meaning.
It's just a good way to remember list of
principles.
These principles are about low-level design
and partly interpretation of OOPbasics.
Introduced by Robert C. Martins
10. SOLID
SOLID
- Single Responsibility Principle
- Open Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inverison Principle
Code becomes more Te stable (remember TDD is not only about
testing, more important its about Design)
Apply ’smart’
- don’t do stuff ’just because of’
- very important to see the context of the program/code when
applying SOLID
It’s said (Wikipedia) when all five principles are applied together
intend to make it more likely that a programmer will create a system
that is easy to maintain and extend over time. Let’s talk about every
principle in detail
11. Single Responsibility Principle
"The re sho uld ne ve r be m o re than o ne re aso n
fo r a class to chang e . " — Ro be rt Martin, SRP
pape r linke d fro m The Principle s o f O O D
Or
"A class should have one and only one
responsibility"
12. SRP - Real world comparison
He works as a team leader for one of the software firm. In his spare
time he do some writing, newspaper editing and other various
projects. Basically, he has multiple responsibilities in his life.
When something bad happens at her work place, like when his
boss scolds him for some mistake, he get distracted from his other
work.
Basically, if one thing goes bad, everything will mess up.
13. SRP - Problem in Programming
Two (Employee DB Operations and Report Generation)
different resposibilities
public class Employee {
public string EmployeeName { get; set; }
public int EmployeeNo { get; set; }
public void Insert(Employee e) { //Database Logic written here }
public Employee Select() { //Database Logic written here }
public void GenerateReport(Employee e) { //Set report formatting }
}
Every time insert logic changes, this class will change.
Every time report format changes, this class will changes.
Every time one gets changed there is a chance that the other
also gets changed because both are staying in the same
home and both have same parent. So a single change leads
to double testing (or maybe more).
14. Solutions which will not Violate
SRP
Now it’s up to us how we achieve this. One thing we can do is create three different
classes
Employee – Contains Properties (Data)
EmployeeDB – Does database operations
EmplyeeReport – Does report related tasks
public class Employee {
public string EmployeeName { get; set; }
public int EmployeeNo { get; set; }
}
public class EmployeeDB {
public void Insert(Employee e) { //Database Logic written here }
public Employee Select() { //Database Logic written here }
}
public class EmployeeReport {
public void GenerateReport(Employee e) { //Set report formatting }
}
15. Solutions which will not Violate
SRP
Note: This principle also applies to methods. Every method should
have a single responsibility.
Can a single class can have multiple methods?
The answer is YES. Now you might ask how it’s possible that
A class will have single responsibility.
A method will have single responsibility.
A class may have more than one method.
Well the answer for this question is simple. It’s context. Here,
responsibility is related to the context in which we are speaking.
When we say class responsibility it will be somewhat at higher level.
For instance, the EmployeeDB class will be responsible for
employee operations related to the Database whereas
the EmployeeReport class will be responsible for employee
operations related to reports.
When it comes to methods it will be at lower level. For instance look
at following example:
16. Solutions which will not Violate
SRP
//Me tho d with m ultiple re spo nsibilitie s – vio lating
SRP
public void Insert(Employee e) {
string StrConnectionString = "";
SqlConnection objCon = new
SqlConnection(StrConnectionString);
SqlParameter[] SomeParameters=null;//Create Parameter array
from values SqlCommand objCommand = new
SqlCommand("InertQuery", objCon);
objCommand.Parameters.AddRange(SomeParameters);
ObjCommand.ExecuteNonQuery();
}
17. Solutions which will not Violate
SRP
//Method with single responsibility – follow SRP
public void Insert(Employee e) {
SqlConnection objCon = GetConnection();
SqlParameter[] SomeParameters=GetParameters();
SqlCommand ObjCommand = GetCommand(objCon,"InertQuery",SomeParameters);
ObjCommand.ExecuteNonQuery();
}
private SqlCommand GetCommand(SqlConnection objCon, string InsertQuery, SqlParameter[]
SomeParameters) {
SqlCommand objCommand = new SqlCommand(InsertQuery, objCon);
objCommand.Parameters.AddRange(SomeParameters); r
eturn objCommand;
}
private SqlParameter[] GetParaeters() {
//Create Paramter array from values
}
private SqlConnection GetConnection() {
string StrConnectionString = "";
return new SqlConnection(StrConnectionString);
}
18. Open Closed Principle
"So ftware e ntitie s (classe s, m o dule s, functio ns, e tc. ) sho uld
be o pe n fo r e xte nsio n, but clo se d fo r m o dificatio n. " — Ro be rt
Martin, O CP pape r linke d fro m The Principle s o f O O D
Or, “Change a class’s behavior using inheritance and
composition”
At the most basic level, that means, you should be able to
extend a class's behavior without modifying it. It's just like I
should be able to put on a dress without doing any change to
my body,
So in OOD, open for extensions means that the behavior of
the module/class can be extended and we can make the
module behave in new and different ways as the
requirements change, or to meet the needs of new
applications.
19. OCP - Real World Comparison
Let’s assume you want to add one more floor between the first and second
floor in your two floor house. Do you think it is possible? Yes it is, but is it
feasible? Here are some options:
One thing you could have done at time you were building the house first time was
make it with three floors, keeping second floor empty. Then utilize the second
floor anytime you want. I don’t know how feasible that is, but it is one solution.
Break the current second floor and build two new floors, which is not sensible.
20. OCP - Identify Problem in
Programming
Let's say the Select method in the EmployeeDB class is used
by two clients/screens. One is made for normal employees,
one is made for managers, and the Manager Screen needs a
change in the method.
If I make changes in the Select method to satisfy the new
requirement, other UI will also be affected. Plus making
changes in existing tested solution may result in unexpected
errors.
21. Solution which will not violate
OCP
We will derive a new class called EmployeeManagerDB from
EmployeeDB and override the Select method as per the new
requirement.
public class EmployeeDB {
public virtual Employee Select() {
//O ld Se le ct Me tho d
}
}
public class EmployeeManagerDB extends EmployeeDB {
public override Employee Select() {
//Se le ct m e tho d as pe r Manag e r //UIre q uire m e nt
}
}
22. Example 2 – Violating Open-Close
Principle
// Open-Close Principle - Bad example
class GraphicEditor {
public void drawShape(Shape s) {
if (s.m_type==1)
drawRectangle(s);
else if (s.m_type==2)
drawCircle(s);
}
public void drawCircle(Circle r) {....}
public void drawRectangle(Rectangle r) {....}
}
class Shape {
int m_type;
}
class Rectangle extends Shape {
Rectangle() {
super.m_type=1;
}
}
class Circle extends Shape {
Circle() {
super.m_type=2;
}
}
23. Open Closed Principle
Few Problems
Impossible to add a new Shape without modifying
GraphEditor
Important to understand GraphEditor to add a
new Shape
Tight coupling between GraphEditor and
Shape
Difficult to test a specific Shape without involving
GraphEditor
If-Else-/Case should be avoided
24. Open Closed Principle
Solution
// Open-Close Principle - Good example
class GraphicEditor {
public void drawShape(Shape s) {
s.draw();
}
}
class Shape {
abstract void draw();
}
class Rectangle extends Shape {
public void draw() {
// draw the rectangle
}
}
25. When should we apply OCP
If we look back our previous example, where did we go wrong? Clearly
even our first implementation wasn’t open for extension. Should it have
been? I’d say that it all depends on context. If we had had very strong
suspicions that {business} would ask us to support other shapes later on we
could probably have prepared for that from word go. However, often it’s not
a good idea to try to anticipate changes in requirements ahead of time and
preparing for future changes which can easily lead to overly complex
designs. Instead, we focus on writing code that is well written enough so
that it’s easy to change if the requirements change.
Once the requirements do change though it’s quite likely that they will
change in a similar way again later on. That is, if {business}asks us to
support another type of shape it’s quite likely that {business} soon will ask
for support for a third type of shape.
So, in other words, we should have put some effort into abiding by the
open/closed principle once the requirements started changing. Before that,
in most cases, I would suggest limiting your efforts to ensuring that the
code is well written enough so that it’s easy to refactor if the requirements
starts changing.
26. Liskov Substitution Principle
"Functio ns that use po inte rs o r re fe re nce s to
base classe s m ust be able to use o bje cts o f
de rive d classe s witho ut kno wing it. " — Ro be rt
Martin, LSP pape r linke d fro m The Principle s
o f O O D
Mean, subclasses should behave nicely when
used in place of their base class
27. LSP - Real World Comparison
A father is a real estate business man whereas his son wants to be
cricketer.
A son can’t replace his father in spite of the fact that they belong to
same family hierarchy.
In basic Object Oriented Principles, "Inheritance" is usually
described as an "is a" relationship. If a "Developer" is a
"SoftwareProfessional", then the "Developer" class should inherit
the "SoftwareProfessional" class. Such "Is a" relationships are very
important in class designs, but it's easy to get carried away and end
up in a wrong design with a bad inheritance.
The "Liskov's Substitution Principle" is just a way of ensuring that
inheritance is used correctly.
28. LSP – Problem in programming
We have an interface defined like
Public interface IPersistedResources{
void load();
void persist();
}
Interface that represents resources that can be loaded in memory, and persisted afterwards in case
there were changes to it.
Let’s pretend we have the following implementations of that interface
1)
Public class ApplicationSetting implemets IPersistedResources{
void load(){
//load application settings
}
void persist(){
//persist application settings
}
}
29. LSP – Problem in programming
2)
Public class UserSetting implemets IPersistedResources{
void load(){
//load User settings
}
void persist(){
//persist User settings
}
}
30. LSP – Problem in programming
Somewhere in the application we have some way to retrieve a list of instances of
implementations of that interface, kind of like this:
Static List<IPersistedResources> loadAll(){
List<IPersistedResources> resources = new ArrayList<>(){
new ApplicationSeetting();
new UserSetting();
}
for(IPersistedResources resource: resources){
resource.load();
}
Return resources;
}
Static saveAll(List<IPersistedResources> resources ){
for(IPersistedResources resource: resources){
resource.save();
}
}
And somewhere else we may use those methods
31. LSP – Problem in programming
Everything works great, until a new class is added to the system in
order to handle, let’s say, some “special settings”:
3)
Public class SpecialSetting implemets IPersistedResources{
void load(){
//load User settings
}
void persist(){
throw new NotImplementedException();
}
}
It looks like the Load method does whatever stuff it’s supposed to
do in order to handle loading these special settings. The Persist
method, on the other hand, throws a NotImplementedException. As
it turns out, those settings are meant to be read-only, therefore, the
Persist method can’t really do anything.
32. LSP – Problem in programming
The system is told to load the new class along with the other ones that implement that same
interface
Static List<IPersistedResources> loadAll(){
List<IPersistedResources> resources = new ArrayList<>(){
new ApplicationSeetting();
new UserSetting();
new SpecialSetting();
}
for(IPersistedResources resource: resources){
resource.load();
}
Return resources;
}
Now when we run the app everything should still work fine, until we hit the code that tries
to persist all of those loaded resources, at which point we get a big and fat
“NotImplementedException”.
One (horrible) way to address this would be to change the SaveAll method:
33. LSP – Problem in programming
Static saveAll(List<IPersistedResources> resources ){
for(IPersistedResources resource: resources){
if(resource instanceOf SpecialSetting){
//Do nothing
}else{
resource.save();
}
}
}
If the specific resource being processed is of type SpecialSettings, we skip that one. Brilliant! Well,
maybe not. Let’s look back at a simplified definition of the Liskov Substitution Principle:
“An o bje ct sho uld be substitutable by its base class (o r inte rface ). ”
Looking at the SaveAll method it should be clear that “SpecialSettings” is NOT substitutable by its
“IPersistedResource” interface; if we call Persist on it, the app blows up, so we need change the
method to take that one problem into consideration. One could say “we ll, le t’s chang e the Pe rsist
m e tho d o n that class so it wo n’t thro w an e xce ptio n anym o re ”. Hmm, having a method on a class
that when called won’t do what its name implies is just bad… really, really bad.
Write this down: anytime you see code that takes in some sort of baseclass or interface and then
performs a check such as “if (someObject is SomeType)”, there’s a very good chance that that’s an
LSP violation. I’ve done that, and I know so have you, let’s be honest.
34. LSP - Solution
The fix here is to tailor the interface based on what each client needs (Interface Segregation Principle, or ISP).
The LoadAll method (which is one client of those classes) is really only concerned about the “Load” capability,
whereas the “SaveAll” method (another client) is only concerned about the “Persist” capability. In other words,
these is what those clients need:
Static List<ILoadResources> loadAll(){
List<ILoadResources> resources = new ArrayList<>(){
new ApplicationSeetting();
new UserSetting();
new SpecialSetting();
}
for(ILoadResources resource: resources){
resource.load();
}
Return resources;
}
Static saveAll(List<ISaveResources> resources ){
for(ISaveResources resource: resources){
resource.save();
}
}
35. LSP - Solution
The SaveAll takes in something tailored to its needs, IPe rsistRe so urce ’s, and the same goes for
LoadAll, which only cares about ILo adRe so urce ’s (in the real app, the actual instantiation of
these classes happen somewhere else). This is what the granular new interfaces look like:
Public interface IPersistResources{
void persist();
}
Public interface ILoadResources{
void load();
}
public class TestCalss {
public static void main(String[] args) {
List<ILoadResource> loadResources = new ArrayList<>();
loadResources.add(new UserSetting());
loadResources.add(new SpecialSetting());
LoadAll(loadResources); // Call to load resource
List<IPersistResource> persistResources = new ArrayList<>();
persistResources.add(new UserSetting());
persistResources.add((IPersistResource) new SpecialSetting());
SaveAll(persistResources);
}
}
36. LSP - Conclusion
Yes, it’s pretty much the former “IPersistedResource” split up
into two separate interfaces, tailored to their client needs.
Both the UserSettings and ApplicationSettings classes can
implement these two interfaces, whereas the SpecialSettings
class would only implement ILo adRe so urce ; this way, it isn’t
forced to implement interface members it can’t handle.
Very often people ask what’s the most appropriate number of
members in an interface. In the real world example I gave
here, the original interface had only 2 members; one could
say that was small enough, but as it turns out, it wasn’t. The
IPersistedResource interface was doing too much (both
loading *and* persisting stuff) based on the clients that use its
implementers. In the end, two interfaces with a single method
on them fit the bill a lot better. Inte rface s with sing le
re spo nsibility? Yes, Single Responsibility Principle (SRP); as
with design patterns, sometimes SOLID principles go hand in
hand together.
37. Interface Segregation Principle
"Clie nts sho uld no t be fo rce d to de pe nd upo n
inte rface s that the y do no t use . " — Ro be rt
Martin, ISP pape r linke d fro m The Principle s o f
O O D
Keep interfaces small
38. ISP – Real World Comparison
Let’s say you purchase a new desktop PC. You will find a couple of USB
ports, some serial ports, a VGA port etc. If you open the cabinet you will
see lots of slots on the motherboard used for connecting various parts with
each other, mostly used by hardware engineers at the time of assembly.
Those internal slots will not be visible until you open the cabinet. In short,
only the required interfaces are made available/visible to you. Imagine a
situation where everything was external or internal. Then there is a greater
chances of hardware failure (as if life wasn't hard enough for computer
users).
39. ISP – Real World Comparison
Let’s say we will go to a shop to buy
something (let’s say, for instance, to buy a
cricket bat).
Now imagine a situation where the
shopkeeper starts showing you the ball and
stumps as well. It may be possible that we will
get confused and may end up buying
something we did not require. We may even
forget why we were there in the first place.
40. ISP - Problem in Programming
Let’s say we want to develop a Report
Management System. Now, the very first task is
creating a business layer which will be used by
three different UIs.
EmployeeUI – Show reports related to currently
logged in employee
ManagerUI – Show reports related to himself and
the team for which he/manager belongs.
AdminUI – Show reports related to individual
employee ,related to team and related to company
like profit report.
41. ISP - Problem in Programming
Suppose we have a report generation service in our application as follow,
public interface IReportService {
void generatePFReport();
void generateESICReport();
void generateResourcePerformanceReport();
void generateProjectSchedule();
void generateProfitReport();
}
public class ReportSericeImpl implements IReportServvice {
public void generatePFReport() {//}
public void generateESICReport() {// }
public void generateResourcePerformanceReport() {//}
public void generateProjectSchedule() {//}
public void generateProfitReport() {// }
}
42. ISP - Problem in Programming
public class EmployeeUI {
public void DisplayUI() {
IReportSerice objBal = new ReportServiceImpl();
objBal.generateESICReport();
objBal.generatePFReport();
}
}
public class ManagerUI {
public void DisplayUI() {
IReportSerice objBal = new ReportServiceImpl();
objBal.generateESICReport();
objBal.generatePFReport();
objBal.generateResourcePerformanceReport ();
objBal.generateProjectSchedule ();
}
}
43. ISP - Problem in Programming
public class AdminUI {
public void DisplayUI() { I
IReportSerice objBal = new ReportServiceImpl();
objBal.generateESICReport();
objBal.generatePFReport();
objBal.generateResourcePerformanceReport();
objBal.generateProjectSchedule();
objBal.generateProfitReport();
}
}
44. ISP - What is the problem?
The developer who is working
on EmployeeUI gets access to all the other
methods as well, which may unnecessarily
cause him/her confusion.
45. ISP - Solution
//Segregating the report service in to three specific report services
public interface IEmployeeReportService {
void GeneratePFReport();
void GenerateESICReport();
}
public interface IManagerReportService extends IEmployeeReportService {
void GenerateResourcePerformanceReport();
void GenerateProjectSchedule();
}
public interface IAdminReportService: IManagerReportService {
void GenerateProfitReport();
}
By following ISP we can make client see, what he is required to see.
46. Interface Segregation Principle
“Each collaboration of a class which violates
SRP should be articulated through separate
interface”
Don’t force classes so implement methods
they can’t (Swing/Java)
Don’t pollute interfaces with a lot of methods
Avoid ’fat’ interfaces
47. Dependency Inversion Principle
"A. Hig h le ve lm o dule s sho uld no t de pe nd upo n lo w le ve l
m o dule s. Bo th sho uld de pe nd upo n abstractio ns.
B. Abstractio ns sho uld no t de pe nd upo n de tails. De tails
sho uld de pe nd upo n abstractio ns. " — Ro be rt Martin, DIP
pape r linke d fro m The Principle s o f O O D
Translation: Use interfaces and abstractions
Motivation is that if high-level code depends on low-level
code, you'll have to recompile, retest whole project every
time low-level code changes. There is an implication that low-
level code changes often. For example, it's totally ok to
depend on java.lang.String class since its interface won't be
broken in next JDK. But if you're in progress of writing your
own library for communication with hardware (for example),
it's better not to depend on its low-level details.
48. DIP: Real World Comparison
Would you solder the electrical lamp directly to wiring in wall? I think NO.
Let’s talk about our desktop computers.
Different parts such as RAM, a hard disk, and CD-ROM (etc.) are loosely connected
to the motherboard. That means that, if, in future in any part stops working it can
easily be replaced with a new one.
Imagine a situation where all parts were tightly coupled to each other, which means it
would not be possible to remove any part from the motherboard. Then in that case if
the RAM stops working we have to buy new motherboard which is going to be very
expensive.
49. DIP - Problem in Programming
Look at following code,
public class CustomerService implements ICutomerService {
public void Insert(Customer c) {
try { //Insert logic }
catch (Exception e) {
FileLogger f = new FileLogger();
f.LogError(e);
}
}
}
public class FileLogger {
public void LogError(Exception e) { //Log Error in a physical file }
}
In the above code CustomerBAL is directly dependent on the FileLogger class which
will log exceptions in physical file. Now let’s assume tomorrow we decides to log
exceptions in the Event Viewer. Now what? Change existing code. Oh no! that might
create a new error!
50. DIP : Solution
Create Logger Interface:
public interface ILogger {
void LogError(Exception e);
}
//Log Error in a physical file
public class FileLogger implements ILogger {
public void LogError(Exception e) { // }
}
//Log Error in a physical file
public class EventViewerLogger implements ILogger {
public void LogError(Exception e) { //}
}
51. Time to yes thanks
So now we've covered all five principles of
SOLID.
Thanks to Ro be rt Martin.
52. Is it end?
NO, but we look into them in subsequent
sessions. Following are other design principles
which play vital role in software/application
design:
Program to Interface and abstraction
Don't Repeat Yourself
Encapsulate What Varies
Hollywood Principle
Apply Design Pattern wherever possible
Strive for Loosely Coupled System
Keep it Simple and Sweet / Stupid
53. Conclusion
We can’t avoid changes. The only thing we can do is develop and
design software in such a way that it is able to handle such
changes.
SRP should be kept in mind while creating any class, method or
any other module (which even applies to SQL stored procedures
and functions). It makes code more readable, robust, and testable.
We can’t follow DIP each and every time, sometimes we have to
depend on concrete classes. The only thing we have to do is
understand the system, requirements and environment properly
and find areas where DIP should be followed.
Following DIP and SRP will opens a door to implement OCP as
well.
Make sure to create specific interfaces so that complexities and
confusions will be kept away from end developers, and thus, the
ISP will not get violated.
While using inheritance take care of LSP.
Editor's Notes
Testing is advantageous in and of itself, but that the code has become readable is an additional advantage. The more the code is readable the simpler it seems