My previous question was just with class diagram and because of some comments I thought I try to find an example with a little piece of code for the diagram to see if principles correct understood?
class Rectangle {
private int width, height;
public void setHeight(int h) { height = h; }
public void setWidth(int w) { width = w; }
public int area() { return height * width; }
}
class Square extends Rectangle {
public void setHeight(int h) { height = width = h; }
public void setWidth(int w) { width = height = w; }
}
// A test function for rectangles
public void areaTest(Rectange rect) {
rect.setHeight = 20;
rect.setWidth = 10;
int area = rect.area();
assert area == 200;
}
I think first problem is that Liskov Substitution Principle is hurt because square extends rectangle so there is no issue saying a square is a rectangle but you cannot say a rectangle is a square which the code does, so this principle is hurt I think for this reason.
Another hurt principle I believe is Single-Responsibility Principle because in class rectangle, in order to modify the properties of a rectangle for example its area(), you would need to change the entire implementation to achieve this, for the similar reason I believe the Open/Closed Principle is hurt as well because if you want add an extension let's say a draw function to draw the objects (rectangle/square), you cannot do this correctly.
I think other principles are not hurt by this design.
-
1Open Close Principle not violated here because your classes don't have other dependencies. It possibly can be violated in case when rectangle will have Draw method and inside that method you will instantiate "Drawing" objects and draw rectangle. So when you want to draw rectangle differently(add fill colour) you will need make changes into rectangle class. In that case OCP will be violated, and correct solution would be introduce drawing as separated class and pass it to rectangle as a dependency.Fabio– Fabio11/18/2018 18:35:46Commented Nov 18, 2018 at 18:35
-
1Possible duplicate of Why would Square inheriting from Rectangle be problematic if we override the SetWidth and SetHeight methods?gnat– gnat01/09/2019 13:36:19Commented Jan 9, 2019 at 13:36
1 Answer 1
There is no problem with the Single Responsibility Principle. The SRP doesn't say that the class shall do only one thing. It says that it should have only one reason to change. Some will argue that this implies that it should have only one single purpose. So for a shape, setting the dimensions and calculating a couple of shape related properties seems perfectly fine !
There is indeed a problem with the Liskow Subsittution Principle, since setting the height also changes the width. This might break the post-conditions of the setters (depending on how these post-conditions are expressed). And it breaks in any case the history constraint, that says that the properties of the Rectangle should only be changed according to the primitives of the Rectangle interface (i.e. no unexplained change).
Edit: This time, there is no issue with Interface Segregation Principle (thanks to the side effects when changing length or width). Dependency inversion is not relevant here either.
On the Open/CLosed, there's some room for discussion. The private variables should not be used by the extension, since it is an implementation detail of Rectangle
and Square
should not know about it. So it's not so open for extension: if the class would change the way it manages the heigth and the width, the extension would no longer work. So Square
should use only the public interface. To improve the situation you could consider separating the interface of Rectangle from its implementation. But this could be an overkill in such a simple case.
-
Oh alright then really only this Liskow principle is hurt and no others? Thank you for explaining why SRP is unhurt by the way :)tenepolis– tenepolis11/18/2018 17:56:51Commented Nov 18, 2018 at 17:56
-
I don't think Liskov Substitution Principle violated here. You can replace
square
withrectangle
and your application will still work. Of course output will be different, but we use inheritance for the situations where base and derived classes need to have different behaviour. LSP will be violated when you will not be able to userectangle
instead ofsqare
. For example you overridesetHeight
and throw an exception if height is les then 10 in square class.Fabio– Fabio11/18/2018 18:25:27Commented Nov 18, 2018 at 18:25 -
3@Fabio The Square–Rectangle Problem is the classic example to illustrate the LSP. I think you are reading the LSP backwards: it's about whether the Square interface satisfies the Rectangle interface, i.e. whether you can use a Square wherever a Rectangle is expected. This is not the case, e.g. the test case in the question that describes the semantics of a Rectangle fails when given a Square.amon– amon11/18/2018 20:07:30Commented Nov 18, 2018 at 20:07
-
1@Fabio according to Barabar Liskov in this article the history constraint is definitively violated. It's not only about using the subtype, but also about making conclusions about state, invariants, and post-conditions based on the methods invoked. And square creates unexpected changes that are not explained only by calls on a rectangle.Christophe– Christophe11/18/2018 20:20:11Commented Nov 18, 2018 at 20:20
-
1The point within the Square-Rectangle Problem is that the Rectangle base class allows for independent modifications of width and height, and that can't be respected in a Square subclass. Anyway, I'd already question the basic approach of having a single individual Rectangle change its properties after creation. I'd instead advocate Rectangle to be immutable, then the problem vanishes.Ralf Kleberhoff– Ralf Kleberhoff11/18/2018 20:22:55Commented Nov 18, 2018 at 20:22
Explore related questions
See similar questions with these tags.