Note: This is a draft manuscript. I haven’t worked on it in several years [since about 1999]. (I got “distracted” by Extreme Programming 🙂 I’d still welcome any feedback on the ideas it contains.
Introduction
Goals:
· What is a framework?
· Show how to move from basic code to a framework.
· Sensitize reader to opportunities for developing frameworks
Hats:
· end user
· framework user = application developer
· framework developer
Basics:
Definition
Source materials
Background skills
The Zoo Mini-Framework
There’s a class of games for pre-schoolers, where they can make simple pictures, and things happen when they click on the pictures. To support such an application, let’s consider a simple framework for that game.
A. Main area: Animals make a sound when clicked, and move when dragged.
B. Cast: when animal is clicked, it makes a sound and a new one appears in the main area.
C. Chatter: At random, animals highlight and make a sound until clicked again.
D. Erase: The screen is reset to an empty screen.
If you’re building a program like this once, you probably don’t need a framework. But suppose you envision a whole line of games: Zoo, Construction Zone, Ocean, etc. Then a framework can pay off.
Hot Spots
This brings us to the fundamental question when designing a framework:
What will change, and what will stay the same?
The parts that change are hot spots (to use Wolfgang Pree’s term [ref]). They represent points of flexibility and control.
For many years, it’s been a tenet of good programming that code should encapsulate decisions (see Parnas’ writings [ref]). By making a segment of code hide one decision, we acquire flexibility when changing that decision.
In our small example, we have a few things that should change:
· the screen title
· the list of “animals”
· their sounds
The main screen, the placement of items, and the overall actions will stay the same.
The Framework
We can imagine a framework to help us create these games. It might be like this:
Instructions for Using the Shape Game Framework
You can control:
· screen title
· list of shapes
· sound for each shape
Screen title
The screen title is controlled by the arguments given when the program is run.
Shapes
Each shape should be an instance of DrawableShape.
1. Extend DrawableShape to create a new class:
public class MyShape extends DrawableShape {…}
public class MyShape extends DrawableShape {…}
2. Include a static initialization that registers your class:
static {Main.add(new MyShape();}
This causes the shape to appear on the menu.
This causes the shape to appear on the menu.
3. Override clone() to make a copy of your object.
4. Override draw() to create your picture. You have access to several important attributes: position, size, and foreground and background colors.
5. Override getSound() to return a Sound object.
Defaults
· The title defaults to “Shape Game”.
· If you don’t override draw(), it defaults to a smiley face.
· If you don’t override getSound(), it defaults to a “boing” sound.
· If you don’t register any items, the default provides a single smiley face.
No Assembly Required
Notice what happens if you don’t add anything to the framework: the defaults kick in, to yield a minimal version “Shape Game” with a smiley face that goes boing.
This is a powerful technique (often ignored). It lets the application developer move from a system that works (though not how they want) to a system that works (more like they want). Instead of having to leap across the river, they can work a stepping stone at a time.
The alternative is a “pile of parts”: assemble them correctly and you get “something”.
What Can We Tell?
Some things change, some things stay the same.
The framework has some things that change, and some that stay the same.
Change: Title, Pictures, Sound
Same: Screen arrangement, Behavior
The framework defines a way to make the changes happen.
Inversion of Control – The Hollywood Principle – “Don’t call us, we’ll call you.”
One classic feature that this framework shows is that the framework controls the flow of control – our objects do not. For example, when are draw() or getSound() called? We don’t know – the framework decides.
Black box vs. White Box
A further issue is how objects are designed. Do we have to understand the code of the framework to use it? If so, it is “white box.” If we can use it without understanding the existing code, it is more “black box.” (This is a matter of degree.)
The best approach is to make the most flexible features be black box. In our example, setting the sound requires no knowledge of the internals of the framework. On the other hand, changing when the sound happens might require subclassing framework classes and making substantial changes.
Carried to the extreme, the black box case will require no code changes at all. This is how JavaBeans work: many parameters can be set, and beans can be connected together, without any code.
As features become more standardized, and their patterns of use become better understood, we can find ways to control them with even less code. For example, our framework could use this approach:
“For each menu item, create a picture file named x.gif, and a sound x.au.
Put them in the ‘menu’ directory.
The menu items will appear in alphabetical order by filename.”
In this case, the user wouldn’t need a programmer at all to add new shapes and sounds.
How do we Achieve Frameworks?
We’ll address a concrete example of converting some existing code into a framework. We’ll look at rules and design approaches that enable that.
[tbd etc]
What is a framework?
Often, when we develop software, we find ourselves saying “I’ve done that before.” How many times have you written logging or persistence code, a series of tools that manipulate the same data structure, or code to handle the same protocol you dealt with last month? If you’re a web developer, how many times have you written code to process a form and generate a web page?
By separating the parts common to many applications from the parts specific to particular applications, we can give ourselves a head start in developing the next version of an application.
“A framework is an application or partial application…” – Ralph Johnson
Subclasses: The Simplest Frameworks
The simplest implementation of a framework is something in every inheritance-based object-oriented programming language: a subclass. The parent class provides the implementation, and says “if you want to do something different, override this method.”
In Java, the Applet class provides an example of this: Applet by itself creates an empty panel, but you can override the paint() method to create your own screen.
Abstract Classes
The next simplest manifestation of this is in the notion of an abstract class. This idea is also present in many object-oriented programming languages: an abstract class is one in which some methods may be implemented, but others are left up to the subclass.
In UML, it looks like this: an AbstractClass (notice the class name is italicized) may have some concrete methods, as well as some abstract methods (whose names are also italicized). The subclass provides an implementation of the abstract method. (As it is a concrete implementation, its name is no longer italicized.)
[TBD] Rose picture AbstractClass ß ConcreteClass
Moving on Up
Clearly, two-class “frameworks” can only go so far. Frameworks are more interesting when there’s a cooperating set of classes. Then we get into issues of how to relate the classes to each other and to the framework user’s classes.
The Three Hats
1. The end user.
This is the person who is using the software that’s been developed. They don’t care (directly) that it’s been developed using a framework. (They might be happier if frameworks get them cheaper or more reliable software, but they’d be just as happy if that were accomplished by magic.)
2. The framework user = the application developer.
This is the person using a framework to create an application. They care about how hard it is to use the framework, and how much it buys them.
3. The framework developer.
This person cares about what it takes to create the framework, and whether anybody uses it.
Most of the time, we’ll be explicit about whether “user” means “end user” or “framework user.” In those cases where we’re not, it should be clear from the context which term is meant; usually it will be “framework user.”
Our Plan
We want to explore how to develop frameworks. We’ll do that through a combination of hands-on and theory.
First, we’ll work with an existing class for graphs (nodes and edges). We’ll improve our way into a framework. Along the way, we’ll identify a number of principles that can improve our code.
Then, we’ll take a look at design patterns. These are stylized groupings of classes that occur frequently, in solving certain types of problems. Some of the classic patterns have a good home in framework development.
Next, we’ll return to our graph framework. Rather than develop it further, we’ll use it as a component in a framework that supports managing a web site. This will help us look at issues in using frameworks, and interconnecting them.
Again, we’ll return to theory, exploring various ways in which frameworks can be interconnected.
We’ll explore two more ways to do frameworks: the JavaBeans environment, and plugins.
Finally, we’ll look at issues with introducing extension languages for frameworks, and software engineering issues of frameworks.
The appendices do… [TBD]
Further Reading
There is not a lot of literature that discusses frameworks per se, but there are several books that deal with related ideas.
At the top of the list is the classic Design Patterns, by Gamma, Johnson, Helm, and Vlissides. It provides a catalog of concrete designs. Many of these are useful in frameworks.
A second useful book is Refactoring, by Martin Fowler. It explores code improvement “in the small.” By making small improvements in code, you can make it more flexible.
For design by contract, see Object-Oriented Software Construction, by Bertrand Meyer. We barely touch the concept here, but it’s a useful tool in design. A related concept is the Liskov Substitution Principle, described in [TBD]
Others: D’Souza, Booch, … [TBD]