The second lab exercise in Design Patterns: factories

This exercise considers factories as components which instantiate objects. In particular we are interested in the design of factories which do not depend on concrete types of the created objects. We call such components generic factories, although their implementation does not have to be connected with generic programming. The main advantage of the generic factories is that they do not have to be changed when we wish to instantiate novel kinds of objects. In all parts of this exercise we shall consider the pet example from the first exercise. Our task shall be to make it possible to create new kinds of animals without changing the code which trigers the creation. The key expression in C is given by:
struct Animal* p1=myfactory("cat", "Ofelija");
We see that the task of a generic factory is i) to associate the symbolic identifier cat with the concrete data type struct Cat, and ii) to create a new object of that particular type. Unfortunately, the implementations of generic factories are tightly coupled with the details of the desired programming language. Therefore we shall have to consider particular languages separately.

In order to receive credit for this exercise you have to solve the problem in C, and any of Java, Python and C++. Of course, you are welcome to solve the problem in all four languages!

1. We shall first consider the implementation of a generic factory in C. Since C has very limited introspection capabilities, the only portable way to reach the solution of our problem is to pack individual components in dynamic libraries (.dll,.so). However, before delving into details, let's illustrate our goal by the following test example.

#include "myfactory.h"

#include <stdio.h>
#include <stdlib.h>

typedef char const* (*PTRFUN)();

struct Animal{
  PTRFUN* vtable;
  // vtable entries:
  // 0: char const* name(void* this);
  // 1: char const* greet();
  // 2: char const* menu();
};

// parrots and tigers defined in respective dynamic libraries

// animalPrintGreeting and animalPrintMenu similar as in lab 1

int main(void){
  struct Animal* p1=(struct Animal*)myfactory("parrot", "Bluebeard");
  struct Animal* p2=(struct Animal*)myfactory("tiger", "Prudence");
  if (!p1 || !p2){
    printf("Creation of plug-in objects failed.\n");
    exit(1);
  }

  animalPrintGreeting(p1);//"Hoist the Colors!"
  animalPrintGreeting(p2);//"Meow!"

  animalPrintMenu(p1);//"brazilian nuts"
  animalPrintMenu(p2);//"lukewarm milk"

  free(p1); free(p2); 
}
Follow these instructions to complete the exercise.
  1. Implement the following function:
       void* myfactory(char const* libname, char const* ctorarg);
       
    The function has to i) open the dynamic library given by the 1st argument (libname), ii) load from it the function create, iii) call that function with the 2nd argument (ctorarg), and iv) return the obtained pointer to the caller. For simplicity, the function may to assume that the desired library is in the current working directory and decorate the name of the library with './' and standard extension .so (UNIX) or .dll (Windows). before writing any code, we recommend to study the documentation of functions dlopen ans dlsym (UNIX), or LoadLibrary and GetProcAddress (Windows). Also, make sure you fully understand the declared typedef PTRFUN. Place the function's prtotype into myfactory.h, and the implementation into myfactory.c.
  2. Implement functions animalPrintGreeting and animalPrintMenu. These implementations shall be very similar to what we had in exercise 1, however pay attention that now we obtaine the pet name by invoking the method name (index 0 of the virtual table). Now you should be able to compile the main program (with gcc: gcc main.c myfactory.c -ldl). Running the obtained executable should terminate with message Creation of plug-in objects failed.
  3. Implement dybaminc libraries parrot.so and tiger.so (parrot.dll i tiger.dll on Windows). These implementations shall be very similar to their counterparts in the first exercise. The source code has to i) define the concrete datatype with a struct containing the pointer to the virtual table, and the pointer to the pet name. ii) define functions ("methods"): name, greet and menu. iii) define the virtual table, te iv) define the "constructor" with prototype void* create(char const* name); Place the source code of the two libraries into files tiger.c and parrot.c, and compile them into shared libraries (with gcc, for parrot.c: gcc -shared parrot.c -o parrot.so)
  4. Test again the main program and resolve the bugs (if any). If uncertain, follow the following recommendations. Place all source files into the same directory (main.c, myfactory.c, myfactory.h, parrot.c, tiger.c). Now you can perform the compiling and testing by pasting the following commands into the terminal (of course, you are free to write a script).
         gcc main.c myfactory.c -ldl
         gcc -shared tiger.c -o tiger.so
         gcc -shared parrot.c -o parrot.so
         ./a.out
       

2. Here we consider generic libraries in Python. In order not to make this exercise too easy, we consider a more demanding problem. You need to implement a program which would perform the following: i) instantiate one pet from each source file found in the directory plugins, and ii) report all instantiated pets by their greeting and their favourite meal. The test program might look as follows:

def test():
  pets=[]
  # visit each file in the directory 'plugins'
  for mymodule in os.listdir('plugins'):
    moduleName, moduleExt = os.path.splitext(mymodule)
    # if it is a file with Python source ...
    if moduleExt=='.py':
      # instantiate a pet ...
      ljubimac=myfactory(moduleName)('The pet '+str(len(pets))))
      # ... and add it to the pet list
      pets.append(ljubimac)

  # report pets
  for pet in pets:
    printGreeting(pet)
    printMenu(pet)
The output should look like so:
The pet 0 greets: Land ahoy!
The pet 0 likes brazilian nuts.
The pet 1 greets: Meow!
The pet 1 likes lukewarm milk.
Feel free to follow the instructions:
  1. model the pets by classes tiger and parrot which you will place into the same named modules in the directory plugins. As before, the pet types need to define a constructor which receives the pet name, and methods name, greet and menu. Individual pets can be described with 9 lines of Python.
  2. The function myfactory has to use the functions find_module and load_module of the imp module, as well as the builtin function getattr. Function myfactory can be implemented with 9 lines of Python.

