Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Sander

macrumors 6502a
Apr 24, 2008
521
67
I am actually curious about this as well. I wouldn't fight hard on either side of this argument, but it seems to be that a square is a special case of a rectangle. Whether or not one would need a special square class or not might be questionable, but why is this wrong?

In object orientation, if you derive a class from a base, you say that your derived class "is-a" base. The classic example is that you have a base class "animal" and you can have derived classes "dog" and "cat", perhaps further deriving "German shepherd" from "dog". So, a German shepherd "is-a" dog, and a dog "is-a" animal. Sounds trivial. But what "is-a" implies is that a German shepherd can do everything a generic dog can, but maybe more. So perhaps "dog" has a method "bark" (assuming that all dogs can bark) but the "German shepherd" has a method "herd sheep" which the generic "dog" class hasn't got (assuming German shepherds can herd sheep and not just any dog can). Of course, "animal" has no method "bark" since not all animals can bark.

The crucial part is that you can pass a derived class to a function expecting a base class, since the derived class can do everything the base class can (and perhaps more). So, it's perfectly fine to pass a "German shepherd" to a function taking a "dog" expecting to call its "bark" method, because German shepherds can bark just fine.

Now suppose I have a function taking a "rectangle", expecting that you can set its width and height to the golden ratio. Someone calls you with a "square" (since a square "is-a" rectangle, right?), but a square can't do everything a rectangle can! Namely, you can't set its width and height to different values.

A lot of people learning OO get this wrong (often in other disguises, such as circles and ellipses); I just didn't expect it to be so blatantly in a book.
 

Cromulent

macrumors 604
Oct 2, 2006
6,812
1,100
The Land of Hope and Glory
Does the latest edition of "Programming in C" cover inheritance? It shouldn't since "C" isn't object oriented, but hey you never know...

There is actually a book called "Object Orientated Programming in ANSI C". Very interesting book. It covers how to write object orientated code using standard C. I haven't had a chance to read much of it, but what I have read shows some rather clever uses of pointers, structs and function pointers.
 

lee1210

macrumors 68040
Jan 10, 2005
3,182
3
Dallas, TX
In object orientation, if you derive a class from a base, you say that your derived class "is-a" base. The classic example is that you have a base class "animal" and you can have derived classes "dog" and "cat", perhaps further deriving "German shepherd" from "dog". So, a German shepherd "is-a" dog, and a dog "is-a" animal. Sounds trivial. But what "is-a" implies is that a German shepherd can do everything a generic dog can, but maybe more. So perhaps "dog" has a method "bark" (assuming that all dogs can bark) but the "German shepherd" has a method "herd sheep" which the generic "dog" class hasn't got (assuming German shepherds can herd sheep and not just any dog can). Of course, "animal" has no method "bark" since not all animals can bark.

The crucial part is that you can pass a derived class to a function expecting a base class, since the derived class can do everything the base class can (and perhaps more). So, it's perfectly fine to pass a "German shepherd" to a function taking a "dog" expecting to call its "bark" method, because German shepherds can bark just fine.

Now suppose I have a function taking a "rectangle", expecting that you can set its width and height to the golden ratio. Someone calls you with a "square" (since a square "is-a" rectangle, right?), but a square can't do everything a rectangle can! Namely, you can't set its width and height to different values.

A lot of people learning OO get this wrong (often in other disguises, such as circles and ellipses); I just didn't expect it to be so blatantly in a book.

Interesting. I haven't thought about that specifically...

Though as far as I am concerned, the "is-a" relationship holds. A square is a rectangle. If you're using mutators to set width/height in your rectangle class, it seems that you would override both in your square class and throw something like operation not supported when you try to use them on a square, or override each to set both, etc.

I guess in my opinion you can't change a shape's height/width once you've created/instantiated it, as that would make it a different shape, but that's a totally different discussion.

I definitely see your point here, and it is interesting. I'm sure early on I built something like this incorrectly due to a lack of understanding and blind adherence to a text. I guess I would say that having a square inherit from rectangle isn't wrong in and of itself, but takes special care.

-Lee
 

gnasher729

Suspended
Nov 25, 2005
17,980
5,566
Interesting. I haven't thought about that specifically...

Though as far as I am concerned, the "is-a" relationship holds. A square is a rectangle. If you're using mutators to set width/height in your rectangle class, it seems that you would override both in your square class and throw something like operation not supported when you try to use them on a square, or override each to set both, etc.

I guess in my opinion you can't change a shape's height/width once you've created/instantiated it, as that would make it a different shape, but that's a totally different discussion.

I definitely see your point here, and it is interesting. I'm sure early on I built something like this incorrectly due to a lack of understanding and blind adherence to a text. I guess I would say that having a square inherit from rectangle isn't wrong in and of itself, but takes special care.

