SmartBox

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:

 

Example: Statistics Calculator

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:

Diagram

Description automatically generated

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:

·        ICalculator.java

·        Calculator.java

·        StatsCalculator.java

Smartbox Design

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();

SmartBox Implementation

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:

·        Container.java

·        Component.java

·        ContainerView.java

·        ContainerPanel.java

Lab: Stack Machine Customization

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:

Step 1

We need a reusable console component that requires a command processor and implements the App interface:

·        Console.java

·        CommandProcessor.java

You can test your code using:

·        MathMachine.java

Step 2

Next we need a stack machine component that implements CommandProcessor and requires an implementation of the IStack interface:

·        StackMachine.java

·        IStack.java

Step 3

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:

·        Stack.java

Complete and test the implementation of the stack machine.

Example (Casino)

Here's another example of a component that implements the CommandProcessor interface:

Casino.java

References

Notes on Java Reflection.