Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Shaun E on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Object oriented Design 6

Status
Not open for further replies.

SPrelewicz

Programmer
Jul 16, 2001
124
US
i apologize if this is sort of semi-perl related, but I figured the best palce to come.

I'm making the step into OOP for my larger web based apps and would like some advice about how others layout their OO Perl apps. I will be using CGI::Application and all its plugins, etc.

My project now is a Sports stats/data site, back-end for entering player data game by game, front on world wide accessible displaying data. [Nothing unusual] i will be using mySQL.

So, I am confused about what to classify and things. I'll need a data layer, but where do i put that and how to let my other classes know about it. Say I have a Player class with position, name, etc...then a Basketball Player class with stats, ability to score point, get rebounds, etc. How then does the data loading/saving into the mySQL DB fit in. Are those methods in the Player class, should they be used to instantiate the object, should I have a separate DATA class I 'use' in all my other classes.

Also, in OO design, is it common to create an array of many objects at once, as in a roster of basketball player objects, and if so how does this affect performance?

Thank you in advance for your time.

Scott

About me at Flying Roman.com lyric meaning and discussion
Prelewic, Renassance Man ;)
 
but how would you design the class for each object, how would use these classes and at the end of the day where is the data stored?

I've been trying to work out how I actually do it and I think the best explanation I can offer is that I try first to work out how I would like to use them. For the form app, I wanted something where I could say something like
Code:
my $f = new Form;
$f->title( 'Claim Input' );
$f->full_screen_help( $helptext );
$f->box(    2, 2, 24, 6 ); #x, y, w, h
$f->text(   4, 4, 'Order Ref:' );
$f->field( 13, 4, 'Text', 'ref', -length=>8 );
$f->text(   3, 5, 'Claim Type:' );
$f->field( 13, 4, 'Text', 'type', -length=>1 );
...
my $results = $f->run();
print 'Ref=', $results->ref(), ', Type=', $results->type(), "\n";

It's then clear that the Form object is going to have collections of more specific objects and it's methods box(), text(), field(), etc will simply create an object of the correct type and install it into the correct collection.

At this stage I've got a good idea of the animals in the object 'zoo' and I can start writing their constructors and stubs for their methods. It's at this stage that commonality becomes apparent and you realise that all the objects are going to have to have a paint() method. In particular, the paint methods for all the fields are going to look pretty similar, so I can slot in a Field object with a paint method that they can all inherit. The same logic applies for their value() methods, which just return the field value.

At this stage the zoo starts looking more like a tree. In a similar fashion to the way you can "factor out" repeated code from conventional programs, you can use the object hierarchy to push common functionality up (down?) the tree.

In the example I gave above, if this were play code (and not already in production!) I'd probably go back and revisit the interface design. If I'd stuck to named parameters then all my constructors could have been identical and, hence, inherited. At that point, so much of the solution is embodied in the object hierarchy that the actual coding of methods becomes almost trivial.

So where's the data? The Form object knows how to write to the screen and it has references to each decoration and each field object, but it doesn't have access to their internals other than via their methods.

Each decoration object knows where it is and how to paint itself and each Field object knows it's location, how to paint itself and how to run itself. It also knows the value that the user has entered, which it will divulge on request via the inhereited value() method.

The only irritation so far is that I've not given myself an ideal way to recover the user input data. The Result object seems a bit of a bolt-on and perhaps a result() method of the form object itself would be better.

As I said earlier, I've not done a lot of real-world object work yet and I'm still feeling my way but I do feel that I'm already getting some tangible benefits from the paradigm. I hope that all this nattering helps a bit.

I'd be happy to play at object building with you guys. If I get another real-world example I'll post it here if no-one else has come up with a candidate.

Yours,

fish

["]As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.["]
--Maur
 
OK - I see how you are trying to create an object and set values.

but how would you write the methods ie you have to write the 'run()', 'new()' , 'result()' methods.

or is there inbuilt functionality in perl for this
Code:
new Form;

is 'new' a method of the form module or an inbuilt PERL command.

"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
new" is the name of a method like any other. The name "new" is merely a convention used by perl people to ensure that we all know what we are talking about. Namely, a constructor method. I recommend you stick with the convention here.
An object is merely a blessed reference or scalar of some kind. Again, convention suggests we create an anonymous hash (remember those? ;-)), bless it into the appropriate class and return it from our constructor. Then, whenever a method is called, the first parameter will be this blessed reference. That allows us to use the blessed anonymous hash to store our instance specific data and properties.
I guess we need a small example here to make it more obvious.

.... the silence is deafening! ....


Trojan.
 
Respect Trojan, I've said it before and I'll say it again, Tek-Tips bods such as yourself and the usual suspects should re-write PERL DOCS & CPAN, it makes much more sense when you explain it in plain english.

But you're right I need an example to fully grasp it.

but grasp it i shall , i hope - lol

"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
You need to realise that the perl documentation is written to be general. We are answering very specific questions. It's much easier to answer like this than to sit down and write a complete and comprehensive document that covers everything at a decent level of detail without boring the readers like crazy.

Respect to all the authors of such content. I'd give them each a star if I could. I know I rely on content like that all the time and therefore the reason that I (and the other usual suspects!) can offer you such good advice is because of these people.

Big Cheer! Applause!!



Trojan.
 
yup Respect to you all, because without the top authors, teaching the top programmers, who then put it in plain english, where would we be. Stars All Round [2thumbsup]

"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
Please don't waggle your thumb at me!
It's a sore point (in many ways) at the moment.
;-)


Trojan.
 
lol - was it you with the cut finger a while back ?

