Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.objective-c > #244

Re: A question on designated initializers

From "Pascal J. Bourguignon" <pjb@informatimago.com>
Newsgroups comp.lang.objective-c
Subject Re: A question on designated initializers
Date 2015-11-29 03:45 +0100
Organization Informatimago
Message-ID <87fuzpseus.fsf@kuiper.lan.informatimago.com> (permalink)
References (2 earlier) <n2tnq0$gs5$1@news.albasani.net> <87egfhwmsj.fsf@kuiper.lan.informatimago.com> <n3avf4$37g$1@news.albasani.net> <87poyus47x.fsf@kuiper.lan.informatimago.com> <n3dktf$4ks$1@news.albasani.net>

Show all headers | View raw


Jon Rossen <jonr17@comcast.net> writes:

> There was one simple example given that discussed a Rectangle class
> that had a Square sub-class.  Obviously, in Geometry a square is a
> rectangle but a rectangle is not a square.  The programming issue that
> was brought up was basically an outgrowth of this subset/superset
> relationship involving the setters needed to set the dimension of the
> objects.  For rectangles you'd need height and width but for squares
> you really only need one, and furthermore having two for the square
> would not really be correct contextually.  These seem like expected
> inherent differences in these two classes, but I was getting mixed up
> following the article and whether it was suggesting that this
> condition between these two classes were following LSP or 'breaking'
> it.  Basically I got lost trying to fully understand how this example
> was being used.  Perhaps I'll reread that specific section and you as
> Professor Bourguignon can fill me in with answers if I can figure out
> how to craft some specific questions.  :-)

It all depends on the set of invariants and pre-/post- conditions you
choose.

Basically, when  you don't specify them, then you have:

    invariant = true
    all pre-condition = true
    all post-condition = true

and since true => true, anything goes in subclasses.

But also, all bugs are features, of course.

If you have:

    -(int)width;
    -(void)setWidth:(int)aWidth;

then you have nothing.

Ie. you cannot say anything about: ([r setWidth:42],[r width])


If you have:

  @interface Rectangle

    -(int)width;

    -(void)setWidth:(int)aWidth;
       // pre:   h==[self height]
       // post:  aWidth==[self width] && h==[self height]

  @end

and similar for heigth, then you won't be able to have Square with the
invariant [square width]==[square height] as a subclass of Rectangle.


But if you have:

  @interface Rectangle

    -(bool)invariant { return YES; }

    -(int)width;

    -(void)setWidth:(int)aWidth;
       // pre:   true
       // post:  aWidth==[self width]

  @end


then you can have Square as a subclass of Rectangle, with:

  @interface Square

    -(bool)invariant { return [self width]==[self height]; }

    -(void)setWidth:(int)aWidth;
       // pre:   true
       // post:  aWidth==[self width] && aWidth==[self height]

  @end

because:

   true ==> true 
and:
   (aWidth==[self width] && aWidth==[self height]) ==> aWidth==[self width]

therefore the pre-condition of -[Rectangle setWidth:] 
      ==> the pre-condition of -[Square setWidth:] 

and the post-condition of -[Square setWidth:] 
==> the post-condition of -[Rectangle setWidth:] 

and -[Square invariant]
==> -[Rectangle invariant]


so Liskov's substitution principle will let you substitue any Rectangle
instance by a Square instance anywhere.

(Because basically, you are not expecting that [self width]!=[self
height], you are just expecting TRUE (ie. anything)).




Notice that there are two sources of difficulty here:

1- mutation.
2- lack of multiple-inheritance.



Consider the graph:

https://en.wikipedia.org/wiki/Parallelogram#/media/File:Symmetries_of_square.svg

it actually gives an inheriting graph (with the superclasses down),
where some subclasses inherit from multiple superclasses (thus
representing an intersection of the set of values of the superclasses).

Trapezoid        isSubclassOf: IrregularQuadrilateral
IsoceleTrapezoid isSubclassOf: Trapezoid
Parallelogram    isSubclassOf: IrregularQuadrilateral
Kite             isSubclassOf: IrregularQuadrilateral

Rhombus          isSubclassOf: Parallelogram and: Kite
Rectangle        isSubclassOf: Parallelogram and: IsoceleTrapezoid

Square isSubclassOf: Parallelogram and: Rectangle and: Rhombus


If we consider only Square and Rectangle, 
we could have:

  @interface Rectangle
  -(id)initWithWidth:(int)w andHeight:(int)h;  //designated initialiser;
  -(bool)invariant{return [self width]>0 && [self height]>0;}
  -(int)width;
  -(int)height;
  // no setters, no mutation
  @end

  @interface Square
  -(id)initWithSide:(int)side; //designated initialiser;
  -(bool)invariant{return [super invariant] && [self width]==[self height];}
  -(int)width;
  -(int)height;
  // no setters, no mutation
  @end

  @implementation Rectangle
  -(id)clusterInitWithWidth:(int)w andHeight:(int)h{
     if((self=[super init])){
        width=w;
        height=h;
     }
     return self;
  }
  -(id)initWithWidth:(int)w andHeight:(int)h{
     w=max(1,w);
     h=max(1,h);
     if(w==h){
        [self dealloc];
        self=[[Square alloc]initWithSide:w];
     }else{
        self=[self clusterInitWithWidth:w andHeight:h];
     }
     return self;
  }
  @end 

  @implementation Square
  -(id)initWithSide:(int)s
     return [self clusterInitWithWidth:s andHeight:s];
  }
  @end 


