Re: Re: Object-Orienting A Procedural Scriptby jreades (Friar)
|on Dec 08, 2002 at 23:04 UTC||Need Help??|
As with many such questions, the Camel book deals with what Object Oriented analysis and design is all about, but it doesn't always do so in a way that makes sense the first time you read it (for me, the Camel book always made more sense on the third re-read of a chapter).
To answer your question about design (and using pfaut's response as a starting point), you need to change the way you think about your application.
With procedural scripts, you tend to focus on verbs -- the user submits a form with values, this causes an update of a database, and a confirmation to be displayed back to the user.
The OO approach is very different, since you are focusing on the nouns ('things' as I believe the Camel book refers to them) -- a quote is submitted by the user, this quote is written out as a row in the database... Uh, that example isn't sounding quite to coherent now that I've written it out, but the idea is there -- you focus on the data, not the process.
So in your case, the key piece of data being handled by the script/database is the quote. This is the starting point for your application -- what are the attributes of the quote? Obviously, there's some pithy phrase, say "Who are you who are so wise in the ways of science?" But there's also an author (say, "Sir Bedevere"). Probably also a date for the quote (We'll use "1066"). And maybe some kind of additional attribution (e.g. "From The Search for the Holy Grail").
Now you have some idea of the attributes of your quote object (using a concrete example is always, IMO, the easiest way to get started with an OO project), but you have to do a good deal more work before you can really say that your new class is ready. For instance, how long can the quote be? Anything between 0 and 250 (i.e. varchar), or longer (i.e. text)? Is the date required? How about the additional quote attribution?
Once you have defined your object from the standpoint of its attributes (the data itself), you can now take a stab at defining an abstract class with methods to access that data (since the whole point of OO is not to manipulate the data directly from your script. There are as many ways to define a class as there are Perl programmers, but I've always liked some of the Java standards since they are easy for others to understand. In your case, you'll want something like this (this is pseudo code):
Here you can see some of how OO works -- I have getter and setter methods (the getQuote and setQuote) that assign and return arbitrary values. In Java these would be typed and I could only assign or retrieve a String value and not, say, an integer or a hash. I also have an 'is' method that returns a boolean value.
The other thing that you may have noticed is that I don't throw any kind of error at the user of the object if they try to assign an invalid String using the setQuote method (in this case, a string longer than 255 characters, the length of the varchar field in a MySQL database). Instead, a flag is set that indicates whether or not the object is 'valid'. So, now one part of your application can set all of the attributes (using the incoming form data) without worrying about whether or not it's valid, because your object will figure that out for you.
Java, just to see another way to do this, would probably throw an exception, which the user of the object could catch (basically a trap whereby a signal doesn't cause the program to abort), or allow to propagate all the way up through the program stack (to the point where it does cause the program to abort.
From there, your program would then test the valid flag using the isValid method, and depending on the answer, it would either proceed to adding the new quote to the database, or printing the form back to the user and adding in the "invalid_message" to the page to tell the user why their quote wasn't accepted.
What's really cool about OO programming, is that your Quote class doesn't just have to be used for users adding new quotes to the database, you can also use it for retrieving quotes -- the get_quote.cgi script would simply do a database lookup, instantiate the object using the right methods, and then pass it off to your printing subroutines.
By creating this (and other) objects for use as part of your script, you are starting to isolate points of change -- another developer could come along and use the Quote object in another part of the site without having to know anything (well, theoretically at least) about what constitutes a valid quote (e.g. less than 255 characters). If you decided to change your validation (everyone wants to submit quotes longer than 255 characters, so I'm going to change the column-type to text) you don't have to change any of your processing logic, you would only change the one line of the Quote object.
To take another example, again as suggested by pfaut, you could also create a Saver class that offers a single method save. And Saver is extended with two subclasses Saver::Database and Saver::File. Maybe to start off with it's easier to just save to a file -- your Saver::File's save method just writes out a new line to a specified file, end of story. But two months from now you switch to a database version, well the Saver::Database's save method opens and database connection, inserts a row, and returns. But none of this requires any significant changes to any of the code that interacts with the Saver class since any scripts are still just calling object->save() without caring where it's being saved to.
This is why OO is a very powerful approach for application design (and especially for large applications where there are more than one or two developers working on the project) -- you use modules/classes/objects to represent logical groupings of data and then you can define the ways that others can interact with that data (i.e. I'm not going to give you a way to reset the valid flag in the Quote object) which (theoreticallt at least) protects them from the changes that you make, and you from the changes that they make.