Creating the Client Application

   

Now that your interfaces and their associated implementation are finished, you can finish the client application. The application is simply going to exercise the interface implementation by creating objects and displaying information on the console.

To begin with, you're going to create two objects and compare them. Therefore, in the DogClient project, open the DogClient.cpp file and create a function named CreateDogs with no parameters and a void return type.

Your interface implementation contains three interfaces, which means a client can ask for any of these interfaces when it creates an instance of your class. When you ask for a single interface, you are only allowed to call the interface methods and access the interface properties of that single interface. The process is similar to dynamically creating a class object, but rather than using a pointer to the class, you use a pointer to the interface you wish to use. So, how does the compiler know at runtime which methods you can call and which ones you can't?

Internally, when you create the class object, each interface has what is called a virtual function table (or vtable). This is a table of function pointers within the actual class implementation. Therefore, if you were to obtain an IDog interface, all the interface methods and properties within the IDog vtable would point to the IDog methods in the implementation class.

Create two objects and assign them to an IPoodle and IBulldog interface pointer. Next, give each of the objects a name by using the name property. You can use the pseudo data member Name rather than the set_Name interface method. Finally, output the results of each object's Bark method and the Name and NumberOfFleas properties. The CreateDogs function should appear similar to the following:

  1: void CreateDogs()  2: {  3:     IPoodle* pPoodle = new CDog;  4:     IBulldog* pBulldog = new CDog;  5:  6:     pPoodle->Name = "Fifi";  7:     pBulldog->Name = "Butch";  8:  9:     Console::WriteLine( "The poodle named {0} says {1} and has {2} fleas.", 10:         pPoodle->Name, pPoodle->Bark(), __box(pPoodle->NumberOfFleas) ); 11:     Console::WriteLine( "The bulldog named {0} says {1} and has {2} fleas." 12:        ,pBulldog->Name, pBulldog->Bark(), __box(pBulldog->NumberOfFleas) ); 13: } 

Make sure you call the CreateDogs function from your _tmain function. Compile your application and execute it. If everything has worked correctly, your output should appear similar to Figure 17.4.

Figure 17.4. Output of the client application using two separate interfaces.

graphics/17fig04.jpg

The final exercise for this hour is designed to show some interesting properties of interfaces. Create another function named CreateTransformingDog with no parameters and a void return type. Rather than creating the CDog class and retrieving an interface pointer, create a class pointer to the class instead.

Interfaces exhibit an interesting property known as transitivity. The transitivity property says that given three elements, if the first and second elements are related and the second and third elements are related, then the first and third elements also maintain that same relationship. An example best explains how this works with interfaces: If you can get to object B from object A, and you also can get to object C from object A, then you should also be able to obtain object C from object B. In the interface implementation you created, if you can get an IPoodle interface pointer from a CDog pointer, and you can get a IBulldog pointer also from a CDog pointer, then you should be able to obtain an IBulldog pointer from an IPoodle pointer because they are both related to a CDog object. Listing 17.5 shows the CreateTransformingDog function with several different ways to obtain interface pointers.

Listing 17.5 Demonstration of the Transitive Properties of Interfaces
 1: void CreateTransformingDog()  2: {  3:     CDog* pDog = new CDog();  4:  5:     // uncomment to see ambiguous error message  6:     //pDog->Name = "Ralph";  7:  8:     // transform dog into a generic dog  9:     IDog* pGenDog = pDog; 10: 11:     // give it a name 12:     pGenDog->Name = "Ralph"; 13: 14:     // print out stats 15:     Console::WriteLine( "The dog named {0} says {1} and has {2} fleas.", 16:         pGenDog->Name, pGenDog->Bark(), __box(pGenDog->NumberOfFleas) ); 17: 18:     // transform dog into a poodle 19:     IPoodle* pPoodle = pDog; 20: 21:     Console::WriteLine( "The poodle named {0} says {1} and has {2} fleas.", 22:         pPoodle->Name, pPoodle->Bark(), __box(pPoodle->NumberOfFleas) ); 23: 24:     // transform poodle into a bulldog 25:     IBulldog* pBulldog = dynamic_cast<IBulldog*>(pPoodle); 26:     Console::WriteLine("The bulldog named {0} says {1} and has {2} fleas.", 27:         pBulldog->Name, pBulldog->Bark(), __box(pBulldog->NumberOfFleas) ); 28: 29:     // rename the original dog to see effect on other interface pointers 30:     pGenDog->Name = "Bowser"; 31: 32:     Console::WriteLine( "The dog named {0} says {1} and has {2} fleas.", 33:         pGenDog->Name, pGenDog->Bark(), __box(pGenDog->NumberOfFleas) ); 34:     Console::WriteLine( "The poodle named {0} says {1} and has {2} fleas.", 35:         pPoodle->Name, pPoodle->Bark(), __box(pPoodle->NumberOfFleas) ); 36:     Console::WriteLine("The bulldog named {0} says {1} and has {2} fleas.", 37:         pBulldog->Name, pBulldog->Bark(), __box(pBulldog->NumberOfFleas) ); 38: } 

You can obtain an interface pointer from the original class object, as shown on line 9. This isn't any different from what you did earlier. On line 19, you see the process of obtaining an interface pointer from an interface pointer it is derived from. This is just a simple assignment statement. However, when converting between unrelated interface pointers (in this case, the IPoodle and IBulldog interfaces), you must use dynamic casting. This is seen on line 25. Finally, because all you are doing is obtaining different interfaces for the same object, any change that affects a property implemented by a single method for all interfaces can be seen through all interface pointers. This is demonstrated on line 30.

Once again, make sure you call the CreateTransformingDog function from _tmain and compile your application. Your output should look similar to Figure 17.5.

Figure 17.5. Output of the client application demonstrating the transitive property of interfaces.

graphics/17fig05.jpg


   
Top


Sams Teach Yourself Visual C++. NET in 24 Hours
Sams Teach Yourself Visual C++.NET in 24 Hours
ISBN: 0672323230
EAN: 2147483647
Year: 2002
Pages: 237

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