Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
In Part 1 of this series, I laid the groundwork for an application based on automatic dependency tracking. The first step was to design and build the information model (IM). The IM records all the system's logic; it is designed such that a client knowledgeable in the problem domain can build, traverse, and apply a solution. In this installment, I will construct a user interface (UI) that automatically discovers dependencies upon the IM and updates itself.
Read the whole series on automatic dependency tracking:
The application that we started in Part 1 is Nebula, a network design tool. Nebula's information model records the connections among various network devices. While building the IM, we inserted dynamic sentries to monitor access and modification of dynamic data. While constructing the UI, we will insert dependent sentries to react to changes in the IM.
We use automatic dependency tracking to simplify application development and maintenance. When we work with a program that discovers dependencies and updates attributes automatically, we can focus on application logic, not housekeeping. The program can also respond to changes in object dependency as we update the program, meaning that as the problem changes, the solution changes. We don't have to go back and revisit old dependencies as we add new features.
To achieve these worthwhile benefits, we must adopt a few disciplines. The following six guidelines will help us construct a user interface based on automatic dependency tracking:
onGet() method to ensure that a dependent attribute is up to date
To use automatic dependency tracking, you simply express the calculation of each dependent attribute. The sentries determine
when each calculation should occur. To express a dependent attribute's calculation, supply a dependent sentry with an update
method in the form of an anonymous inner class. The update method gathers all required information, performs the necessary
calculations, and records the dependent attribute's new value. As the application developer, you never call the update method
directly. The dependent sentry calls the update methods when the dependent attribute needs updating. You pass the update methods
into the dependent sentry's constructor as an implementation of the IUpdate interface.
Take, for example, the update method for a graphic hop's bounding rectangle. A graphic hop, represented by the GraphicHop class, displays the IP address of a network interface card or router port near the attached device. To do its job, it must
calculate the position on the screen where the text will appear. The text's bounding rectangle is a dependent attribute, as
it is calculated based on several pieces of information: the IP address text itself, the associated device's location, and
the cable's direction. To calculate the bounding rectangle, we find the attached device's location, go a fixed distance along
the cable toward the remote device, and anchor one corner of the rectangle surrounding the text. The update method below performs
these calculations:
private Dependent m_depBounds = new Dependent( new IUpdate()
{
public void onUpdate()
{
IGraphicsInfo info = getGraphicsInfo();
// Get the size of the text.
String strAddress = m_hop.getAddress().toString();
Dimension dimAddress = info.getStringSize( strAddress, g_font
);
info.dispose();
// Find the location of the device and the angle of the cable.
Point ptAnchor;
Point ptRemote;
if ( m_hop.getDevice() == m_locatedCable.getFrom().getDevice()
)
{
ptAnchor = m_locatedCable.getFrom().getLocation();
ptRemote = m_locatedCable.getTo().getLocation();
}
else
{
ptAnchor = m_locatedCable.getTo().getLocation();
ptRemote = m_locatedCable.getFrom().getLocation();
}
Dimension dimCable = new Dimension( ptRemote.x - ptAnchor.x,
ptRemote.y - ptAnchor.y );
if ( dimCable.width == 0 && dimCable.height == 0 )
dimCable.height = -1;
// Find the point a fixed distance from the device along the
cable.
double dFactor = 20.0 /
Math.sqrt(
dimCable.width*dimCable.width +
dimCable.height*dimCable.height );
ptAnchor.x += (double)dimCable.width * dFactor;
ptAnchor.y += (double)dimCable.height * dFactor;
// Put one corner of the rectangle on that point.
m_rectBounds.setLocation( ptAnchor );
m_rectBounds.setSize( dimAddress );
if ( dimCable.width < 0 )
m_rectBounds.translate( -m_rectBounds.width, 0 );
if ( dimCable.height > 0 )
m_rectBounds.translate( 0, -m_rectBounds.height );
}
} );
Each GraphicHop object creates a Dependent sentry, m_depBounds, and gives it an IUpdate interface implementation. The anonymous inner class implements one method, onUpdate(), which gathers all the required information, calculates the bounding rectangle, and stores the result in m_rectBounds.
The Dependent sentry m_depBounds invokes the update method whenever the dependent attribute m_rectBounds needs recalculating. Any code that accesses the attribute must therefore call m_depBounds.onGet() before obtaining m_rectBounds's value. Since the update method only invokes if the attribute is out of date, onGet() ensures that the attribute is up to date.
To guarantee that onGet() is called prior to each access of m_rectBounds, the GraphicHop class defines the private method getBounds(), which appears below. Any method that requires the bounding rectangle calls getBounds() rather than accessing m_rectBounds directly, thus ensuring that the dynamic sentry is notified: