1.6 Compostion

Composition

In a real-world solution, objects don't exist by themselves. They are composed of other objects. A car, for example, might be composed of an engine, tires, seats, and so forth. Of course, we could have one object that had all of those components, but this would raise problems. What if the car should get another engine? We'd have to redefine the whole inheritance tree. Maybe we would break other car classes by doing that. To avoid that, we'd have to create totally separate classes. This would ruin the whole inheritance idea.

For this reason, smart people invented composition, which allows us to combine classes and to assemble bigger objects out of simple components. This allows flexibility because we can exchange components independently. Let's go back to our movie theater, which is assembled of screens. Each screen might be assembled of other objects that we haven't discovered so far, such as chairs and projectors.

Another use for composition would be in the user interface. Each form is composed of various objects like buttons, page frames, textboxes and other components.

There are two different kinds of composition: containers and logical composition.

Containers

Containers are the most common way to achieve composition in Visual FoxPro. When container composition is applied, objects basically live inside other objects. This is mostly true for interfaces, but there are also some reasons to use contained behavioral objects, as you'll see later. For now we'll stick with interface objects such as buttons and textboxes, since the containership is easy to understand this way.

Imagine a simple Visual FoxPro form. The form has visual elements such as buttons, page frames, option buttons, checkboxes and, of course, the form itself. Each of these elements is a single object with a distinct set of properties and methods. It's obvious that most of these objects live within the form. So in this case, the form is the container for the other objects. What's not so obvious is that some of these objects might be containers themselves. Pageframes, for example, are containers that can contain only pages. Each page, on the other hand, can contain almost any kind of object, including other pageframes. Another container would be an option group that includes option buttons. Of course, the user might not even see it, since the container might be borderless and transparent and therefore invisible. Other containers might be totally invisible, such as a form set which is used only to tie a couple of forms together.

Containers have one very important advantage. Once the container is created, all the objects contained in that container (also called member objects) are created as well, since the container knows about all the objects that live in it. As a result, containers are easy to use because they hide a lot of the complexity they may deal with.

Most of the FoxPro containers have a very specific use. They can contain only certain objects, such as the pages in a pageframe, interface objects in a form, or columns in a grid. In addition, there is one generic container that can contain almost any kind of object. It is used only to compose objects and has no visual appearance other than an optional border. This might be useful for grouping objects and simplifying the use of that logical group. In our theater example, we could create a user interface that combines fields for the movie name, the schedule, and the number of available seats. We could then use this group of objects on every form that deals with this kind of data simply by dropping the whole container on a form.

Containers can also be subclassed. The child class would inherit information about the objects that live within each container. Take note that only the container itself is subclassed. All the member objects still remain of the same class that they were in the parent container. But we'll get to that in more detail later on.

In the subclass, you can change and overwrite properties and methods of the container, as well as for the members. This means you can move members around if they are visible objects, or you can change their behavior. This is a great feature, especially for rapid application development (RAD), and it's unique to FoxPro. But there are also some issues that come with it. We'll discuss them in Chapter 3 when we talk about pseudo-subclassing and instance programming.

How to add objects to a container

Defining contained objects with the visual designers is easy. The programmer simply drops an object in a container. Accomplishing the same in source code requires a little more knowledge, but it's still rather obvious. Here's some code to demonstrate:

DEFINE CLASS MyContainer AS Container
ADD OBJECT Button AS CommandButton WITH;

Caption = "Button 1";

Left = 100;
Top = 50
ENDDEFINE

In this sample, we create a container and add an object using the ADD OBJECT command. We can also specify the object's class by using the AS keyword, and we can specify default values after the WITH keyword.

This example shows how you can specify member objects in the class-definition level. But there are ways to add objects to containers dynamically once the objects are in memory. All containers have AddObject() and NewObject() methods. They both create an object in a container. Here's how you could create the above button sample dynamically:

SomeContainer.AddObject( "Button", "CommandButton" )

We'll get into more detail about AddObject() and NewObject() in Chapter 2.

Referencing objects in containers

Now that we know how to define contained objects, it's time to think about referencing them. So far we've only talked to stand-alone objects, but referencing contained objects isn't much different. All you have to do is add the name of the container.

The following code shows how to reference a button in a form:

Form.Button.Caption = "Click me!"