In a real-life situation you'd have to step away from the problem and think about it for a while, because using the wrong class design is something that can really make your life very hard.

First, you could ask yourself: Why do I need a class for squares (or circles) at all? A rectangle with equal width and height is a fine square. So why do you need to make a distinction at all?

You might then find that in your user interface, you want to display and manipulate rectangles, but also rectangles with the property that width and height are always the same. Or rectangles where the width/height ratio stays unchanged, or rectangles that have constant area. You could then create a completely new object: One that combines a shape with rules how to change the dimensions. You could then have a method say -adjustShapeWhenMouseMovedFrom: Point pt1 to: Point pt2 .

Or you might have completely different needs that lead to a completely different design.
 

aLoC

macrumors 6502a
Nov 10, 2006
726
0
In a real-life situation you'd have to step away from the problem and think about it for a while, because using the wrong class design is something that can really make your life very hard.

In real life your program probably doesn't have Square and Circle classes, but things like UserSession, ControllerServlet, etc (just making a joke :) )
 

Sander

macrumors 6502a
Apr 24, 2008
521
67
Though as far as I am concerned, the "is-a" relationship holds. A square is a rectangle. If you're using mutators to set width/height in your rectangle class, it seems that you would override both in your square class and throw something like operation not supported when you try to use them on a square, or override each to set both, etc.

You should only throw when something unexpected happens:

Code:
void widen(Rectangle* rect)
{
   double originalArea = [rect area];
   [rect setWidth: 2*[rect width]];
   assert([rect area] == 2*originalArea); // just to be sure
}

If Square inherits from Rectangle, then Square is-a Rectangle, yet this code would assert.

(I'm no Objective-C programmer yet, so this code may not compile, but you get my point.)
 

lazydog

macrumors 6502a
Sep 3, 2005
709
6
Cramlington, UK
I don't think a square inheriting from a rectangle is plain wrong. In the case of 'rectangle' having methods for setting the width and height, ratio of sides etc, then yes, a square subclass is going to cause problems. But if a rectangle is defined by the length of its major axis and area, then a 'square' subclass isn't going to break it. But like others have mentioned, it's all down to the particular app being designed. No absolutes!

b e n
 

Sander

macrumors 6502a
Apr 24, 2008
521
67
In that case, I would expect methods to get/set the major axis, and get/set the area. Since these cannot be set independently for a square, it still "is-not-a" rectangle.

I'm sure we all agree that at the very least this is not the best of examples to use to explain inheritance :)
 

lazydog

macrumors 6502a
Sep 3, 2005
709
6
Cramlington, UK
In that case, I would expect methods to get/set the major axis, and get/set the area. Since these cannot be set independently for a square, it still "is-not-a" rectangle.

I'm sure we all agree that at the very least this is not the best of examples to use to explain inheritance :)

Hi Sander,

Totally agree with you're last comment :cool:

I'm not trying to be awkward or argumentative, but I think the major-axis/area way of doing things works even if it is very contrived. If you're not bored rigid by the subject here's my reasoning:- The major-axis/area model of a rectangle is a strange way of defining a rectangle because the only property of the rectangle that can be guaranteed to be consistent is its area. (changing the area of a rectangle does not guarantee that the major axis will not change in value, and changing the length of the major-axis does not actually guarantee that it's length will be set to what you asked). So in this model, a square sub-class would preserve its area by ignoring any call to change its major axis - entirely consistent with it's superclass.

b e n
 

yeroen

macrumors 6502a
Mar 8, 2007
944
2
Cambridge, MA
Forgive me if this has been said before, but what people often forget in this red herring is that it's all completely dependent on what behaviors (methods, constraints) you define for your rectangle class. In one implementation a square is a rectangle is a quadrilateral is a polygon. In another implementation this chain may be broken.

It may be perfectly sensible that class walrus is class sealingWax is class cabbage is class king.
 

Columbo X

macrumors member
Jun 10, 2007
62
0
UK
It makes sense to define a square as a subclass of a rectangle, since it's a specialisation of the rectangle (with the constraint width = height). Inheriting methods to set different widths and heights from the rectangle base class should not be a problem if they're polymorphic, in other words functions declared as 'virtual' in C++ and in Objective-C all method calls in [] are polymorphic.

What this means is that I make 'square' a subclass of 'rectangle' and provide new versions of the inherited setWidth and setHeight methods for the square class (this is overriding). The setWidth and setHeight methods in the square subclass make sure the width and height of the square stay the same.

When I pass my square subclass to a method that takes a rectangle, any calls to the setWidth or setHeight functions are then passed to the Square version, not the rectangle's version (even though the compiler sees a Rectangle). This is managed at runtime - in Objective-C the runtime engine looks for the correct version of the function based on the object passed in and in C++ the compiler builds a table of function pointers to handle this for you.

