This is a simple example, but it reflects a tension between SOLID principles that I often find myself struggling with.
A popular example of the Open/Closed Principle (e.g. [1], [2]) imagines that you have many Shape classes, and a drawShape()
method, e.g.:
// 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) {....}
}
Using the Open/Closed Principle (OCP), we observe that this code would need to be modified for every new shape we add; we want to close the need for modification, and allow extension as a better way to add new behavior.
The examples then offer the following as an OCP-compliant 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
}
}
As a demonstration of OCP in the abstract, this is a fine example. However, this solution seems to me a blatant violation of the Single-Responsibility Principle (SRP). We started with the "drawing responsibility" separated into its own class, and solved our OCP problem by lumping that "drawing responsibility" back in with the simple definition of a shape.
For example, if drawing involves interaction with a GUI, this example couples the definition of a simple shape together with GUI operations! That seems wrong.
What would be a good redesign here that would conform to OCP and to SRP?