Author Topic: FB Graphics #4.2: A Closer Look at Objects  (Read 3812 times)

0 Members and 1 Guest are viewing this topic.

Offline rdc

  • Pentium
  • *****
  • Posts: 1495
  • Karma: 140
  • Yes, it is me.
    • View Profile
    • Clark Productions
Introduction

Before we get too much farther in these tutorials I thought we would step back a moment from the details and look at the big picture. One way to look at programming is through the idea of relationships and interactions. How does one piece of code relate to another piece of code? Those relationships dictate the interactions that can take place between different code modules.

For example, FreeBasic supports modules, independent code files that have no direct relationship between each other. In order to share data between modules you must use the Common declaration on the variables that are shared between modules. The relationship of the modules dictates the interaction of the modules.

In this tutorial we will look at the differences between traditional OOP relationships and the object relationships we have in FreeBasic. We will then examine the object hierarchy in our screen object and see how these relationships dictate the interactions between our objects and how that impacts our coding strategies.

FreeBasic OOP: A “Contains A” RelationshipOOP: A Vertical Object Hierarchy

In traditional OOP the object hierarchy tends to be vertical in nature, with the program interacting with the most recent child in the chain. This is because all the child classes inherit the methods of the ancestors classes so there is no reason to interact with the ancestor classes. (I am of course talking about using an specific instance of a class with a hierarchy). For example, take the following class hierarchy.

Quote
Animal
     |
  Dog
    |
G.Shepherd

If we want to work with the German Shepherd class we don't need to worry about the Dog or Animal class since the Shepherd class contains all the inherited methods from both ancestor classes. In working with the Shepherd class we are in fact working with all the classes, but the interaction is transparent.

FreeBasic OOP: A Flat Object Hierarchy

In FreeBasic the object hierarchy is flat rather than vertical. Since we can't utilize inheritance, we are forced to working with each class to create the relationships that are transparent in traditional OOP. If we were diagram this it might look like the following.

Quote
[G.Shepherd [Dog [Animal]]]

Here the Shepherd object contains a reference to the Dog object which in turn contains a reference to the Animal object. The Screen Object Hierarchy

The screen object illustrates this cooperative nature of the containment method. The screen object hierarchy follows the flat hierarchy model as the following diagram shows.

