Question on C++ Class Inheritance

Started by
76 comments, last by arbitus 12 years, 6 months ago

However, under the general definition of a geometric rectangle, the width and height are independent dimensions.


Height and width aren't even part of the geometric definition of a rectangle, and NOWHERE in the definition does it say they are independent. I have no idea where you are pulling that from. The square is the simple counter case to saying that a rectangle's width and height don't have to be independent.
Advertisement

You're operating under a different description of the contract.


That's part of my point, that the contract made illustrating that squares and rectangles violate LSP isn't the contract made by the geometrical definitions of the shapes.

Height and width aren't even part of the geometric definition of a rectangle, and NOWHERE in the definition does it say they are independent. I have no idea where you are pulling that from.


Height and width are geometric properties of a Rectangle. While it is true that you do not need to use Height and Width to define a rectangle, all rectangles will have a defined Height and Width which can be derived from any data that is used to construct the Rectangle. It is simplest to talk about in terms of Width and Height. But no matter what attributes or properties you use to define your Rectangle class, the same postconditions will apply.

The square is the simple counter case to saying that a rectangle's width and height don't have to be independent.[/quote]

Just because two things are independent does not mean they cannot be equal. This is a logically incorrect statement. Only when you are talking about a Square do you talk about dependence of dimensionality, thus the Square has altered the postconditions of the properties that define a Rectangle.

Just because two things are independent does not mean they cannot be equal. This is a logically incorrect statement. Only when you are talking about a Square do you talk about dependence of dimensionality, thus the Square has altered the postconditions of the properties that define a Rectangle.


No it hasn't. Where in the definition does it say anything about the length of the sides being independent from one another? If we're just adding post conditions to stuff we can just run around any inherited object with a mutator function longer than one line and say it doesn't follow the LSP, but that's clearly not the case.

All you need to do is just point out where in the definition of a rectangle it says that height and width are independent and your case is made. I don't see why that's so difficult for you to do if what you say is indeed the case.

Using all wikipedia definitions for plane, line, quadrilateral, and rectangle the definition is, "A flat shape consisting of vertices joined by straight lines to form a circuit with four edges and four vertices with four right angles."

By all means try to construct a definition for a rectangle that says the lengths of the perpendicular sides must be independent from one another; not can be or is often, but must.

No it hasn't. Where in the definition does it say anything about the length of the sides being independent from one another? If we're just adding post conditions to stuff we can just run around any inherited object with a mutator function longer than one line and say it doesn't follow the LSP, but that's clearly not the case.

All you need to do is just point out where in the definition of a rectangle it says that height and width are independent and your case is made. I don't see why that's so difficult for you to do if what you say is indeed the case.

Using all wikipedia definitions for plane, line, quadrilateral, and rectangle the definition is, "A flat shape consisting of vertices joined by straight lines to form a circuit with four edges and four vertices with four right angles."

By all means try to construct a definition for a rectangle that says the lengths of the perpendicular sides must be independent from one another; not can be or is often, but must.


Geometrically, a rectangle must have a Width and Height (by virtue of being a geometric object), and this is traditionally chosen as the simplest way to implement a Rectangle class, which is why the examples presented in this thread use it. So let's assume that the Rectangle class uses Width and Height to express the geometric representation of the rectangle. In order for this class to appropriately represent ANY rectangle (which you would agree is a requirement of a Rectangle class?), it must be able to hold Width and Height values that are different, correct?

OK, now let us assume that this class (for whatever reason) has mutators that allow the user to set the Width and the Height of the Rectangle. In order for the instance to continue to be geometrically capable of expressing ANY rectangle, the user must have the capability of independently altering the values of the Width and the Height. Would we not agree that a mutable instance of the Rectangle class (not subclass/subtype/specialization) should be mutable into any other possible rectangle? Of course an instance of a more specific class will have more specific constraints and would behave differently, but we are specifically talking about a specific instance of a concrete Rectangle class. If you were to read this code:


Rectangle rect = new Rectangle();
rect.setWidth(5);
rect.setHeight(4);
var area = rect.getWidth() * rect.getHeight();
Console.WriteLine("Area = " + area);


What would you expect the output of this code be? What should the output of this code be?
In order for this Rectangle instance to be able to express any rectangle, the Width and Height properties must be independently mutable (granted you would also need a transform component if you want to also include spatial information such as position and rotation). This is basically a tautology.

The reason you will not find this in definition of "rectangle" on Wikipedia is because this is a consequence of implementation via mutability. I could have stopped at implementation. We are no longer defining "a rectangle" we are implementing a class that describes a rectangle, and thus the derivative features and consequences of the rules that define a rectangle become our responsibility. Independent mutability of Width and Height in this case are a consequence of the defining qualities of a rectangle. They are inseparable from the concept of a mutable rectangle that is described by height and width. This is why all of the formulas that describe the definable features of a rectangle require the use of length and width. The concept of length and width are inseparable from the geometric rectangle, and their independence is needed to maintain the ability to express ANY rectangle using a single description.