3. The solution of problem 1 is fully applicable in C++, however we won't repeat that since we already did that before. Instead, we shall consider a possibility to create objects of types which are compiled and linked into the current executable program, but without an explicit dependency towards these types. The key to the solution is to realize that in C++ we can initialize static variables by arbitrary functions which are automagically invoked by the runtime environment before calling main(). Our task is to iterate over all available pet types, to creat an instance of each type, and to report on it by printing its greeting and favourite meal. Use the following assumptions in teh implementation.

  1. All pet types inherits from class Animal:
      class Animal{
      public:
        virtual char const* name()=0;
        virtual char const* greet()=0;
        virtual char const* menu()=0;
      };
      
  2. Each concrete pet type has to define the following: Definitions of classes and methods of a particular pet can be placed into the same file (e.g. parrot.cpp).
  3. Let the factory be a singleton which:
        class myfactory{
        public:
          typedef void* (*pFunCreator)(const std::string&);
          typedef std::map<std::string, pFunCreator> MyMap;
        public:
          static myfactory &instance();
        public:
          int registerCreator(const std::string &id, pFunCreator pfn);
        public:
          void *create(const std::string &id, const std::string &arg);
          std::vector<std::string> getIds();
        private:
          myfactory();
          static myfactory *instance_;
          MyMap creators_;
        };
        

For simplicity, we have proposed that the function create returns void*. Due to that, the clients of the factory must cast the received pointer to the proper base type. A better solution can be devised by employing templates, but we leave that as a facultative exercise to the interested student. The main program could look like so:

int main(void){
  myfactory& fact(myfactory::instance());
  std::vector<std::string> vecIds=fact.getIds();
  for (int i=0; i<vecIds.size(); ++i){
    std::ostringstream oss;
    oss <<"Ljubimac " <<i;
    Animal* pa=(Animal*) fact.create(vecIds[i], oss.str());
    printGreeting(*pa);
    printMenu(*pa);
    delete pa; 
  }
}

Recommendation: place all files (main.cpp, animal.hpp, myfactory.cpp, myfactory.hpp, parrot.cpp, tiger.cpp) into the same directory and invoke the compilation simply with:

g++ *cpp; ./a.out

A remark for those who would like to implement this solution in C. Unfortunately, the C standard does not leave a possibility to invoke user function before main() is called. However, there are no substantial obstacles to realize such solution. Therefore, particular compilers offer non-standard additions to the standard (gcc: constructor attribute, msvc: pragma data_seg autostart).

4. Here we address the implementation of a generic factory in Java. We consider the abstract class hr.fer.zemris.ooup.lab2.model.Animal with abstract methods:

  public abstract String name();
	public abstract String greet();
	public abstract String menu();
and the concrete methods:
	
  public void animalPrintGreeting() {
		...
	}
	
	public void animalPrintMenu() {
		...
	}
We assume that the concrete implementations of the class Animal shall be placed into the package hr.fer.zemris.ooup.lab2.model.plugins, as well as that each pet is going to have a constructor receiving a string (the pets name).
public class Parrot extends Animal {

	private String animalName;

	public Parrot(String name) {
		...
	}

}
Your job is to write a class AnimalFactory with a static method newInstance which creates an arbitrary pet. There should be no compile-time dependencies towards components with concrete classes: everything has to be resolved at run time. The skeleton of that class is as follows:
public class AnimalFactory {

	public static Animal newInstance(String animalKind, String name) {
		...
		return ...;
	}
	
}
Assuming that the .class files of concrete types are available in the classpath of the Java virtual machine, the dynamic loading of the corresponding types can be carried out by the static name forName of the class Class:
Class<Animal> clazz = null;
clazz = (Class<Animal>)Class.forName("hr.fer.zemris.ooup.lab2.model.plugins."+animalKind);
Once we have a reference to the class, new instances of that class can be default constructed by invoking the method newInstance():
Animal animal = (Animal)clazz.newInstance();
Unfortunately, this is not going to work in our case, since our pets do not have the default constructor (remember, their constructors receive the String representing the pet name). Therefore we can rely on Java Reflection API, look for constructor receiving a String and finally invoke it as shown below:
Constructor<?> ctr = clazz.getConstructor(String.class);
Animal animal = (Animal)ctr.newInstance(name);
This approach will not work if the .class files corresponding to the concrete types are not available in the JVM classpath. In this case one needs to create an instance of the ClassLoader object receiving a path to the directory with .class files, and either i) invoke its method loadClass(...) or ii) a variant of the Class.forName method receiving the desired ClassLoader. Evohere is an example:
ClassLoader parent = AnimalFactory.class.getClassLoader();

URLClassLoader newClassLoader = new URLClassLoader(
	new URL[] {
		// Add a directory (ends with '/')
		new File("D:/java/plugins/").toURI().toURL(),
		// Add a JAR (ends with '/')
		new File("D:/java/animals.jar").toURI().toURL()
	}, parent);
Now we can write:
Class<Animal> clazz = (Class<Animal>)newClassLoader.loadClass("hr.fer.zemris.ooup.lab2.model.plugins."+animalKind);
or
Class<Animal> clazz = (Class<Animal>)Class.forName("hr.fer.zemris.ooup.lab2.model.plugins."+animalKind, true, newClassLoader);
Please note that if a ClassLoader is used, the Factory class should ensure that the same ClassLoader is used for loading all variants of a particular pet class.