Agile Software Development# 9.OCP: The Open-Closed Principle
This post is my reading note on Agile Software Development by Robert C. Martin.
9.OCP: The Open–Closed Principle
📌What is Open-Closed Principle?
Open: open for extension
Closed: closed for modification
📌Example of not OCP
Because Client
and Server
classes are concrete.
📌Abstraction is the KEY
1️⃣OCP is NOT one-off[^5]. Because we meet changes, and changes will affect the abstraction for current OCP.
2️⃣There is no abstraction that is natural to all contexts! (still due to changes)
3️⃣Don't over-"abstract" the design(Needless complexity).But, we wait until the changes happen!
4️⃣"Fool me once, shame on you. Fool me twice, shame on me." To keep from loading our software with Needless Complexity, we may permit ourselves to be fooled once.
5️⃣Resisting premature abstraction is as important as abstraction itself.⭐
📌Primary Mechanism behind OCP
Speak without particular programming language
- abstraction
- polymorphism
Speak with particular programming language (e.g. C++)
- inheritance = abstraction + polymorphism
📌STRATEGT pattern on OCP⭐
The preceding is the diagram after applied OCP. One question left, why did we abstract Client
as ClientInterface
rather than AbstractServer
?
Because abstract classes are more closely associated to their clients[^4] than to the classes that implement them.
📌TEMPLATE METHOD pattern on OCP⭐
The policy functions describe some work that needs to be done in terms of some abstract interfaces. e.g. pure virtual functions in C++.
📌Bad Practice violating OCP
xtypedef struct Shape *ShapePointer;
void DrawAllShapes(ShapePointer list[], int n)
{
int i;
for (i=0; i<n; i++)
{
struct Shape* s = list[i];
switch (s->itsType)
{
case square:
DrawSquare((struct Square*)s);
break;
case circle:
DrawCircle((struct Circle*)s);
break;
}
}
}
In the preceding example, the switch-case
block is violating the OCP. In the future, it requires lots of coding when modification comes in, e.g. triangle.
📌Using polymorphism for OCP
Detail code refers to here.
xxxxxxxxxx
class Shape
{
public:
virtual void Draw() const = 0;
};
class Square : public Shape
{
public:
virtual void Draw() const;
};
class Circle : public Shape
{
public:
virtual void Draw() const;
};
void DrawAllShapes(vector<Shape*>& list)
{
vector<Shape*>::iterator i;
for (i=list.begin(); i != list.end(); i++)
(*i)->Draw();
}
In the future, if I were asked to draw triangle, I can easily add a Triangle::Shape
class without touching other codes.