Quote
[screen object [color object]]
                       [point object [color object]]
                       [line object [point object [color object]]

The screen object handles the interaction with the screen buffer and writing that buffer to the screen. It must be able to interact with all the other objects. We can create a color object and use that to color a particular pixel location in the buffer through the screen object. We can create and manipulate a point object, and since the point object contains a color object, we can color that point and pass it to the screen object to color a particular point in the buffer. We can also create a line object which contains two point objects, and the two point objects each have a color object so that we can plot a line to the buffer using the two points along with the associated colors. Notice that all the objects are basically on the same level and that each object must cooperate with other objects to a greater or lesser degree.

For example, the point object needs to access the methods of the color object, but the color object doesn't need to access the point object methods. Likewise the line object needs access to the point object methods, but the point and color objects do not need to access the line object methods. This defines the levels of interaction that need to take place within the objects. We can see this in the line object.

Line Object Data

The line object uses two point objects as data members.

Code: [Select]
Private:
  _p1 As pntobj2D 'End points of line.
  _p2 As pntobj2D

The point object has X and Y data members, that correspond to a pixel location in the buffer, and a color object data member.

Code: [Select]
Private:
  _pntx As Integer  'X coord of point.
  _pnty As Integer  'Y coord of point.
  _pntclr As clrobj 'The color of the point.

The color object has both RGB and HSV color as data members, which are actually type definitions that contain the RGB and HSV color elements.

Code: [Select]
Private:
  _color As pixel  'RGB color type.
  _hsv As colorhsv 'HSV color type

Since our line object contains a point object, it has access to the point object data (through the point object interface) and access to the color data (through the color object interface exposed in the point object). Notice that the line object data definition is very simple, and is one of the benefits of object oriented programming. We are utilizing the definitions in the point and color objects to add location and color to our line object.

It is All About the Interface

I mentioned that the containment model requires a bit more work than the inheritance model. This is because we need to expose the needed data members through the interface through each object reference. We still want to maintain the core concepts of information hiding and encapsulation. We still want our object data protected from unwarranted changes. We still want to hide the internal workings of our object from the outside world. Yet, our objects still need to cooperate with each other. We can do all of this, maintaining good OOP practices and cooperation, through proper interfaces.  An example will illustrate what I mean here. Let's look at setting the color for one point of the line object.

Code: [Select]
'Sets color using color object.
Property lineobj2D.PointColor1 (clr As clrobj)
  _p1.PointColor = clr
End Property

This property sets the color of Point1 (one end-point of the line) to the color of the passed color object. _p1 here is a point object, so in fact we are calling the point object's color property. The point object code is shown below.

Code: [Select]
'Sets color using color object.
Property pntobj2D.PointColor (clr As clrobj)
  _pntclr = clr
End Property

_pntclr is the color object contained within the point object. This code uses the Let operator in the color object to set the color of the point, as shown below.

Code: [Select]
'Assign color using object.
Operator clrobj.Let (clr As clrobj)
  _color.value = clr.Colorvalue
  _rgb2hsv
End Operator

Here the color is set in the color object, so that ultimately our line point now has an associated color. In order for this to work, the interface needs to be structured so that we can pass the information from the line object to the point object to the color object. The interface definitions are shown below.

Code: [Select]
'Line object public member:
Declare Property PointColor1 (clr As clrobj) 'Sets color using color object.

'Point object public member:
Declare Property PointColor (clr As clrobj) 'Sets color using color object.

'Color object public member:
Declare Operator Let (clr As UInteger) 'Assignment operator.

The information flow can go the other way as well, from the color object to the line object through the point object. The following shows the code in each object to return the alpha channel of a color.

Code: [Select]
'Line object:
'Return alpha channel of point1.
Property lineobj2D.ChannelAlpha1 () As UByte
Return _p1.ChannelAlpha
End Property

'Point object:
'Return alpha channel of point.
Property pntobj2D.ChannelAlpha () As UByte
Return _pntclr.ChannelAlpha
End Property

'Color object:
'Return alpha channel of color.
Property clrobj.ChannelAlpha () As UByte
Return _color.channel.a
End Property

The Write Once Notion

One of the benefits of OOP is the idea of writing code once and then using that code in descendant classes. We can do the same with the containment model as well.

One of the common tasks in programming is rotating an object on the screen. The line object has a rotation member as shown below.

Code: [Select]
'Rotates line around rotpoint.
Sub lineobj2D.Rotate (rotpnt As pntobj2D, angle As Double)
   _p1.Rotate rotpnt.PointX, rotpnt.PointY, angle
   _p2.Rotate rotpnt.PointX, rotpnt.PointY, angle
End Sub

You may notice that there isn't any rotation code here. The rotation code is actually contained within the point object and we simply call that rotation code in the line object. The point rotation code is shown below.

Code: [Select]
'Rotates a point around rotpoint.
Sub pntobj2D.Rotate (rotpnt As pntobj2D, angle As Double)
  Dim As Double rad = angle * (_pi / 180)
  Dim As Double cosphi = Cos(rad), sinphi = Sin(rad)
  Dim As Integer rx = _pntx - rotpnt.PointX, ry = _pnty - rotpnt.PointY

  _pntx = rotpnt.PointX + rx * cosphi - ry * sinphi
  _pnty = rotpnt.PointY + rx * sinphi + ry * cosphi
End Sub

To rotate a line we simply rotate each point of the line. We write the code once and are able to use it in any object that uses a point object. Again, the extra work here is just exposing the proper members in each object.

Code: [Select]
'Line object rotate:
Declare Sub Rotate (rotpnt As pntobj2D, angle As Double) 'Rotates line around point for angle.

'Point object rotate:
Declare Sub Rotate (rotpnt As pntobj2D, angle As Double) 'Rotates a point around point for angle.

So we gain the benefit of the write once notion of OOP with just a little extra work in defining the interfaces so that the objects can interact. If you look over the code for the line object you will see that essentially it is just calling the interface members of the point objects which in turn call the interface members of the color object as needed. There is very little original code in the line object. The bulk of the code is in defining the interface so that we and the screen object can interact with the line object. Just as in the inheritance model, we are using the code from one object in another object because the object interfaces allow the interaction to take place.

Summary

I hope you can see that there is quite a bit of benefit in using the object model as defined in FreeBasic. The extra work needed in defining the interfaces is offset by the ability to rely on member objects within the hierarchy to share some of the load of the functionality. If the interfaces are defined correctly, we can use the containment model to help speed development while maintaining good OOP practices to create robust, reusable code.