SDSU CS 535 Object-Oriented Programming & Design
Spring Semester, 1999
Coupling & Function Pointers
Previous    Lecture Notes Index    Next    
© 1999, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 15-Feb-99

Contents of Doc 5, Coupling & Function Pointers


References


C++ Primer 2 nd Ed., Lippman, Addison-Wesley, 1995, pp. 179-206, 250-256

Java Reflection Lecture Notes, http://www.eli.sdsu.edu/courses/fall98/cs596/notes/reflection/reflection.html

Doc 5, Coupling & Function Pointers Slide # 2

Coupling & Function Pointers


We will look at two situations. First, a class needs to parse a string into name-value pairs. Second, we wish to write a timer class that will execute an object’s method at a given time. In each case notice the coupling involved.

Doc 5, Coupling & Function Pointers Slide # 3

Parsing Name-Value Pairs

The format of the string is:
name=value;name2=value2;...;nameN=valueN;

Notice the coupling of the class Foo to the StringTokenizer class. Even if you know nothing about the StringTokenizer, there is obvious coupling.

import java.util.StringTokenizer;
class Foo {
   public static final String PAIR_TERMINATOR = ";";
   public static final String 
      NAME_VALUE_SEPARATOR = "=";
   
   String[] names;
   String[] values;
   
   public void bar( String namesValues ) {
      StringTokenizer parser = 
         new StringTokenizer( namesValues, PAIR_TERMINATOR );
      int numberOfPairs = parser.countTokens();
      
      names = new String[ numberOfPairs ];
      values = new String[ numberOfPairs ];
      
      for (int k = 0; k < numberOfPairs; k++ ) {
         names[k] = parser.nextToken(NAME_VALUE_SEPARATOR);
         if (names[k].startsWith( PAIR_TERMINATOR ) )
            names[k] = names[k].substring( 1);
         values[k] = parser.nextToken( PAIR_TERMINATOR );
         if (values[k].startsWith( NAME_VALUE_SEPARATOR ) )
            values[k] = values[k].substring( 1);
      }
   }
}

Doc 5, Coupling & Function Pointers Slide # 4

Scheduling Methods

Solution 1

The class that has a method to be executed later has to implement an interface. This means that the class has to know that it will be used in this manner. It has to have a method that will be called later.

public interface Executable {
   public void execute();
}
public class Scheduler {
   // code that does scheduling not shown
   Executable toExecute;
   public Scheduler( Executable anExcutable ) {
      toExecute = anExcutable;
   }
   
   public void doIt() {
      toExecute.execute();
   }
}
public class AClass implements Executable {
   //lots of methods/field not shown
   
   public void execute() {
      System.out.println( "Done" );
   }
}

Doc 5, Coupling & Function Pointers Slide # 5

Solution 2 - Use an Adapter

In this solution AClass does not know one of its methods will be scheduled later. A third class, AclassAdapter, is created to adapt the Executable interface and AClass.

public class AClassAdapter implements Executable {
   AClass myClassObject;
   
   public AClassAdapter( AClass anObject ) {
      myClassObject = anObject;
   }
   
   public void execute() {
      myClassObject.foo();
   }
}
   
public class AClass{
   //lots of methods/field not shown
   
   public void foo(){
      System.out.println( "Done" );
   }
}
Sample Use
AClass sample = new AClass();
AClassAdapter thirdParty = new AClassAdapter(  sample );
Scheduler test = new Scheduler( thirdParty );
test.doIt();

Doc 5, Coupling & Function Pointers Slide # 6

Solution 3 - Use Method Pointers (Java)

In this solution, the Scheduler gets a string containing the name of the method to execute. Using Java’s reflection the method can be executed. Notice how little the Schedule class knows about AClass.

public class Scheduler {
   Object executee;
   Method toExecute;
   
   public Scheduler( Object anObject, String methodName ) 
      throws NoSuchMethodException, IllegalAccessException {
      Class objectsClass = anObject.getClass();
      Class[] argumentTypes = {};
      toExecute = 
         objectsClass.getMethod( methodName, argumentTypes );
      executee = anObject;
      
      int modifiers = toExecute.getModifiers();
      if ( !Modifier.isPublic( modifiers ) )
         throw new IllegalAccessException("Method " + methodName +
            " is not public in class " + objectsClass.getName() );
   }
   
   public void doIt() throws InvocationTargetException {
      try {
         Object[] noArguments = {};
         toExecute.invoke( executee, noArguments );
      } catch (IllegalAccessException willNotOccur ) {
      }
   }
}
Sample Use
Scheduler test = new Scheduler( new AClass(), "foo" );
test.doIt();

Doc 5, Coupling & Function Pointers Slide # 7

Solution 3 - Use Method Pointers (C++)


We can use pointers to do a similar thing in C++. The syntax can be a little complicated, so I will provide a little background.

Function Pointers in C++

In this example simplePointer has one argument. The argument is a pointer to a function, which has an int argument and returns an int. The argument name is functionPointer.

#include <iostream.h>
int sampleFunction( int x ){
   return x + 1;
}
void simplePointer( int (*functionPointer)( int ) ) {
   int result = functionPointer( 5 );
   cout << "The result " << result << endl;
}
int main() {
   cout << "in main" << endl;
   simplePointer( &sampleFunction );
};

Doc 5, Coupling & Function Pointers Slide # 8
Function Member Pointer
The name of the class is part of type of a pointer to a function member. The function "methodTest" has two arguments. The first is a pointer to an object of type “Foo”. The second is to a function member of class Foo. The function member has no arguments and void return type.

class Foo {
   public:
      void test();
};
void Foo::test() {
   cout << "Hi mom\n";
}
void methodTest( Foo *object, void (Foo::*methodPointer) () ){
   (object->*methodPointer)();
}
int main() {
   cout << "in main" << endl;
   Foo *bar = new Foo;
   bar->test();
   methodTest( bar, &Foo::test );
};

Doc 5, Coupling & Function Pointers Slide # 9
Template Function Member Pointer
Using templates, we can reduce the coupling on the class of the object (object abstraction decoupling). Note that “ClassType” is a template variable, whose values are types.

template <class ClassType>
void better( ClassType *y, void (ClassType::*methodPointer)() ) {
   (y->*methodPointer)();
};
int main() {
   cout << "in main" << endl;
   Foo *bar = new Foo;
   bar->test();
   better( bar, &Foo::test );
};

Doc 5, Coupling & Function Pointers Slide # 10
The Scheduler class
Finally the scheduler class in C++

template <class ClassType>
class Scheduler {
   private:
      ClassType *executee;
      void (ClassType::*toExecute) ();
   public:
      Scheduler( ClassType *object, void (ClassType::*aMethod) () );
      void doIt();
};
template <class ClassType>
Scheduler<ClassType>::Scheduler( ClassType *object, 
                                    void (ClassType::*aMethod) () )
   {
   executee = object;
   toExecute = aMethod;
};
template <class ClassType>
void Scheduler<ClassType>::doIt() {
   (executee->*toExecute)();
}
int main() {
   cout << "in main" << endl;
   Foo bar;
   bar.test();
   Scheduler<Foo>   testIt( &bar, &Foo::test);
   testIt.doIt();
};

Copyright © 1999 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.

Previous    visitors since 15-Feb-99    Next