"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
Thumb, not finger (hence waggling thumbs not funny!!!)
And a bit more than "cut".
I shredded it on a table saw on 9th August. It's still very painful.
:-(


Trojan.
 
that was silly. hope it gets better soon. [thumbsdown]

"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
..now, if you'd used objects, you could have insulated the properties of the Thumb object and protected them from the DESTRUCT() method of the TableSaw object...

f

["]As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.["]
--Maur
 
roflmfao

"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
OK - here's a simple object to get us started. It's intially trivial but we can extend it as we go.

Let's write a calculator that we can use like this:
Code:
my $calc = new Calculator;
$calc->initialise( 7 );
$calc->multiply( 2 );
$calc->add( 6 );
print $calc->result(), "\n";

We can see the methods we want and we can see how the calc object only needs one attribute, it's current value. initialise() sets it and result() returns it. The other methods should be child's play.

First we need an object. According to the bok, an object is a blessed reference. Perl doesn't care what sort of reference but I almost always use hash references because they are future-proof - you can add more and more attributes as keys in the hash - and the use of named keys can lead to more self-documenting code.

We bless a reference into a class. The bless call simply takes a reference to be blessed and a string as the name of the class. Now, here's the key to object syntax: the -> operator. When you write
Code:
$class->method( @args );
or
Code:
method $class @args
perl goes and looks in a package with the same name as $class and calls it, passing $class as the first arg, as if you had written
Code:
method( $class, @args );

This means that, if we write
Code:
my $calc = new Calculator;
perl looks in package Calculator for a method called new and calls it with the scalar 'Calculator' as it's first argument. It's actually the only argument because we didn't supply any others.

In this case, we are calling a constructor. There's nothing special about the name new(), but it makes the code clearer so we'll stick with it.

So how do we write new()? It gets the name of it's class as an argument and needs to return a blessed reference so, at it's simplest, we only need
Code:
package Calculator;

sub new {
  my $class = shift;
  my $self = {};           # a reference.
  bless( $self, $class );  # now $self is an object
  return $self;            # return it
}

I'm going to overcomplicate here because there's a habit which is well worth getting into. The constructor we've created is a class constructor - that is: it is called with the name of the class as it's argument. When we get frisky with objects, we will find uses for copy constuctors. In essence, these let us say
Code:
my $first_calculator = new Caculator;
my $new_calculator = $first_calculator->new();

Note that we didn't tell perl the class - it worked it out from the class of $first_calulator. The upshot is that the first argument to new() here is an object of class Calculator rather than the string [tt]Calculator[/tt]. Fortunately we can recover the class of an object with the ref() function, so it's easy to rewrite our new method to accept either the scalar 'Calculator' or a reference to an object of type Calculator. We just need to look and see if our first argument is a reference or not. ref() returns either the class name or undef, so we can use || to say "the class of the prototype if it's a ref, or the prototype itself".

The modified new()looks like this
Code:
package Calculator;

sub new {
  my $prototype = shift;
  my $class = ref($prototype) || $prototype;
  my $self = {};
  bless( $self, $class );
  return $self;
}

This gets typed so often that it's typically shortened beyond (IMHO) legibility to
Code:
package Calculator;

sub new {
  my $proto = shift;
  return bless( {}, ref($proto) || $proto );
}

</overcomplication>

Now lets add some methods. Remember that the first argument to each of these will be the Calculator object against which they are called, so the call
Code:
$calc->initialise( 7 );
will call our initialise method with two arguments: $calc and 7.

It's an easy method, as is the result() method. One sets a value in the hash, the other returns it:
Code:
package Calculator;

sub new {
  my $proto = shift;
  return bless( {}, ref($proto) || $proto );
}

sub initialise {
  my $self = shift;
  $self->{Value} = shift;
}

sub result {
  my $self = shift;
  return $self->{Value};
}

The numeric functions are similarly trivial:
Code:
sub add {
  my $self = shift;
  $self->{Value} += shift;
}

sub multiply {
  my $self = shift;
  $self->{Value} *= shift;
}

We've got no error-checking or reporting and we've missed a couple of good tricks on the way, but we've got a working calculator object. You can put it in the same file as it's test code like this:
Code:
#!/usr/bin/perl

my $calc = new Calculator;
$calc->initialise( 7 );
$calc->multiply( 2 );
$calc->add( 6 );
print $calc->result(), "\n";

package Calculator;

sub new {
  my $proto = shift;
  return bless( {}, ref($proto) || $proto );
}

sub initialise {
  my $self = shift;
  $self->{Value} = shift;
}

sub result {
  my $self = shift;
  return $self->{Value};
}

sub add {
  my $self = shift;
  $self->{Value} += shift;
}

sub multiply {
  my $self = shift;
  $self->{Value} *= shift;
}

There you go - a ten-minute object. Perhaps you might like to give it more complicated methods such as [tt]the_number_you_first_thought_of()[/tt] - you'd need an extra attribute (hash key) to store the intialisation values - or named registers with store() and recall() methods.

You could give the constructor an initialisation argument.

You could arrange for every method to return the invoking object so that you could say
Code:
my $calc = new Calculator( 7 );
$calc->add( 7 )
     ->multiply( 6 )
     ->store( 'tmp' )
     ->initialise( 3 )
     ->multiply( 4 )
     ->add_from_recall( 'tmp' ) );
print $calc->result(), "\n";

I've just realised that the store and recall syntax is a bit broken, but then I didn't spend much time on it!

As with this trivial example, the methods of real-world object often turn out to be very easy to write. In fact, when they are not, it's often a sign that the original design wasn't sufficiently thought through (as in the store() and recall() debacle above, where I've sentenced myself to writing second versions of every arithmetic method that use the recalled value instead of one passed as an argument. Alarm bells ring and I go back to the whiteboard).

If this has been useful, I can cover object inheritance in a similar fashion.

f



[&quot;]As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.[&quot;]
--Maur
 
was it something I said?

f

[&quot;]As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.[&quot;]
--Maur
 
fish, i'm up to my neck in it at the mo, this is something i want to learn with your help, but you'll need to hold fire on this for a bit, or carry on with the others without me :-(

"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
fishiface - that (and the sequel) would make an excellent FAQ entry...

Mike

You cannot really appreciate Dilbert unless you've read it in the
original Klingon.

Want great answers to your Tek-Tips questions? Have a look at faq219-2884

 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top