Meng Weng Wong on Thu, 3 Oct 2002 15:17:48 -0400


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Stumbling Towards OOP


On Thu, Oct 03, 2002 at 01:21:16PM -0400, Hans Dieter Pearcey wrote:
| > I'm tempted to suggest that you should bring your mom to the talk, but
| > I'll be out of town and I wouldn't want to miss that.
| 
| In the absence of a suggestion from you, I make the same suggestion,
| because I *will* be in town and I don't want to miss the chance :)

well, actually, i'm taking her to the airport at roughly the time we're
supposed to meet... but let me make up for my cancellation by providing
the idea behind the talk.

The Problem: it's difficult for a beginning programmer to appreciate the
OOP model.  "What's the point?  Everything's so complicated."  He spends
a long time grubbing about with inefficient representations.

Have you guys ever seen the cartoon --- I think it was from The Far Side
--- which has a title of "Evolution" and shows a series of footprints in
order, something like

    an ape's footprint
    a human footprint
    a man's business shoe
    a woman's high heeled pump

The Solution: we need a road map of how programmers evolve toward OOP.
Beginners could locate themselves on the map and learn from others'
mistakes rather than having to repeat them.

So, in the spirit of mjd's teflon pipe thread tape talk, I present:

----------------------------------------------------------------------
			 STUMBLING TOWARDS OOP
			      with aliens

Stage 1: you have an alien.  just one.

    $name = "Spock";
    $race = "Vulcan";
    $ears = "pointy";

    print "$name is a $race and he has $ears ears.";

  It's the most natural way to start.

----------------------------------------------------------------------
Stage 2: your program evolves.  you have two thingies, or more.

    $spock_race = "Vulcan";
    $spock_ears = "pointy";

    $quark_race = "Ferengi";
    $quark_ears = "huge and hairy";

    print "Spock is a $spock_race and he has $spock_ears ears.";
    print "Quark is a $quark_race and he has $quark_ears ears.";

  A logical outgrowth of stage 1.  Beginning to be messy.

----------------------------------------------------------------------
Stage 3: you read man perlref and think, "aha!  soft references."

    $race_for_spock = "Vulcan";  $race_for_quark = "Ferengi";       
    $ears_for_spock = "pointy";	 $ears_for_quark = "huge and hairy";

    foreach my $guy ("spock", "quark") {
      print qq(\u$guy is ${"race_for_$guy"} and he has ${"ears_for_$guy"} ears.);
    }

  Yuck, beginner code!
  Do you remember writing stuff like that?
  I do!  :)

----------------------------------------------------------------------
Stage 4: someone tells you "don't use soft references!"  You discover anonymous arrays.

    $alien_1 = ["Spock", "Vulcan",  "pointy"];
    $alien_2 = ["Quark", "Ferengi", "huge and hairy"];

    print "$alien_1->[0] is a $alien_1->[1] and has $alien_1->[2] ears.";
    print "$alien_2->[0] is a $alien_2->[1] and has $alien_2->[2] ears.";

  You later realize you can loop through them.

    for ($alien_1, $alien_2) {
      print "$_->[0] is a $_->[1] and has $_->[2] ears.";
    }

  But for each alien you have to make up a whole new set of variables.  Hmm.

    $alien_1 = ...;
    $alien_2 = ...;
    $alien_3 = ...;        # bad.
    $alien_4 = ...;    
    $alien_5 = ...;

  A classic red flag.  There has to be a better way.

----------------------------------------------------------------------
Stage 5: you read perldsc and it gives you some strange ideas.

    @aliens = ( ["Spock", "Vulcan",  "pointy"],
		["Quark", "Ferengi", "huge and hairy"],
	      );

    for (@aliens) {
      print "$_->[0] is a $_->[1] and has $_->[2] ears.";
    }

  At least we're not wasting variables now --- just punctuation.

----------------------------------------------------------------------
Stage 6: you read perlreftut and learn about hashes.

    %aliens = ( Spock => [ "Vulcan",  "pointy" ],
                Quark => [ "Ferengi", "huge and hairy" ],
              );

    while (my ($name, $attrs) = each %aliens) {
      my ($race, $ears) = @$attrs;
      print "$name is a $race and has $ears ears.";
    }

  But keeping track of the ordering is a pain, because when you add a
  new attribute, such as "homeworld", you have to update all the places
  that dereference $attrs.

----------------------------------------------------------------------
Stage 7: during a late-night hackathon, around 4am, you realize:

    %aliens = ( Spock => { race => "Vulcan",  ears => "pointy", },
		Quark => { race => "Ferengi", ears => "huge and hairy", },
              );

  Now the attribute thingies are inside the data, and you don't have to
  keep track of the ordering!

    while (my ($name, $attrs) = each $aliens) {
      while (my ($attr, $value) = each %$attrs) {
        print "$name\'s $attr = $value";
      }
    }

  Unfortunately, you can't say "race is Vulcan" and "ears are pointy"
  because of the is/are problem.  We'll come back to this later.

----------------------------------------------------------------------
Stage 8: hey, let's use that trick for the name also!

    @aliens = ( { name => "Spock", race => "Vulcan",  ears => "pointy", },
		{ name => "Quark", race => "Ferengi", ears => "huge and hairy", },
	      );

    foreach my $alien (@aliens) {
      print "$alien->{'name'}\'s race is a $alien->{'race'} and his ears are $alien->{'ears'}";
    }

  We're getting real close to being OOP now.

----------------------------------------------------------------------
Stage 9: the big step: blessing.

  Each thingy is already being represented by a hash of attributes.
  Bless 'em.

    package Alien;
    sub new {
      my $class = shift;
      return bless { @_ }, $class;
    }

    @aliens = ( Alien->new(name=>"Spock", race=>"Vulcan",  ears=>"pointy"),
	        Alien->new(name=>"Quark", race=>"Ferengi", ears=>"hairy"),
              );

  The display code is exactly the same as in Stage 7.

    foreach my $alien (@aliens) {
      print "$alien->{'name'}\'s race is a $alien->{'race'} and his ears are $alien->{'ears'}";
    }

  We have now officially crossed the OOP threshhold.

----------------------------------------------------------------------
Stage 10: accessors

    package Alien;
    sub name { my $alien = shift; return "My name is  $alien->{'name'}."; }
    sub race { my $alien = shift; return "I am a $alien->{'race'}."; }
    sub ears { my $alien = shift; return "My ears are $alien->{'ears'}."; }

  This solves the grammar problem we saw in Stage 6.

    foreach my $alien (@aliens) {
      print join "  ", $alien->name, $alien->race, $alien->ears;
    }

----------------------------------------------------------------------
Stage 11: subclassing

  This lets us put the "race" and "ears" attributes into the class.

    package Alien::Vulcan;
    sub new {
      my $class = shift;
      return bless { race => "Vulcan", ears => "pointy", @_ }, $class;
    }

    package Alien::Ferengi;
    sub new {
      my $class = shift;
      return bless { race => "Ferengi", ears => "huge and hairy", @_ }, $class;
    }

    @aliens = ( Alien::Vulcan ->new(name=>"Spock"),
	        Alien::Ferengi->new(name=>"Quark"),
              );

----------------------------------------------------------------------
Stage 12: ... and beyond!

  I'm sure you guys can think of where things can go from here.
  Any suggestions?

**Majordomo list services provided by PANIX <URL:http://www.panix.com>**
**To Unsubscribe, send "unsubscribe phl" to majordomo@lists.pm.org**