http://qs321.pair.com?node_id=176224

I wasn't an OO proponent until recently. Perhaps I'm not really even a good one now. I did my fair share of OO classes when I was studying at university and I knew the concepts pretty well. Both of my major team projects involved writing in Visual C++ (oh! the horror!) and I was pretty o'fay with classes and objects and methods and all. I knew how to use objects but I was hazy on their design. What makes an object? The programming I have had to do doesn't map so well to the simple examples of "person" classes, or "trucks" with "fire trucks", "tip trucks" and such inheriting from the "truck" class.

Sure, my 3rd and 4th year projects didn't immediately map into objects either. It might have been a useful learning exercise if I'd been on the design teams, but I would have slowed things down. Taking a requirements specification and turning out a list of objects and how they'll interact was magic so far as I could tell.

I didn't learn Perl OO until I had to look for work. It had always seemed too hard before then. It took me 30 minutes to read all the relevant stuff from Damian's book. It looked easy. It was.

After doing some simple introductory projects at my new job, I was set loose on "The Project". Well coded... well mostly well coded but it had 1 object and about 1000 functions full of if/else statements. If my type is a subnet do this. Else if my type is a host do that. Else if my type is a domain do something else...

Even I could see that there should be either a heap more specialized functions or a lot more specific objects. So I asked if we could change it, and of course was told "no". It worked as it was. So I "maintained" The Project and secretly started to modularize it a bit better. Then another developer, slightly higher in the hierarchy than I, found that a feature request was impossible to provide in the system as it stood. We started to write more specific objects (subnet, host, domain etc), all inheriting from the initial monster... Hooray!

After working on this project I suddenly understood both Perl OO and general OOD much better. I got a good feel for what kind of functions should be generic and which should be specific. I started thinking more in terms of OO. I started proposing OO solutions to things, even here because they seemed to be better solutions than otherwise.

Then BUU asked a question and I found myself responding "Do consider OO where it might be useful". But what does that mean? If someone had said that to me before I worked on The Project I doubt I would have had a good idea of where OO would be useful at all.

Computer Science classes had taught me the platitudes:

  • Use OO for data encapsulation
  • Use OO whenever you have things that belong together
  • Use OO to provide a consistent interface even when the underlying implementation changes
but these never really helped me. In a CGI project what's a "useful" use of OO? The answer is, it depends.

So I begin to think that it might be helpful to look at WHY I thought an OO solution to vaevictus's problem was a good idea. Firstly it seemed obvious to me that a solution involving an array of hashes would work. So I created one to play with. The requested push function took my array and did stuff to it, likewise my pop function.

Then I looked at it again and thought, this function is very specific to this data structure. Nothing else could be passed to this function and have a sensible result. It seemed that it would be nice to save the programmer from assuming that these just replaced push and pop. And I was passing the array around, how could I stop the programmer from messing up it's order and then wondering why the pop function didn't give the next event? Could I make it so this code could be useful to other programs too?

It seemed like this event handling was a really good thing to modularize. When a lot of OO Perl programmers think of modules they immediately think of objects. Not me. I usually think of libraries. Then that second last concern up there started niggling at me. How could I hide the internals of the event queue from the programmer? OO of course. I'd even stopped thinking of it as "an array" and started thinking of it as "an event queue". It wanted to be an object. So it was.

Of course the use of a single object in a Perl project doesn't automatically require that the rest of your code be broken up around objects. Many of us work with CGI in an OO sense every day yet write largely imperative scripts. Findy Services and B. Jacobs don't have it all wrong when they criticize OO. It's not a magical solution. But it can be very, very useful.

So here are my guidelines for indications that you might want to use objects, even in otherwise imperative programs.

  • Create an object when you have a number of functions that all work exclusively on the same data type and would return errors or meaningless results on anything else.
  • Create an object when your data type must not have its internals messed about with by the object user.
  • Create an object when it's easier to think of a collection of data as a unit instead of thinking of its structure.
  • Create a number of objects when you find yourself writing many different subroutines with the same basic purpose to work on slightly different data (eg. read_data1, read_data2, read_data3, process_data1, process_data2, process_data3), or huge subroutines with big cascades of if-then-elses. Polymorphism works great here.

What other suggestions do people have?

jarich

Replies are listed 'Best First'.
(Revelation) Re: Where/When is OO useful?
by Revelation (Deacon) on Jun 21, 2002 at 06:57 UTC
    Using OOP:
    Personally, I find object oriented programming to be most useful when coding for abstraction, and structure. Object oriented programming is a much more serious commitment, that programming in general, because I find myself more committed to revision, optimization, and incite into my data. Packages that focus on abstraction, and being useful in many situations seem to be easier to write, as well.

    Object oriented program seems to go hand in hand with modeling the input data, something that I don't find myself doing most of the time. This goes hand in had with better algorithms, increased efficiency in SQL queries, and consistent data parsed. All this leads to well structured, clean code; usable in the future, and soapy clean.

    Data modeling makes us look at the most important factor in our code, the initial data sent to the script. OOP stresses an underlying structure that can deal with this data methodically, and with an inherited structure, forcing a more thorough data analysis, reducing development time, and ensuring more accurate coding. Part of data modeling is 'data hiding,' preventing classes from accessing data meant for their counterparts, allowing for greater security.

    OOP allows me to abstract subroutines, optimize quicker, and revise in smaller chunks. Programming for data manipulation, instead of functionality brings structure, and detail into the code earlier in the game. Structure has always been a great virtue in code, and OOP allows me to get to that clean structure that it seems to so elastically bind around.

    Adding to the list from my thoughts:
    • Use OO when maintainability counts.
    • Use OO when structure is lacking, or for abstracting structure. (much like your fourth suggestion.)
    • Use OO when logic fails, or has little use, but data manipulation seems to bring us closer to the goal.
    • Use OO when you know how to use OO. Don't just go and bless something, and tell us you're OO programming, use it if you know how to manipulate it and the methodology it gives you in programming.

    I do not claim to know what I'm talking about :)
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Where/When is OO useful?
by Zaxo (Archbishop) on Jun 21, 2002 at 07:11 UTC
Re: Where/When is OO useful?
by rinceWind (Monsignor) on Jun 21, 2002 at 13:21 UTC
    Some advice for jarich and anyone else dabbling in OO.

    Get a book on design patterns. The Gang of Four book is a classic, and many people's bible, but the book is somewhat meaty and not very accessible. There are many simpler books available which explain the patterns in more user friendly language, but most of these have set opinions about one or more favourite patterns, and why they are so important.

    An understanding of design patterns will help differentiate between good OOD and bad OOD, and also highlight when OO is appropriate.

      There is also www.patternsinperl.com

      My personal thinking is that design patterns are overrated and can lead to the attempt to match problems to patterns.

      gav^

