| |||||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||||
Print This Article REALbasic University: Column 094
OOP University: Part SeventeenGreetings, REALbasic fans! Sorry about the lack of columns the past couple of weeks. It was unplanned, or I would have warned you, but at any rate, my schedule is back to normal. I hope you enjoyed the vacation because it's now back to work! I've received a number of emails from readers who've appreciated our current "OOP University" focus, so I hope you won't mind if we continue it for a little while longer. There are still a few important aspects of object-oriented programming I'd like to cover. First, to refresh our minds after our long bout with SuperDraw, we'll do a quick review of the concepts we've learned so far. I want to make sure everyone understands everything. In the future I'll introduce some new ideas to consider, and we'll explore some tips and suggestions on how to create better object-oriented programs. Finally I have some plans for some example projects which will illustrate many of the ideas we've discussed.
OOP in ReviewModal and Event-Driven ProgrammingThe first couple OOP University lessons pointed the important differences between modal and event-driven programming. This is a topic that is rarely discussed any more (all modern programming is event-driven), but it's vitally important. Event-driven programs are not necessarily object-oriented, but object-oriented programs fit naturally into the event-driven paradigm. To refresh your memory, in modal programs the program controls the current "mode" and only allows certain commands within that mode. Event-driven programs are sometimes called "user-driven" programs because the program is controlled by the user. Event-driven programs are more difficult to write because the program must be ready any contingency. However, that makes them ideal for OOP since you can create reusable objects that already incorporate some basic functionality. A perfect example is a window object: add a new window in REALbasic and without any programming by you the window knows how to allow itself to be moved around, expanded, resized, and closed. The basics of how that window is drawn and the user interaction of widgets on that window (resize, collapse, etc.) work without any extra programming by you. Because OOP makes event-driven programming so much easier, a lot of OOP concepts are wrapped into event-driven programming. People with traditional programming experience often confuse the two, mistaking event-driven aspects for OOP (and vice versa). Just keep in mind that events in themselves have nothing to with objects. Most objects communicate via events (messages), but problems in your application's structure usually have more to do with modal programming than object-oriented design flaws.
Object BasicsOur next few lessons introduced objects, explaining the concept of an object (an arbitrary encapsulated structure), and most importantly, the difference between a class and an object. Here are the three key things to remember about objects:
It's critical you understand these three points, so learn them well.
The Genealogy of ObjectsOur OOP lessons continued with giving objects the ability to do things. That's done simply by adding appropriate methods to our classes. But then we complicate the situation by introducing the most important feature of objects: inheritance. Without inheritance objects wouldn't be much different from modules -- simply a collection of routines. You can reuse a module or method just as you can reuse objects, but the reuse must always be exactly the same. With inheritance, however, an object can inherit the main features of its parent, but add its own unique capabilities. Even better, with overriding, an object has the ability to redefine the behavior of its parent. Thus two objects can have a draw method but each do different things (the sub-object can define its own version of the draw method and override the parent's method). This is what gives OOP its power. We get the benefits of code reuse yet still have the ability to customize. The customization doesn't change the parent object at all. Even better, none of our external code changes either!
EncapsulationThis leads us to the next most important aspect of objects: encapsulation. Encapsulation is way of building your object (or object collection) into its own world separate from the rest of your code. This is extremely useful and powerful because it allows you to modify the object without interfering with the outside code, and change outside code without hurting the object. It also makes the object more generic and less specific. It's important to understand that encapsulation implies incorporating a proprietary data structure to your object. (Objects that don't manipulate or hold data are rather useless.) Your external code (code not part of the object) knows nothing about how the object's data structure is organized. Is it an array? A linked list? A series of properties? Who knows! The external code can only talk to the object via a set of predefined methods. There might be an "addData" method, a "removeData" command, and a "getData" function. But how that data is structured and stored is up to the class. The best way to understand this is to compare an encapsulated approach to the non-encapsulated way. Let's pretend we have created a freeform address book application. It holds names, mailing addresses, phone numbers, and other information about contacts. Here's what it looks like: ![]() The interface is simple: a scrollbar along the bottom controls which record is displayed and updates a staticText which tells us which record we're editing. Buttons at the top let us add or delete records. Obviously our address data needs to be stored in a data structure for the program to manipulate. Since this is a freeform database, we don't need to worry about which piece of data is where: all we need is a way to separate each record of data. So let's say we use an array of a strings: each string will contain a single record of data. So we add a property to our window: theData(0) as string Now in our non-encapsulated version, all aspects of the window directly manipulate our data structure (the array). So, for instance, our newRecordButton has this code:
See how it is modifying theData directly? That's bad programming: we want as few elements as possible modifying the data structure. It's also modifying scrollBar1, which could be another problem down the road. Our ScrollBar1 control has this code:
As we continue to add features, the code becomes more complex. DeleteRecordButton needs to modify theData also, and we've created multiple elements that interact and each manipulate the data structure directly. Sure, this will work... for now. The problems come in the future, when you decide to add more features (like a "Find" command) and perhaps decide to modify your data structure. Then your entire program breaks -- all the code between your interface and data structure must be rewritten.
If this is written in an encapsulated format, however, it's far easier to update in the future because the interface code simply sends messages to the data object. Let's rewrite this app by making a subclass of the ScrollBar control. We'll make it Data Central Command and only allow it to manipulate the data structure. Add a class (File menu, "New Class") called scrollClass with a super of ScrollBar. Our scrollClass class will contain routines (methods) for manipulating the data structure: no external code will know anything about the structure or be able to manipulate it: ![]() As you can see, we've added a number items to the class. We've added a new event (initialize), which happens when the control opens. We'll need that in order to set links to the editField and staticText objects that scrollClass manipulates. Within ScrollBar1 (which is now a scrollClass object), we put this code in the initialize event:
This lets our object talk to the appropriate elements of window1. It's really the most complicated part of the program, and as you can see, it's not that complicated. You just must remember to properly initialize scrollBar1 before using it. Once initialized, scrollBar1 controls everything. Look how much simpler the window1 code becomes. The action event of newRecordButton is now this:
DeleteRecordButton looks like this:
See how much simpler our external code becomes? Now we can modify scrollClass as much as we want but we'll never have to change that external code. Of course to properly finish this class we'd need to add routines to load and save the data to a file, as well as a folderItem property. Then window1's file menu commands would just send a message to scrollClass to open or save the file. I hope this example makes the importance of encapsulation a little clearer. While I didn't actually finish this address book application, you can look at the shells of the app if you like. I'm including both versions (non-encapsulated and encapsulated) of the project file.
Next WeekWe'll finish our OOP review and move on to a few new ideas you should understand. NewsRecently REAL Software announced news we've been anticipating for some time: REALbasic is now available for the Microsoft Windows platform! The basic product is US$99.99 while the professional version (normally US$399.99) has an introductory price of US$299.95 until May 23, 2003. You can read the full announcement here. REAL Software have also updated the Mac product, releasing the free 5.1 update. This update fixes problems and significantly speeds up compilation time -- I highly recommend you go download this latest version immediately! This version also includes a handy "4.5 to 5.x" project converter PDF document, which details some of the changes 5.x brings and how to ensure your old projects work in the new compiler. LettersOur letter this week comes from Joe Rice, who writes:
I've actually thought of doing a calculator as an RBU project, though I don't know when that will be. It's a simple but good project in that it demonstrates many useful techniques. The problem you are experiencing is extremely common. The mistake you are making is using integers instead of doubles. You should define your variables as double like this:
The double data type supports fractions (numbers with a decimal point). This won't hurt anything if the typed in number doesn't include a decimal point, but if it does, the number will convert correctly with the val() function. That should solve your main problem. Going the other way, number to string, you may find that str() trims the numbers past the decimal point too soon. For better control over the conversion, use the format() command instead. The format() function will also let you work with percentages, currency, and even automatically add commas for larger numbers. I hope this helps! About the Column REALbasic University is a weekly instructional column on programming with REALbasic and is brought to you by REALbasic Developer, the magazine for REALbasic programmers. Each week we answer select reader questions, and we're always open to ideas for future columns. Send your questions to . (Keep your questions simple and specific. General queries like "How do I write my own web browser?" will be neglected.) Your question won't be answered immediately, but will be answered in a future column. (If you don't want your correspondence published, just be sure to indicate that when you write. Otherwise it's fair game.) About the Author See the REALbasic University Archives
REALbasic University contents ©2001-2004 by Marc Zeedar and REALbasic Developer. All Rights Reserved.
| |||||||||||||||||||||||||||||