It's this sort of feature that solves a lot of design problems but makes doing object orientation difficult in a language like C.
 

aLoC

macrumors 6502a
Nov 10, 2006
726
0
It makes sense to define a square as a subclass of a rectangle, since it's a specialisation of the rectangle (with the constraint width = height).

Are you sure? There's two kinds of reuse you're aiming for with inheritance: reuse of the class you're extending, but also reuse of client code of the old class.

In the case of square extending rectangle, you get the first kind but not the second. As Sander pointed out, a utility method somewhere that took a Rectangle and tried to change it's sides to a certain non 1:1 ratio (as part of some graphical morphing action maybe) would not be reusable with the new subclass.
 

Columbo X

macrumors member
Jun 10, 2007
62
0
UK
Are you sure? There's two kinds of reuse you're aiming for with inheritance: reuse of the class you're extending, but also reuse of client code of the old class.

In the case of square extending rectangle, you get the first kind but not the second. As Sander pointed out, a utility method somewhere that took a Rectangle and tried to change it's sides to a certain non 1:1 ratio (as part of some graphical morphing action maybe) would not be reusable with the new subclass.

Hi aLoC,

Yes - that's a good point and I agree with Sander's point - you should avoid factoring classes where a parent implements methods/attributes that will be irrelevant to a child class. But can I not factor my methods in rectangle so they are meaninful to square also - thus avoiding the obvious design problems Sander points out above?

But can I just check I get you on your example - with regards to your deform function example, are you talking about an inherited method or a function in a client application? I assume you're talking about the latter - you have a utility method in client code to apply a deformation to the object (to set a non 1:1 width:height ratio for example) which is written against the rectangle class.

Generally speaking, if I want to reuse this client method for subclasses of rectangle, polymorphism lets you do this anyway. Each object I pass in (that is a rectangle or a subclass of rectangle) will inherit rectangle's methods (interface), and given the client function is written against this interface, it will be reusable for all subclasses of rectangle. The one requirement of course if that the relevant rectangle methods are virtual (in C++) or the classes are objective-C based.

If you go down the route of making square a subclass of rectangle and I pass in a square object the function may try and change it's width:height ratio so it's no longer 1:1, but the square will ensure it remains constant. I guess it depends on what reuse means in this case. I'd say the function is reusable if it can be called with the subclass and the subclass behaves correctly. For the purposes of the square-rectangle example specifically the deform method in the client application becomes redundant but it could still be called on the square object.

Of course, if the client function relies on explicit knowledge of the internal workings of rectangle then you've lost encapsulation which is not good design.

So as far as I can see it, the client code would also be reusable for subclasses of rectangle.
 

aLoC

macrumors 6502a
Nov 10, 2006
726
0
Here is an example:

// A deliberately ambiguous function name
int calculateKeyForRect(Rectangle r)
{
// Make sure the sides are the ratio we need.
Rectangle r2 = modifyRatio(r, "16:10");

int ans = 10 / ( r2.getLength() - r2.getWidth() );

return ans;
}

This client code worked fine with Rectangle. But once someone made the Square subclass whose setWidth() and setHeight() methods silently set the other to be the same, it now crashes with a division by zero exception.
 

Sander

macrumors 6502a
Apr 24, 2008
521
67
Exactly.

The main misunderstanding is that you can reason "a German shephard is a special case of a dog". This is valid OO: a German shepherd "is-a" dog. But this is a different "special case" than in the statement "a square is a special case of a rectangle". In the latter case, it does not imply "a square is-a rectangle". Because in this case, it adds restrictions.

In some programming languages, iniheritance uses the keyword "extends". There, you would say

class GermanShepherd: extends Dog

in our square/rectangle case, you would expect something more along the lines of

class Square: limits Rectangle

(I'm not aware of any programming languages which support this, and I'm not sure it would make any sense.)
 
Squares and Rectangles don't have to be related by a parent child relationship.

We could have equilateral polygons and non-equilateral polygons (or maybe concave polygons and convex polygons).

It then makes less sense to say, a non-equilateral polygon "is a" equilateral polygon, or vice-versa.

Both have behaviours that are not applicable to the other and common functionality would lie with a common parent.
 

gnasher729

Suspended
Nov 25, 2005
17,980
5,566
It makes sense to define a square as a subclass of a rectangle, since it's a specialisation of the rectangle (with the constraint width = height). Inheriting methods to set different widths and heights from the rectangle base class should not be a problem if they're polymorphic, in other words functions declared as 'virtual' in C++ and in Objective-C all method calls in [] are polymorphic.

