When teaching Object Oriented Frameworks (usually in Advanced Object Oriented Programming courses) there is a challenge for students to understand their nature, differences with libraries and the difference between white box frameworks vs black box frameworks.

In this post I will start talking very briefly about general concepts of frameworks. Then I will implement a pretty simple white box framework as an example and after that I will transform the white box framework into a black box framework.

Frameworks Definition

The key driver that made to evolve software development into the creation and use of Frameworks was, is and will be code reuse.

Before frameworks appeared, the only software constructions used to obtain code reuse were library of functions, for procedural / imperative programming languages and library of classes for object oriented languages (other ways you can find out there are modules in Modula, packages in Ada and/or clusters in CLU). Libraries in general allows you to reuse only source code and for an specific task.

Frameworks came into the world of software development to provide design and source code reuse. Frameworks provides a greater level of reuse. They define a design and the implementation of an specific class of software application allowing the users, which are developers, to use it as it is (with some configuration like connection strings and i18n for instance), or extend it to adapt it to their specific needs.

To give a definition of what a framework is, I will take the one from the GOF book: “A framework is a set of cooperating classes that make up a reusable design for a specific class of software”. A framework define the design of our application, the key classes, responsibilities and collaborations. It will also determine the control flow of our application, where the execution always start in the framework’s code.

How Frameworks are different from Libraries?

When you use a software library, the execution start in the application’s code and is the application who call the library to use it. With frameworks, the execution start in the framework’s code, and is the framework who call application methods. This is called Inversion of Control and is one of the key concepts for frameworks and a key distinction between frameworks and libraries.

How to provide extension points and how they are achieved?

There are no new or magic mechanisms in how the frameworks provides extension points, also called hook methods. They are available thanks to the dynamic binding concept, well known from the very beginnings of Object Oriented Programming. Frameworks are able to make calls to object methods which can be replaced or interchanged at runtime with developer’s code (users of the framework) thanks to the dynamic binding mechanism.

Within the flow of collaborations implemented in a framework, there will exists documented extension points or hook methods that users can take advantage of to inject their own behaviour. This is the way of how you can extend a framework.

Why White Box or Black box Frameworks?

The way in which the framework provides the extensibility make the framework be of type white box or black box. A framework that provides extensibility by using inheritance are white box. Which means that if you need to customize a framework and to do that you have to inherit from a framework’s class and override a method then that is white box.

A framework that provides extensibility by composing and injecting objects to combine different collaborations are black box. Which means that if you need to customize a framework and to do that you have to implement an specific interface and somehow the framework will pick your class and call it at the extension points that was documented, then that is a black box.

White Box Framework Example

Lets imagine that in the Java programming language, printing a message in a dialog box is very hard. So, in order to make a software that need to do that you need to use a framework to make your life easier. Here we will develop the framework using white box design and then we will demonstrate how a developer can use our framework.

Below you will see our PrintOnScreen framework, including some comments to explain how is used.

package ar.cpfw.whitebox;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

/* This framework allows you to show text on a dialog box.
 *
 * To use it, you have to extends from the PrintOnScreen class
 * and implement their abstract method.
 *
 * The framework provides the PrintOnScreen#print method which
 * makes the magic.
 * */
public abstract class PrintOnScreen {

	public void print() {
		JFrame frame = new JFrame();
		JOptionPane.showMessageDialog(frame, textToShow());
		frame.dispose();
	}

	protected abstract String textToShow();
}

Now, lets extends the framework:

package ar.cpfw.utilization;

import ar.cpfw.whitebox.PrintOnScreen;

public class MyApplication extends PrintOnScreen {

	@Override
	protected String textToShow() {
		return "printing this text on"
			+ "screen using PrintOnScreen"
			+ "white Box Framework";
	}
}

And finally, instantiate it, and run it:

package ar.cpfw.utilization;

public class Main {
	public static void main(String args[]) {
		MyApplication m = new MyApplication();
		m.print();
	}
}

Some conceptual points to highlight about the PrintOnScreen framework and their usage:

  • Note that the framework itself is in a different package that the use of the framework. That is what will normally happen in real life if you use a framework developed by other developer. Even if you develop the framework and use it, it is a good practice to separate the classes in packages (modules).
  • The extension point (hook) is an abstract method. To use and extend the framework I have to inherit from the framework class. That is a key characteristic of a white box framework.
  • It use inversion of control, the framework method PrintOnScrenn#print calls our method MyApplication#textToShow.
  • The framework use the Template Method pattern, which is mainly the pattern used to create white box frameworks.
  • Finally, note that as a developer I would be able to override the PrintOnScreen#print method as is public. That method was not developed for that intention, that is why framework documentation is important for clarifying these things. However, as a developer I might make this mistake. This is a clear drawback of frameworks based on inheritance. Java (but not all Object Oriented languages) provides the final keyword to help framework developers to flag which methods cannot be overridden. If we put the final keyword to the PrintOnScreen#print method, that would be best.

Black Box Framework Example

Here we are going to transform our white box framework into a black box one. Find below the framework’s classes:

package ar.cpfw.blackbox;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

/* This framework allows you to show text on a dialog box.
 *
 * To use it, you have to implement the interface TextToShow.
 * Then, create an instance of the class PrintOnScreen passing
 * your implementation of TextToShow as a constructor param.
 *
 * The framework provides the PrintOnScreen#print method which
 * makes the magic.
 * */
public final class PrintOnScreen {

	TextToShow textToShow;

	public PrintOnScreen(TextToShow text) {
		this.textToShow = text;
	}

	public void print() {
		JFrame frame = new JFrame();
		JOptionPane.showMessageDialog(frame, textToShow.text());
		frame.dispose();
	}
}
package ar.cpfw.blackbox;

public interface TextToShow {
	String text();
}

Some points to highlight about the changes made:

  • We made the PrintOnScreen class final (we don’t want our users to inherit from it).
  • Then, we created an interface called TextToShow, which is the extension point. And compose it as a private member of the PrintOnScreen class.
  • After that, we changed the PrintOnScreen#print method to delegate on the TextToShow#text method.
  • And finally, we created a constructor method on the PrintOnScreen class that accept a TextToShow instance as a parameter. This is the way our users will inject their instance.

In the black box example, words like compose and delegate are now the important ones over inheritance from the white box example.

We have only left to show how to use our black box framework, let follow their instructions. First we implement the TextToShow interface:

package ar.cpfw.utilization;

import ar.cpfw.blackbox.TextToShow;

public class MyTextToShow implements TextToShow {

	@Override
	public String text() {
		return "printing this text on"
			+ "screen using PrintOnScreen"
			+ "black Box Framework";
	}
}

And finally,

package ar.cpfw.utilization;

import ar.cpfw.blackbox.PrintOnScreen;

public class Main {

	public static void main(String args[]) {
		PrintOnScreen m = new PrintOnScreen(new MyTextToShow());
		m.print();
	}
}

That completed our migration from white box to black box and hopefully illustrate the differences.