2.1 Object-Oriented Programming Basics


2.1 Object-Oriented Programming Basics

Most programmers agree that programs consist of algorithms and data structures [Wirth]. That's all fine and good, but experience over the years has indicated that algorithms tend to depend on data structures, rather than the other way around.

In other words, nearly every algorithm operates on a specific data structure. If a clearly written C program defines data structures such as struct Flipper and struct Slop , you can expect to see associated functions like flipper_insert() , flipper_shift() , flipper_status() , slop_create() , slop_blocksize() , and slop_destroy() . The functions "belong" to their data structures and always take parameters of a specific data structure. You will not be able to run flipper_status() on a Slop structure.

Take a look at the names above. You should recognize GLib's naming convention from Section 1.2. One tenet of object-oriented programming is that data types, variables of these types, and their algorithms belong together. It's just a way of thinking, but it does help if you have tools that can help you with the day-to-day organizational details. This tool can be the programming language itself; the most prominent object-oriented languages are C++ and Java, and nearly all popular scripting languages offer some sort of object system, even though not everyone chooses to use these features.

C isn't an object-oriented language by any stretch of the imagination , but that's not a problem, because the GLib's GObject library ( libgobject-2.0 ) provides object-oriented programming features for C.

2.1.1 Objects as Instances of Classes

Let's get back to Flippers and Slop.

You can think of the Flipper and Slop types as classes , and you can think of variables of these types as instances of those classes. Because classes are data structures, they contain various data fields ( attributes ; later in this book, you will see the term property ” see Section 2.4). A class also has several functions that operate on object attributes. These are called methods , and you can just think of them as functions that are attached to certain classes. For example, the Flipper and Slop classes have methods that start with flipper_ and slop_ .

An object is a data structure in memory that conforms to the class or an instance of a class. The process of creating an object from a class is called instantiation ; you invoke the constructor of a class to create objects. You can have as many objects as you like, and you can set their attributes any way you like. For example, you can have two objects named red_car and blue_car of the Car class, where the only difference between the two objects is in the color attribute.

GObject manipulates its objects with object references: typed pointers to objects that you create and invalidate with special functions. You may have more than one reference to the same object, and therefore, objects have reference counts to keep track of their references in the currently running program. If the reference count goes to zero, GObject detects that you no longer need the object and performs the following actions:

  1. GObject enters the dispose phase to get rid of any references to other objects.

  2. GObject finalizes the object, marking the memory as ready for reuse.

  3. A garbage collector returns the memory to the free pool somewhere down the line.

Constructors and destructors allow custom code that runs when you create or destroy an object. Therefore, an object can be more than just a coupling of data structures and algorithms; it can represent a process. For example, you could define FilmShort class to show an animated cartoon. When you create an object from this class, the constructor places the animation on your screen and starts playing the animation. Finalizing the object stops the animation and removes it from your monitor.

2.1.2 Inheritance

Because an object belongs to a class, it has a type. This is sometimes called a membership relation ; an object my_flipper might belong to the Flipper class because you created it with the Flipper constructor, and therefore, the class and object have a membership relation.

Now let's assume that you need to something a little trickier; for example, you want to write a program to manage your bloated CD collection. First, you create a CD class with several attributes, such as the storage location ( location ), the title ( title ), and an inventory number ( inv_nr ). However, pedant that you are, you have some CD-ROMs on your shelves in addition to audio CDs. Therefore, you decide on two classes, AudioCD and CDROM . There are some differences in the attributes: AudioCD has an artist name and track list; CDROM has an operating system and version.

Meanwhile, your CD collection grows so large that you have to store it on different planets, and therefore, you must add a planet attribute to both classes. Isn't it a little clumsy to add that to both classes?

Well, yes. AudioCD and CDROM share several attributes that were in your original CD class. Furthermore, audio CDs and CD-ROMs are both CDs, so they should have a membership relation reflecting this fact. It would make sense if AudioCD objects and CDROM objects were also CD objects.