What this means is that I make 'square' a subclass of 'rectangle' and provide new versions of the inherited setWidth and setHeight methods for the square class (this is overriding). The setWidth and setHeight methods in the square subclass make sure the width and height of the square stay the same.

When I pass my square subclass to a method that takes a rectangle, any calls to the setWidth or setHeight functions are then passed to the Square version, not the rectangle's version (even though the compiler sees a Rectangle). This is managed at runtime - in Objective-C the runtime engine looks for the correct version of the function based on the object passed in and in C++ the compiler builds a table of function pointers to handle this for you.

It's this sort of feature that solves a lot of design problems but makes doing object orientation difficult in a language like C.

Making a subclass like you suggest will break any code that expects Rects to behave like Rects. If I write a function

void ChangeRectSize (Rect* aRect, int addToWidth, int addToHeight) {
[aRect setWidth: [aRect width] + addToWidth];
[aRect setHeight: [aRect height] + addToHeight];
}

then what happens if you pass a pointer to a Square to this function? Answer: Its width and height will both be increased by (addToWidth + addToHeight). It's broken.
 

lazydog

macrumors 6502a
Sep 3, 2005
709
6
Cramlington, UK
I would guess that most class hierarchies that are modeled on 'real' things can be broken. For example, the superclass 'dog' would be broken if you assumed all dogs bark (I think there is a breed that doesn't bark). So you might be tempted to split dog into 2 further subclasses, ones that bark and ones that don't… and so on. But going down that path would lead to a horribly convoluted class hierarchy to deal with all the other exceptions that dogs have. The real world wasn't designed in an OOP language!

So back to the square/rectangle thing. If you were writing an application that dealt with rectangles, square, circles etc, it might make perfect sense for you to inherit square from rectangle and simply have a method called canChangeAspectRatio() or something like that in the base class (canBark() for class Dog). The other choice is to design the 'perfect' class hierarchy where all your objects fit perfectly in the grand scheme of things and abide by every OOP principle under the Sun. Even if this is possible for your particular application, it may result in a very complicated and convoluted class hierarchy which is very difficult to use, and, more importantly very fragile and easily broken in the future, for example if you need to add a new 'shape' with some characteristic that you hadn't thought about, or perhaps add new properties (such as interior angles).

b e n

(By the way, if somebody was designing a Shape hierarchy, I would suggest thinking about implementing squares as particular instances of Rectangles and supply a factory method in Rectangle for creating squares.)
 

lee1210

macrumors 68040
Jan 10, 2005
3,182
3
Dallas, TX
as interesting as this discussion is, I think it's time to lock this up. If a mod is so inclined the last page or so of posts could be stuck in another thread. The OP's question was answered as best as it might be a while ago.

-Lee
 

Columbo X

macrumors member
Jun 10, 2007
62
0
UK
Thanks aLoC and gnasher729 - I see where you're at now. Sander, I like the extends vs. limits example. The main limitation of inheritance is you can break existing functions / object behaviour by inappropriately overriding parent class functions (which in this example, the hidden width=height operation in square would do).
 

I'm a Mac

macrumors 6502
Original poster
Nov 5, 2007
436
0
The Kochan book has proven to be very useful... It does teach a lot of the basic concepts of programming, and even has a whole chapter on underlying c features- which I haven't gotten to yet.

Correct me If I'm wrong, but I kinda came to the realization that I'm not going to get everything at once- but I should try to understand a concept the best I can and move on... and maybe go back to it later. I understand a good amount of it, but there are a couple things where I'm just like "huh?" like Bit operators.

So for the most part, I got your message (or at least I hope so)- some c knowledge is helpful, but complete c proficiency is not completely necessary, and that programming takes time, and you can't just pick it up in a couple days.
 

deputy_doofy

macrumors 65816
Sep 11, 2002
1,466
410
So why not add a boolean instance variable to the rectangle class - isASquare. It gets set by having the methods that set width and height call yet a third method, which checks the instance variables of height and width. I'd rather deal with a "square" internally than create it as a subclass.
 

Cromulent

macrumors 604
Oct 2, 2006
6,812
1,100
The Land of Hope and Glory
So for the most part, I got your message (or at least I hope so)- some c knowledge is helpful, but complete c proficiency is not completely necessary, and that programming takes time, and you can't just pick it up in a couple days.

Correct. It took me a couple of months before I "got it" then everything came at once. Then from that point on it was learning how to do things and learning the best ways to solve problems.

Learning a programming language is actually the easiest part of learning to program though. It is learning concepts and APIs that is the tricky part.
 

deputy_doofy

macrumors 65816
Sep 11, 2002
1,466
410
Linked lists can be very tricky at first, but once you understand those concepts, they are extremely useful.

The day I realized I could write 2 or more different linked lists but have them point to the same variables in a different order was a BIG "A ha!" moment for me.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.