Re: Where/When is OO useful?
by kvale (Monsignor) on Jun 21, 2002 at 05:36 UTC
    Excellent post. I would add the guideline

    "Create a class when you want a resusable perl module with a flexible interface"

    Many OO CPAN modules are simplicity themselves to use and modify.

    -Mark
      Many OO CPAN modules are simplicity themselves to use and modify.

      I don't agree. I think many OO modules are hard to modify - the latter meaning "subclass".

      Here's coding problem.

      Someone has written a BaseballPlayer class, to maintain statistics of players. Your task is to subclass it to create a BaseballPlayer::Pitcher class so the additional statistics for pitchers can be dealt with.

      Where are you going to store your instance data?

      If you answer "in the hash returned by the constructor of the BaseballPlayer class", or "in whatever object the superclass uses", or "that depends on how the BaseballPlayer class is implemented", you fail the test. And why do you fail? Because you are breaking the basic concept of object orientness: encapsulation. It shouldn't matter how the superclass is implemented. Unfortunally, Perl doesn't make it easy to use inheritance. It all works handy-dandy if you can enforce a certain style, implementation or module usage of the total inheritance tree, but that isn't usually the case. It wouldn't be code reuse if you had to write everything yourself, now would it?

      Abigail

        Where are you going to store your instance data?

        Since you asked... in a variable outside the object itself: a closure, in all likelihood. In my experience, this is the best use for flyweight objects in Perl, far better than as an awkward method of strong encapsulation. Here is how I'd do it:

        #!/usr/bin/perl use strict; use warnings; package BaseballPlayer; use Carp; my %attrib; sub BEGIN { %attrib = map { $_ => 1 } qw( RBI Batting_Average Hits Runs Stolen_Bases Games_Played ); no strict 'refs'; for my $n ( keys %attrib ) { *$n = sub { $_[0]->{$n} } } } sub new { my( $class, %arg ) = @_; exists $attrib{$_} or croak "Unknown stat: $_" for keys %arg; $arg{$_} ||= 0 for keys %attrib; bless \%arg, $class; } package BaseballPlayer::Pitcher; our @ISA = 'BaseballPlayer'; { my %object; sub new { my( $class, %arg ) = @_; my %pitcher_stat = map { $_ => delete $arg{$_} || 0 } qw( ERA Stri +keouts ); my $base = BaseballPlayer->new(%arg); my $ret = bless $base, $class; $object{$ret} = \%pitcher_stat; $ret; } sub ERA { $object{$_[0]}{ERA} } sub Strikeouts { $object{$_[0]}{Strikeouts} } sub DESTROY { delete $object{$_[0]} } } package main; my $p = BaseballPlayer::Pitcher->new( Hits => 23, ERA => 4.32 ); print $p->Hits, "\n"; print $p->ERA, "\n";

        Likely you have your own solution; if it is significantly different (or especially if it's better) than mine, do share it. This solution passes the tests you mentioned, and has become a regular habit for me. I agree with your point: Perl doesn't make it easy to use inheritance. This is a wordy, tiresome ritual, and thus is error-prone. Various aspects of Perl's OO require such rituals, however; personally, I wouldn't single out inheritance on this account.

        Update: Changed $p's ERA to something realistic, upon zdog's advice.

        Update: Simplified my code, upon tye's advice. My inclusion of needless code was a cargo-cult imitation of my own practices, which reflected the needs of prior projects. This, I think, underscores my point about the unfortunate effects of rituals which compensate for the shortcomings of a language.

        The truth is that ineritance sucks. There are problems where inheritance is a good solution, but most of the time composition is a better method, and not just because it promotes encapsulation. A has-a relationship is just plain easier to code with. Even if your language provides a good mechanism for inheritance (which Perl does not) if your writing a substantial class than you'll find yourself looking under the hood 9 times out of 10.

        Cheers,
        Erik

        Light a man a fire, he's warm for a day. Catch a man on fire, and he's warm for the rest of his life. - Terry Pratchet

Re: Where/When is OO useful?
by jotti (Scribe) on Jun 22, 2002 at 18:27 UTC
    Quote:
    Then BUU asked a question and I found myself responding "Do consider OO where it might be useful". But what does that mean? If someone had said that to me before I worked on The Project I doubt I would have had a good idea of where OO would be useful at all.


    Yes, it's like the stupid answer to the question What kind of music do you listen to? "I listen only to good music." or the statement "Good thing that I don't like coffee. It tastes so horrible."