So now, when you do: 

   [Rectangle initWithWidth:3 andHeight:3]

you get actually an instance of Square, which is a subclass of
Rectangle, so all the method of Rectangle still apply (thanks to
Liskov's Substitution Principle).

(Notice that in Foundation and AppKit, there are several such instances
of class clusters, where allocating a superclass actually gives you an
instance of a specific subclass)

But since in Objective-C we lack multiple-inheritance, this using
immutable classes goes only so far, since we couldn't make Square also a
subclass of Rhombus and Parallelogram.


Types are often considered as mathematical sets of values.

On the other hand, classes, while having a potential set of instances,
define more a representation than a set of values.

For example, we could have two unrelated classes:

    @interface Rectangle
    -(id)initWithWidth:(int)w andHeight:(int)h;
    -(int)width;
    -(int)height;
    @end

    @interface Square
    -(id)initWithSide:(int)s;
    -(int)side;
    @end


    id r=[[Rectangle alloc]initWithWidth:3 andHeight:3];
    id s=[[Square alloc]initWithSide:3];

What is r?

You'll say it's an instance of Rectangle, 
but it's also the representation of a square of side 3.

Just like s, is an instance of Square, and the representation of the
same square of side 3.

So we have two representations of a square of side 3.

Mathematically, there would be a single type, a single set of a square
of side 3, with this unique element, but in OO, we have defined two
different classes and two different instances representating this same
value.

So one should not confuse types and classes, and the set operations that
are mathematically valid on types, are not directly applicable to
classes and their instances.


Having two unrelated classes for Square and Rectangle can be as
problematic has having one class being a subclass of the other or
vice-versa.  You have to choose what representations are the best for
the problem at hand, by specifying the invariants, pre-/post- conditions
that makes you program simple and clear, and therefore the class
hierarchy that works best for the current problem.

For example, if you have to deal with polygons and their mathematical
properties, it might be better to have a general Polygon class, allowing
for any number of points at any coordinates, and instead of representing
the mathematical sets Square Rectangle, Rhombus, Parallelograms,
Triangle, Hexagon, etc,  you could define:

    @interface Polygon
    -(id)initWithPoints:(NSArray*)points;
    -(bool)invariant{return [point count]>=3;}
    -(bool)isRectangle;
    -(bool)isRhombus;
    -(bool)isParallelogram;
    -(bool)isSquare{return [self isParallelogram]
                        && [self isRectangle]
                        && [self isRhombus];}
    …
    -(float)baseAngle;
    …
    @end

So the definition of Square as the intersection of Rectangle
Parallelogram and Rhombus translates clearly as a conjuction in the
predicate -isSquare.

And this model would allow mutation, since you could easily "move" a
single point of the parallelogram (or add or remove points), and the
predicates would "reclassify" it.

-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk

Back to comp.lang.objective-c | Previous | NextPrevious in thread | Next in thread | Find similar | Unroll thread


Thread

A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-22 13:39 -0800
  Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-22 14:48 -0800
    Re: A question on designated initializers Louis Wu <louiswu@ringworld.net> - 2015-11-22 16:26 -0800
      Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-22 17:24 -0800
        Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-23 02:39 +0100
  Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-23 01:18 +0100
    Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-22 16:49 -0800
      Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-23 02:02 +0100
        Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-27 17:19 -0800
          Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-28 13:22 +0100
            Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-28 17:37 -0800
              Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-29 03:45 +0100
  Re: A question on designated initializers Don Bruder <dakidd@sonic.net> - 2015-11-22 19:15 -0800
    Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-23 18:44 -0800
      Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-24 04:58 +0100
        Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-24 16:42 -0800
          Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-25 02:00 +0100
        Re: A question on designated initializers Greg Parker <gparker@apple.com> - 2015-11-25 01:25 -0800
      Re: A question on designated initializers Don Bruder <dakidd@sonic.net> - 2015-11-24 07:38 -0800
        Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-24 15:54 -0800
          Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-25 01:49 +0100
            Re: A question on designated initializers Don Bruder <dakidd@sonic.net> - 2015-11-24 18:51 -0800
              Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-25 04:41 +0100
                Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-25 00:25 -0800
                Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-25 14:16 +0100
                Re: A question on designated initializers Jon Rossen <jonr17@comcast.net> - 2015-11-25 17:30 -0800
                Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-26 03:03 +0100
                Re: A question on designated initializers Don Bruder <dakidd@sonic.net> - 2015-11-25 09:08 -0800
                Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-11-25 18:41 +0100
                Re: A question on designated initializers Don Bruder <dakidd@sonic.net> - 2015-12-02 14:37 -0800
                Re: A question on designated initializers "Pascal J. Bourguignon" <pjb@informatimago.com> - 2015-12-03 00:58 +0100
          Re: A question on designated initializers Don Bruder <dakidd@sonic.net> - 2015-11-24 18:29 -0800

csiph-web