It's possible to implement this approach. You can dig out your old CD class and define AudioCD and CDROM as subclasses of CD . They inherit attributes from their parent class (or superclass) and add some attributes of their own. The whole system is known as inheritance .

Subclasses also inherit methods from their parents. All of the methods from the CD class work on AudioCD and CDROM objects; a CD method doesn't care about the specific subclass membership of an object.

You can create subclasses of subclasses. For example, CDROM could have a SoftwareCD subclass for CDs containing programs and a RecordsCD subclass for your archived documents. These subclasses would inherit their attributes and methods from CDROM and add their own. If you had a RecordsCD object called my_disc , its membership relations would be as follows :

  • my_disc is a RecordsCD object.

  • my_disc is a CDROM , object.

  • my_disc is a CD object.

Figure 2.1 shows the entire class hierarchy as a tree.


Figure 2.1: CD class hierarchy.

If you implement RecordsCD in GObject, Figure 2.1 isn't the complete story. The GObject system has a base class called GObject . Therefore, you have the following membership relations:

  • my_disc is a RecordsCD object.

  • my_disc is a CDROM object.

  • my_disc is a CD object.

  • my_disc is a GObject object.

See Figure 2.2 for the whole tree diagram.


Figure 2.2: CD class hierarchy with GObject base class.

If your collection doesn't contain any exotic CD formats, every CD is a CD-ROM (some subclass of CDROM ) or an audio CD ( AudioCD ). The CD class serves only for the derivation of subclasses; you wouldn't instantiate CD objects. Such a class is called an abstract class . Some real-life examples of abstract classes are "building," "vehicle," and "work of art." Nothing is just a work of art, but rather, a painting, installation, sculpture, or whatever.

Interfaces

Let's say that you've finished with your CD inventory program. Now you decide that CDs are too small and so add some tapes and records. Organized person that you are, you decide to expand the inventory system to include some new classes: Media as a new abstract class that has your old CD as a subclass, as well as two other new subclasses, Tape and Vinyl . Furthermore, these last two have their own subclasses: EightTrack , StudioTape , EP , and LP (see Figure 2.3 for the exact positions ).

click to expand
Figure 2.3: Media hierarchy.

With this archival business out of the way, you now want to enable a robot to retrieve audio CDs, 8-track tapes, and records from their storage slots and play them on your designer stereo system. Therefore, the functions that control this system must be able to deal with objects from the AudioCD , Vinyl , and EightTrack classes. This would be practical if they all belonged to a common superclass somewhere, and there is one: Media . Unfortunately, this class also includes objects like CD-ROMs and studio tapes, which don't work with your fancy stereo. Therefore, you can't add the support at this level, because the whole idea of a superclass is that all of its methods and attributes are supposed to work with its subclasses.

In other object models, it's possible to define classes that inherit from several other classes at once, not just a parent class. This is called multiple inheritance , and it tends to confuse a lot of people.

GObject and many other object systems use a similar concept called an interface . In the ongoing example, all audio CDs, records, and 8-track tapes have one characteristic in common: They fit in the stereo system. In object-oriented terminology, you would say that they all implement the same interface ” that is, you can play all of them in a stereo.

Interfaces contain only methods and therefore reflect the capabilities of these objects. Typical interface names often end in - able . You could use Playable to label the interface to play something on the stereo. Figure 2.4, on the following page, shows the new interface's relationships.

click to expand
Figure 2.4: Hierarchy of media types, with Playable interface.

All functions that work with Playable objects see the methods from only those objects that are defined in the interface. The location of the object classes in the class hierarchy doesn't matter.

What you have just seen sounds easy enough, but now you're about to see the GObject implementation. The rest of this chapter demonstrates how to define classes, create objects, and build interfaces.




The Official GNOME 2 Developers Guide
The Official GNOME 2 Developers Guide
ISBN: 1593270305
EAN: 2147483647
Year: 2004
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net