in reply to Re^2: When to Use Object Oriented approach in Perl? (RFC)
in thread When to Use Object Oriented approach in Perl? (RFC)

The only one of those I am not sure about is the natural hierarchy/inheritance one. The big problem is the LSP and often data that seems to follow a natural hierarchy (a square is a rectangle) in fact violates the LSP.

Hmm, could you elaborate on this? As I understand it, the LSP stipulates that a property that holds for objects of a given type must not fail for objects of a subtype thereof.

So suppose that you've got a class Rectangle, and a subclass Square. What property would be provable about instances of Rectangle that isn't provable about instances of Square? You can prove additional facts about the latter, of course, but I'm not seeing any property that's lost by transitioning from (general) rectangles to squares.

  • Comment on Re^3: When to Use Object Oriented approach in Perl? (RFC)

Replies are listed 'Best First'.
Re^4: When to Use Object Oriented approach in Perl? (RFC)
by Arunbear (Prior) on Sep 06, 2014 at 14:41 UTC
    How about the property that the width and height (of a Rectangle) are independent:
    package Rectangle; use Moo; has height => (is => 'rw'); has width => (is => 'rw'); sub demo { my ($self) = @_; printf "Demo of %s\n", ref $self; $self->show_dimensions; printf "Resetting height => 4, width => 3\n"; $self->height(4); $self->width(3); $self->show_dimensions; printf "area: %d\n", $self->area; $self->show_dimensions; } sub area { my ($self) = @_; $self->{width} * $self->{height}; } sub show_dimensions { my ($self) = @_; printf "height: %s, width: %s\n", $self->{height} || 'undef', $sel +f->{width} || ''; } package Square; use Moo; extends 'Rectangle'; sub area { my ($self) = @_; $self->{height} = $self->{width}; $self->SUPER::area(); } package main; my $rect = Rectangle->new(width => 2, height => 2); my $sq = Square->new(width => 2); $rect->demo; print "\n"; $sq->demo;
    Which outputs:
    Demo of Rectangle height: 2, width: 2 Resetting height => 4, width => 3 height: 4, width: 3 area: 12 height: 4, width: 3 Demo of Square height: undef, width: 2 Resetting height => 4, width => 3 height: 4, width: 3 area: 9 height: 3, width: 3
    The square fails as a stand-in for a rectangle. The is one of the reasons that wise man say Favor Composition Over Inheritance

      A rectangle class might have horizontal_stretch and vertical_stretch methods which stretch a rectangle in one direction. Square is a subclass of rectangle, but if you stretch a square in one direction, it's no longer a square.

      Generalising the issue: mutator methods in the parent class might invalidate constraints imposed by the child class.

      The square—rectangle problem (a.k.a. the circle—ellipse problem) is often seen as a demonstration of why subclassing is evil. I prefer to see it as a demonstration of why mutable objects are evil.

      Here's a perfectly consistent implementation of a Square and Rectangle class using immutable objects and subclassing...

      use Moops; class Rectangle using Moose :ro { has height => (isa => Num, required => true); has width => (isa => Num, required => true); has colour => (isa => Str, default => "black"); method area () { $self->height * $self->width; } method perimeter () { 2 * ($self->height + $self->width); } # Here's a private method; it creates a clone of # the current object, but allows some attributes to # be changed. # method my $_but (%args) { __PACKAGE__->new(%$self, %args); } method paint (Str $colour) { $self->$_but( colour => $colour ); } method horizontal_stretch (Num $factor) { my $new_width = $self->width * $factor; $self->$_but( width => $new_width ); } method vertical_stretch (Num $factor) { my $new_height = $self->height * $factor; $self->$_but( height => $new_height ); } method grow (Num $factor) { $self->horizontal_stretch($factor)->vertical_stretch($factor); } } class Square extends Rectangle using Moose :ro { around BUILDARGS (@args) { my $params = $self->$next(@args); $params->{height} //= $params->{width}; $params->{width} //= $params->{height}; return $params; } method BUILD { confess "Not a square" unless $self->width == $self->height; } } my $square = Square->new(width => 12); print $square->dump; my $painted = $square->paint("red"); print $painted->dump; my $grown = $painted->vertical_stretch(2.5); print $grown->dump;
      /a