SmartBox is a Component-Container framework. It allows users to create and run applications simply by loading pre-defined components into a running container that connects them.
Here are a few SmartBox use cases:
In this example there are two components: A simple calculator component that implements the ICalculator interface, and a statistics calculator that depends on the ICalculator interface. This is how the above application would be expresses as a UML component diagram:
Here’s how it would translate to a class diagram:
Here's a screenshot right after the Calculator component has been added to the container:
The Add, Rem, and Run commands prompt the user for the component name:
All components are assumed to reside the components sub-package of the smartcalculator package. This makes them easy to find (but imposes a burden on the user.)
Here's a screen shot after both components have been loaded:
Running the StatsCalculator produces the following dialog:
Here are the implementations:
SmartBox is an MVC application. Components is a sub-package of smartbox that contains user-defined components.
A container contains components. Each component maintains a set of provided and a set of required interfaces as well as a collection of fields.
Notes
· Interfaces are represented by members of Class in Java reflection. You can tell if an instance of Class represents an interface like this:
someClass.isInterface();
A component has a name, reference to its container, a set of interfaces it implements or provides, a set of interfaces it needs other components to implement (i.e., the required interfaces), and a table associating each required interface with the field of that type:
Set<Class<?>> requiredInterfaces;
Set<Class<?>> providedInterfaces;
Map<Class<?>, Field> fields;
Container container;
String name;
For example, the StatsCalculator does not implement any interface, but it has a field of type ICalculator:
public class StatsCalculator extends Component implements App {
public ICalculator
arithmeticCalculator;
// etc.
}
This means that ICalculator is a required interface for StatsCalculator. By contrast, Calculator has no required interfaces but provides ICalculator.
The container maintains three tables:
Map<Class<?>, Component> providedInterfaces;
Map<Class<?>, Component> requiredInterfaces;
Map<String, Component> components;
When a component is added to a container, its container reference is set and it is placed in the components table under its name:
component.setContainer(this);
components.put(component.getName(), component);
The interfaces it implements are added to the provided interfaces table:
for(Class<?> intf: component.getProvidedInterfaces()) {
providedInterfaces.put(intf, component);
}
Similarly, its required interfaces are added to the provided interfaces table.
Finally, the container does a little matchmaking. If component c1 of class C1 requires interface Intf1, in other words, c1 has a field fld1 of type Intf1:
class C1 extends Component {
public Intf1 fld1;
// etc.
}
and if component c2 of class C2 implements Intf1:
class C2 extends Component implements Intf1 { ... }
Then the container sets c1.fld1 to c2:
c1.setProvider(Intf1, c2);
Here are partial implementations of the important classes:
Recall that a stack machine is able to execute basic arithmetic commands such as add, mul, div, and sub. Each of these commands replaces the top two elements of a stack by their sum, produce, quotient, and differences, respectively. In addition, the stack machine can also execute basic stack commands:
push arg // push arg onto
the stack where arg is any double
pop // remove top element from the
stack
top // return top element without
removing it
isEmpty // return true if the stack is
empty
clear // remove all elements from the
stack
Here's a sample session with the stack machine:
-> push 7
result: done
-> push 3
result: done
-> push 5
result: done
-> push 1
result: done
-> top
result: 1.0
-> add
result: done
-> mul
result: done
-> top
result: 18.0
-> sub
result: done
-> top
result: 11.0
-> quit
bye
We would like to implement our stack machine as a customization of SmartBox.
Here's a component diagram:
Here's the design:
Here's a screenshot of the container after the components have been loaded:
We need a reusable console component that requires a command processor and implements the App interface:
You can test your code using:
Next we need a stack machine component that implements CommandProcessor and requires an implementation of the IStack interface:
Finally, need a Stack component that implements the Stack interface. Java already has a generic Stack<T> class. Unfortunately, it's not a component nor does it know about our IStack interface. This is an excellent opportunity to use the Adapter pattern:
Complete and test the implementation of the stack machine.
Here's another example of a component that implements the CommandProcessor interface:
Notes on Java Reflection.