As you can imagine, this is simple. Unfortunately, it gets more complex sometimes. Let's assume we have a form that contains a pageframe, which has a couple of pages, and on page 3 we have a button. We would talk to this button like so:

Form.PageFrame.Page3.Button.Caption = "Click me on page 3!"

These examples show how to reference objects from the outside world. But objects can also be referenced from within the container using the THIS keyword. The following sample shows how this would work from a method that belongs to the form:

FUNCTION CustomFormMethod
THIS.PageFrame.Page3.Button.Caption = "Click me on page 3!"
ENDFUNC

From within the pageframe, the path would vary because we are one level deeper in the nesting structure:

FUNCTION CustomPageFrameMethod
THIS.Page3.Button.Caption = "Click me on page 3!"
ENDFUNC

Parent vs. ParentClass

Not only does each container know about its members, but each member also knows about its container. It can reference the container using its Parent property. The pageframe in the sample above could talk to the form it lives in like so:

FUNCTION CustomPageFrameMethod
THIS.Parent.Show()
ENDFUNC

The container an object lives in is also called the parent object not to be confused with the parent class. Parent classes are the classes the current class inherits from. A parent object is the object that hosts the current object. There is a big difference in these relationships.

The inheritance relationship is also called an IS-A relationship. For example, the secure screen we created above IS A screen. The container relationship is also called a HAS-A relationship. Using the same example we could say: "Each screen HAS-A projector "

To fully understand the difference between these two relationships, it's important to differentiate the terms class and object.

Composition by reference

Besides containership, the other form of composition is called logical composition. This kind of aggregation (which is another word for composition) is language specific. It might be a simple logical aggregation where one object is simply aware of other objects and knows how to talk to them. Or this might be accomplished by having object references (pointers) to other objects.

Some might call logical aggregation the classical composition, used by many of the object-oriented environments. In FoxPro it's used less often than containers, because containers are easier to maintain and use. They are basically a more evolved form of aggregation.

Let's look at how an object can reference another object without containing it. First of all, we have to create a couple of classes:

DEFINE CLASS Screen AS Custom
MainProjector = .NULL.
ENDDEFINE

DEFINE CLASS Projector AS Custom
FUNCTION StartMovie()
** Some code to start a movie
ENDFUNC
ENDDEFINE

Once the classes are objects in memory (see Chapter 2 for more information about object instantiation), we can establish the logical connection:

Screen.MainProjector = Projector

As you can see, we use a custom property to reference the projector object. From the screen's point of view, this construction looks a lot like container composition. The projector could be referenced like so:

Screen.MainProjector.StartMovie()

The other way is more difficult. The projector doesn't know what object it belongs to because parent objects exist only in container relations. The ownership issue can get tricky using logical composition. References can even become cyclic, as in the following example:

DEFINE CLASS Screen AS Custom
MainProjector = .NULL.
ENDDEFINE

DEFINE CLASS Projector AS Custom
Screen = .NULL.
ENDDEFINE

Screen.MainProjector = Projector
Projector.Screen = Screen

In this sample, the projector has a link to the screen it belongs to. Unfortunately, we've lost track of which object is the owner (user) of the other object and which object is being used. But ownership might not be a major issue when using this kind of composition. As a matter of fact, one object could belong to more than one other object at a time:

Screen1.MainProjector = Projector

Screen2.MainProjector = Projector

In this scenario, both screens reference the same projector. This might not make a whole lot of sense in the movie theater example, but it might in other cases. Imagine two forms that reference the same behavioral objects. This is a very powerful way to share functionality in a resource-friendly way. Or imagine that two or more objects simply refer to something they share, like the cafeteria in the hallway of the movie theater. This kind of aggregation cannot be built using containers because each object can only live within a single host. Also, objects that live within a container cannot move to other objects. With referential aggregation, objects are free to travel and can make themselves at home wherever they find a hosting object.

There are a couple of issues associated with this construction. Obviously, we'll have some overhead in determining when the projector object can be released once it's no longer needed by any other object. But in the basic scenarios, Visual FoxPro keeps track of that and releases the referenced object as soon as all references are gone.



Advanced Object Oriented Programming with Visual FoxPro 6. 0
Advanced Object Oriented Programming with Visual FoxPro 6.0
ISBN: 0965509389
EAN: 2147483647
Year: 1998
Pages: 113
Authors: Markus Egger

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