Because we have made the decision that our class should be allowed to mutate its state/data via externally accessible mutators, we now have to consider the pre- and post- conditions of these mutators. Generally in mathematics, something such as "a rectangle" is treated as an immutable object. Transformations result in a new rectangle, rather than changing the "instance" of the existing rectangle. We don't have to discuss the independence of the Width and Height we just assume that they never change. However, for whatever reason, we have chosen mutability and with mutability comes great responsibility! (hah) In order for our Rectangle class to provide correct, predictable and reliable results, its setWidth() method must pass the aforementioned unit test. If it does not pass that unit test, then it is expressing behavior more specific than that of a generic rectangle or it is simply geometrically incorrect. Note that I am specifically referring to the implementation of the setWidth() method on the concrete Rectangle class, not any of its subclasses.

This same principle applies no matter how (what data, 4 points? 4 lines? Center + extents?) you intend to express/represent your mutable Rectangle class. I really, really get what you are saying. Special cases of the geometric rectangle have different rules concerning mutability behaviors, and the idea that a Rectangle class enforces mutability in a different manner is why we are having this conversation and why this is chosen as the poster example of LSP violations. This is why almost any hierarchy that allows mutability will likely violate LSP in some way.

Geometrically, a rectangle must have a Width and Height (by virtue of being a geometric object), and this is traditionally chosen as the simplest way to implement a Rectangle class, which is why the examples presented in this thread use it. So let's assume that the Rectangle class uses Width and Height to express the geometric representation of the rectangle. In order for this class to appropriately represent ANY rectangle (which you would agree is a requirement of a Rectangle class?), it must be able to hold Width and Height values that are different, correct?

That is totally begging the question. "Let's assume that they are independent. Therefore they are independent."

A rectangle can have it's height and width changed independently, but it can also have it's height and width changed dependent on each other and it will still be a rectangle. Changing them independently is not a property of a rectangle. The properties are that it is a shape with four edges connecting 4 vertices in a circuit with 4 right angles. You can derive some properties from this, none of which is that the width and height MUST BE independent from each other.


The reason you will not find this in definition of "rectangle" on Wikipedia is because this is a consequence of implementation via mutability. I could have stopped at implementation. We are no longer defining "a rectangle" we are implementing a class that describes a rectangle, and thus the derivative features and consequences of the rules that define a rectangle become our responsibility. Independent mutability of Width and Height in this case are a consequence of the defining qualities of a rectangle. [/quote]
You can very easily implement a rectangle class that describes all rectangles, including squares, and can be subclassed by squares, AND won't violate the LSP.

The difference being that one is consistent with geometric definitions of shapes and one assumes properties of the rectangle to be true that aren't actually true because it's easier to visualize and barely anybody needs to subclass a square anyway.
I like taquitos

That is totally begging the question. "Let's assume that they are independent. Therefore they are independent."

A rectangle can have it's height and width changed independently, but it can also have it's height and width changed dependent on each other and it will still be a rectangle.


Right, but if you have two mutators, setWidth() and setHeight(), and both mutators affect the other dimension as a function of the mutated dimension, your Rectangle instance is no longer able to represent ANY rectangle. We are not talking in abstractions here, we are talking about concrete details, concrete classes, and implementations. How would you implement a Rectangle class that has mutable Width and Height dimensions that are both independent and dependent at the same time?


Changing them independently is not a property of a rectangle. The properties are that it is a shape with four edges connecting 4 vertices in a circuit with 4 right angles. You can derive some properties from this, none of which is that the width and height MUST BE independent from each other.
[/quote]

In order for you to mathematically describe ANY rectangle through the use of width and height, they must be independent (ie, not a function of each other) quantities.



You can very easily implement a rectangle class that describes all rectangles, including squares, and can be subclassed by squares, AND won't violate the LSP.
[/quote]

Yes, you can. It is called immutability. Or it can be done through the use of abstract classes/interfaces and only allowing mutable data to exist within the concrete implementations of all of the various flavors of specific rectangle. It is also possible if you create a Rectangle class that does not behave as a generic rectangle. But if you have a counter example, by all means, give it to us! We have been giving you concrete example after concrete example, I am not sure why you have waited until now to actually offer something substantial.

I like taquitos


Normally I do not sperg out like this, I have no idea why this topic has touched a nerve. I need sleep.

Right, but if you have two mutators, setWidth() and setHeight(), and both mutators affect the other dimension as a function of the mutated dimension, your Rectangle instance is no longer able to represent ANY rectangle.

It doesn't affect both in the rectangle class. Only in the square class. Rectangle still describes ALL rectangles, even squares, and square still describes ALL squares.

In order for you to mathematically describe ANY rectangle through the use of width and height, they must be independent (ie, not a function of each other) quantities.[/quote]
For most rectangles they are independent. For squares they are not. That is why squares are a subset of rectangles, and the dependence of width/height is defined differently for both.


Yes, you can. It is called immutability.
[/quote]
What I've been describing this entire time is a mutable rectangle with a mutable square subclass that does not break LSP. Have you been reading?

This topic is closed to new replies.

Advertisement