Matthew's Design Study
For my design study, I am going to be working to improve the design of my COSC460 project. The project is a plug-in for Eclipse designed to provide a tool for visualising software metrics directly onto source code. I have included the abstract of my honours project in an attempt to provide an overview.
The stuff below may look fairly behemothic... (It's a word right?) But it is all iterative changes to the system, so don't be scared!
Apparently my code base is too large to be compressed as a zip and uploaded... here is a tar.gz... File:CoderChrome-Alpha-5-10-2009.tar.gz
Software is typically big and complex. Software metrics provide measurements of software products and development processes, in order to help software developers understand and improve their products. Metrics, however, can add to developers' information overload problems, so visualisation techniques are needed to allow large volumes of measurement data to be efficiently communicated to an observer.
Software measurement data is normally presented in reports, tables, or graphical visualisations that are distinct from the primary way developers view their products: in a source code editor. This separation makes it hard for developers to relate measurement data to the features being measured. Additionally, the intrusive task of having to run measurement tools and accommodate different views provides a disincentive for measuring at all. We present a new visualisation technique that directly applies a visualisation overlay to source code. We have developed a tool providing this functionality for the Eclipse Java Editor.
We are conducting an evaluation to test whether this visualisation approach has the potential to improve the effectiveness of developers. The tool provides a basis for continued research into the usefulness of software metrics and understanding of the best practices of developers.
Desired Behaviour & Requirements
In this section, the basic requirements and constraints of the system are detailed.
- Produce a system that can take in metrics information and update a JavaEditor with overlaid decorations.
- Code Colouring
- Colour/shape chips
- Other augmentations...
- Any type of metrics data should be able to be represented.
- Multiple metrics/augmentations should be able to be displayed per file.
- The system should be able to deal with static and dynamic sources of metrics.
- XML and an API should be supplied to allow external providers to update information.
- It should be possible to determine mappings/metrics that are applied.
- The system should be extensible, with the possibility of supporting other display types and other languages.
- The Eclipse plug-in infrastructure must be used to provide the runtime environment for the plug-in/
- Eclipse can not open the same type of editor within the same perspective or same window.
- The JavaEditor may NOT be subclassed. Note this will force us to create a wrapper/strategy for the JavaEditor which can prod at it to gain the additional functionality required.
- The Eclipse structural hierarchy must be used.
Existing Code Base
The existing plug-in is written in Java and is approximately 5K LOC. This system is my first attempt at developing this system. At first glance it appears fairly logical; however, some more serious OOD issues lurk beneath the surface...
To the left are two initial diagrams that attempt to make sense of the existing code base. The first diagram is just the classes and most of the major connections. The second diagram groups the classes in the first diagram using colour in an attempt to provide the functional groupings of classes within the existing program. This has not directly been divided into packages as it has been noted that some of the package distinctions are lacking.
In this section I will briefly discuss each of the current components and their roles. Please note that these functional groupings do not directly correspond to existing packages. The diagrams to the left provide further detail.
- Plugin: This Singleton class provides a launcher for the Eclipse plugin. It provides all of the initial driving calls to instantiate the underlying document monitor and listeners.
- Document Monitor: This package provides handling of Java files in the editor, keeping track of those that have been loaded and also as a location to retrieve updated metrics data. Generally, this class relies on Singletons to keep track of the open files.
- Editor Listeners: These classes listen to the Eclipse editor, in most cases this is effectively the current document's buffer. Each change of state is reported to the EditorListener class.
- The Model: This set of packages provides a representation of the stored data. A MetricDocument contains all of the metrics, mappings and displays for a single Java file. There can be multiple
metrics represented using multiple mappings in any one document, and each of these may have multiple MetricSections, which represent the an actual appearance of the metric in the editor.
- Display & Editor Manipulation: This set of classes deal with the current Eclipse editor and provide the functionality to add adornments to the editor. A wide variety of these adornments exist,
they are backed up with appropriate Shape and Color managers.
- API: These classes provides an Observer interface where external 'Providers' can listen for and then as necessary provide metrics information as requested.
- Serialisation: This package allows metrics data to be provided and/or converted via a defined XML format.
- Eclipse View: These classes provide a view that allows users to alter which metric visualizations are currently active and which displays are being used for each.
Existing Usage of OOD Features & Techniques
The usage of these has not been described in detail as they are subject to change and the changes are described in greater detail below. In addition, some of the patterns usage described in this section is wrong.
- Design patterns currently used:
- Observer is used in the API.
- Singleton is used widely in both the Editor Listeners and the Document Monitor groupings.
- Strategy is used with the Display and DisplayStrategy.
- Command MetricFile acts like a passable object to facilitate interactions.
- Template Method is used in Range and Display Strategy.
- Factory Method is used in the API and with the Display.
- Design Maxims/Idioms followed (imo):
- Design Maixim/Idioms neglected:
Areas of Greatest Concern
- API: Is the architectural model appropriate? Is the use of the Observer pattern correct here? Maybe the MVC should be explored...
- Model: Are there any possible improvements?
- String Manipulation: Is there any way to avoid the current String manipulation in Mapping Properties?
- Display Strategies: Is there any way of simplifying this hierarchy? Are the current methods of instantiation appropriate?
- Eclipse View: Dynamic updating of data is a concern here.
- File Instances: Is the current design the most sound way of dealing with Eclipse's editor model (i.e. multiple files, with multiple views).
- Alternative Languages: What can be done to make the system more modular and appropriate for multiple languages?
Iterative Design Process
This section is divided into the may concerns of the project, each of these has essentially chronological actions/iterations to improve certain areas.
The key point to note here is that, if we consider the MVC pattern, the old architecture is clearly wrong as the provider of the Model is being told to update itself rather than informing the View when it is updated. This clearly breaks Tell, Don't Ask and a number of other significant maxims. In order to complete this transformation, the existing single Eclipse plugin will be broken into 3+ or more eclipse plug-ins with defined extension points and dependencies. This alteration will also have a significant affect on the API of the MetricsOverlay plugin.
Eclipse Plugin Considerations
- Eclipse allows the ability to instantiate individual plugins as required at runtime. This functionality has been deemed currently unnecessary.
- Extension points have been set up for the EditorListener and MetricOverlay plugins to notify others of their functionality.
- All of the plugins at this point have been defined as singletons, it is intended that any providers intending to provide additional functionality implement their own plug in. Would it be possible to subclass objects in MetricsProvider to make this easier?
The UpdatedEditor Issue
In order for information on the editor that has been updated to flow through the system, a few things need to be considered.
- In the first design, as it formed a single plugin, all of the data stored in what is now UpdatedEditor was stored in MetricFile with the appropriate metric information for that instance of the file.
- In order to provide necessary information to the system, data on the updated editor needs to be forwarded through the plug in.
- We want to maximise component encapsulation while minimising complexity.
Our options, therefore, are:
- UpdatedEditor is held by MetricsOverlay, this means that EditorListener needs to know about the existence of MetricsOverlay. This seems unnecessary for a simple component designed to create a well-rounded architecture. It should be able to provide its functionality by itself.
- UpdatedEditor is held by EditorListener. This seems sensible as Keep related data and behavior in one place is conserved. It does provide a slight problem, in that MetricsOverlay needs to know of the existence of EditorListener. This is usually not a problem, but implementations of MetricsDataProvider need not use EditorListener, introducing unnecessary coupling.
- We create a new plug-in containing information needed by all sections. This adds unnecessary complexity.
- We accept that the states of both are separate and that they should never know about each other. In order to do this, we must commit the sin of Avoid becomes in the MetricsDataProvider. This has the additional disadvantage that the extensibility of the system is reduced as additional knowledge is required.
I feel the best option is leaving UpdatedEditor in EditorListener and allow MetricsOverlay to see it.
This new plug-in responds to changes in Eclipse by listening to provided listener classes and collating all listen events.
- Activator - Provides the plug in instantiation.
- ServiceChecker - Checks available services that can provide dispatches and attaches dispatches as possible.
- Dispatch + Sublasses - These hierarchy checks Eclipse's hierarchy and listens to the available listeners to generate updates as appropriate, ie if the file type is right etc. Updates are call an appropriate method in EdiotrListener.
- EditorListener - Forms a part of an Observer pattern providing updates for any registered objects. Constructs an UpdatedEditor object to transfer during a update notification. This object keeps track of the previous state and uses this, with the incoming updates to determine the current state of the editor part that is currently active. If an update is needed, this class notifies any observers. This class also makes sure the ServiceTracker keeps up to date.
- UpdatedEditor - This data class stores the necessary information for accessing and modifying the editor.
- EditorState - This enum represents the state of the current editor. There are only 3 states we are actually interested in.
- AcceptableFileTypeHandler - This class allows the system to change the file types it is interested in. (Note: the default constructor is designed specifically for Java as this is its primary design. This should be subclassed for the Java version).
Comparison to Initial Design
- Now abstracted to new plug in.
- Listens to a much wider variety of events.
- Can handle different file types.
- Editor Listener and FileBufferDispatch existed with mostly the same job. Editor listener is now much less complicated as it only produces results from the listening.
- Other Listeners (Dipatch) are all new, as is the service checker.
- File handling is no longer completely statically defined as AcceptableFileypeHandler is new.
- Updated editor is a new type of data object.
- Removing EditorListener from its previous plug in was a result of using Separation of concerns.
- Getters and setters have been used only where absolutely required.
- Tell, don't ask is generally practised by all components.
- Classes that have the potential to be subclassed have protected fields, all the rest are private.
- I considered a much more heavy weight solution where this plug in kept track of all open pages and made sure they were listened to with an iron fist. However, this level of detail is unnecessary for the updates required. This fulfils Do the simplest thing that could possibly work.
- I have avoided Premature optimization, several classes contain inefficient code that it is current unnecessary to improve.
- Singleton on Activator, EditorListener, ServiceChecker, Two Dispatches.
- Observer with EditorListener as the concrete observable object.
- Mediator between EditorListener and ServiceChecker. ServiceChecker is the ConcreteMediator, Dispatch is the Colleague, and its subclasses are the ConcreteColleagues. Their interaction is complex and handled by the EditorListener class. The coupling between the Dispatches and EditorListener is intentionally loose.
- Unclear Strategy between EditorListener and AcceptableFileTypeHandler. (I thought this was a Strategy, it clearly isn't. It could be refactored into a State pattern if the system becomes used for different file types.)
- Create an abstract superclass for AcceptableFileTypeHandler and sub class for Java/UserDefined. This will clean up that strategy pattern. (Actually, it will become a state...)
- Should UpdatedEditor be part of this plug in, while it is the logical place to include it, this means two things: 1) If an EditorListener is not used, then this class will not be visible, unless explicitly included, in MetricsOverlay. 2) Currently MetricsOverlay stores the same data + MetricDocument in a MetricFile, this is suboptimal as the UpdatedEditor has to be split up to become a MetricFile. Maybe move UpdatedEditor into MetricOverlay and let EditorListener know about MetricOverlay
Comparison to Initial Design
- Ability to have annotations
- Removed necessity to use string definitions
- Enums and design patterns are used instead...
- A GUI to modify the current instance of the model.
New Design Features
- Comparators for model elements
- More logical model
- Refined definitions of a whole lot of elements in the model.
- And more!
The Model - Part 1 - Unnecessary Model Element: MappedMetric
This class was unnecessary, it simply held Mappings and MetricSections. MetricSections are now held in Mapping, this makes the model tidier. Arguably, the previous solution broke the Law of Demeter, as objects would have to query the MappedMetric just to get at the underlying Mapping. It has the slight disadvantage of adding to the complexity of Mappings, but it doing so, decreases the complexity of all classes that use the model.
The Model - Part 2 - Interpolation between Metric and Display
A value for each section of the metric has been interpolated into a value that can be used by the display. This interpolation can be linear, logarithmic (on the metric or display side, by a certain log) or exponential (on the metric or display side, by a certain exp). It should also be extensible to allow new interpolation methods, like tanh to be added.
In the old design, this was one huge method (89 lines long) with a massive switch statement in Mapping.
This was clearly a strategy of Mapping. A strategy pattern has been implemented from Mapping which deals with each exponential type as a separate subclass of the abstract InterpolationStrategy class. The instantiation is completed in Mapping and uses lazy instantiation, apart from being a good design feature, an unintended side effect is that this will also help the initial startup efficiency, which is quite important. See the attached thumbnail for details.
The Model - Part 3 - Multiple Metrics on One Display
- Some of the display types have the ability to display multiple metrics. How should we deal with this?
- This is an issue because the model is currently designed to support mappings between one metric and one display.
- This may be considered a major issue; however, it may be too difficult to realistically fix.
- Displaying multiple metrics, while possibly useful, has no proven applications currently. Having said that, one has yet to prove that metrics themselves are useful...
- Not only this, the display code is fairly ugly. Some refactoring is probably good here regardless.
- We intentionally don't solve it.
- We could just leave it, but this is probably not a nice thing to leave people who might want to extend the system.
- Have a mapping be able to take multiple metrics.
- If this change is made, some way of differentiating between different kinds of displays is needed.
- We will also need to make the mapping strategy more generic.
- This will require changes to the XML.
- Metrics should not ever have multiple displays.
The Model - Part 4 - Metric Sections & Metrics
Wait! In part 1 we moved the set of MetricSections to be held by Mapping. Work on the GUI prompted a dramatic rethink...
- This decision was wrong, we want have the general concept of Mapping from a Metric to a Display.
- There should be able to exist multiple mappings for each type of metric.
- A MetricSection represents a instance point of a Metric.
- Currently this design breaks Separation of concerns.
Solution: Move the set of MetricSections into Metric.
This solution then prompted another rethink. So, each Metric has a Range, this seems right. However:
- It should be possible to use the same Metric data with different Ranges.
Solution: Use the Adapter pattern to model the two different types of Metric, ActualMetric & DerivedMetric, with an abstract superclass Metric. DerivedMetric uses an ActualMetric to provide the MetricSections yet defines its own name and range.
The Model - Part 5 - Concept Redefinition: Display -> Augmentation
In order to clarify to important concept of Display, much thought was given to what this was meant to achieve.
- It provides and holds the detailed needed to add information to the existing JavaEditor.
- It is part of the model and can have definable properties.
- There are distinct types of these concepts.
- It does not create a new Display it simply adds to an existing Display.
Solution: Rename Display to Augmentation
The Model - Part 6 - Strings Based Settings
The current design uses lots of Strings to represent properties. Strings are ugly, unnecessary and unclean. They make my code feel like it has leprosy.
- Model the properties.
- If it doesn't make sense to model the property, use a enum.
- For user defined String values, Strings can still be used.
This change has led to the following alterations:
- The MappingProperties class is being deprecated.
- Properties can't be added in String form, the model can be manipulated to provide the same effect.
- This has led to the Strategy pattern being used to do interpolation (The Model - Part 2).
- It has removed the Switch statement smell from a number of locations in the code, particularly interpolation, augmentation instantiation.
- The addition of enums - ShapeType, ShapeDirection, MappingState.
- Many other small changes.
This was actually one of the largest changes to the model from the original release and turned the entire design in to a much more complete OO model. In doing so, it allowed me to crack a number of hard problems. It also lead to all of the refactoring work around the augmentations. With all the changes to the model, it also prompted the rewriting of the XML reader and writer classes, a 10+ hour job...
The Model - Part 7 - GUI Lessons
While many of the above decisions were prompted by the development of a GUI. One strange decision stands out. Previously, MetricDocument held only a set of Mappings. This was shown to be overly simplistic. We had to have access to Metrics and Augmentations that were not currently part of a Mapping so they could later be added to one. MetricDocument now holds sets of these three key elements.
This decision has created an interface that is closer to the Facade pattern for MetricDocument. The fact that we have the potential to have unused Metrics and Augmentations arguably follows the Behavioral completeness maxim more closely. This class was sufficiently simple that the addition of this complexity does not break Limit compositions in a class .
The Model - Part 8 - The Augmentation Hierarchy
This problem vexed me for several weeks for the following reasons:
- This part of the model needs to be very extensible.
- There is the potential that the augmentations may be used on other display types than the JavaEditor. This means that the Bridge pattern should be considered to achieve Seperation of concerns.
- The elements each augmentation involves has the potential to be complex and so a good hierarchy is important. The current strategy hierarchy was very flat and I wanted to consider Class hierarchies should be deep and narrow.
- The initial design had a single class for Augmentations and used a Strategy to model the different types. This failed to model the nature of different types of augmentation.
- Two connected inheritance hierarchies using a Strategy pattern. Each strategy is instantiated by an Augmentation subclass.
- This solution allows the possibility for a Bridge to be used if the system need extending to work on different types of concrete display.
- Model the concepts relative to each part of the hierarchy.
- Use default values to avoid oversized constructors.
- See the class diagram.
As well of working on the model of the MetricOverlay component, I have also been working on the GUI. Discounting the augmentations, their are two main interfaces that are provided to the user in the Eclipse UI. These are a table view that represents the declared mappings that are currently in the system. It also provides toggle support for a variety of other pieces of functionality.
Additionally, a set of preference windows provide the ability to configure current model to the extent that everything except that underlying metrics data is modelled and can be changed in these preference windows.
GUI Design - Part 1 - MVC Patterns
In this section I will briefly discuss the strategy used to create the preference pages and the rationale behind this strategy. I will also compare it to the MVC pattern.
Their are 4 preferences pages, a general one and then one for each of the augmentation, mapping and metric model elements. The general page is just a standard page that implements Eclipse's IWorkbenchPreferencePage and uses the FieldEditors Eclipse provides. FieldEditors are Eclipses way of wrapping SWT components into parts that could be useful as additions to preference pages. They include things like check box controls, sliders, drop down boxes etc. They also provide a way of connecting directly to the a PreferenceStore, Eclipse's way of storing interal data values. This works brilliantly for simple preference pages.
There is a slight snag to this, this system gets really complicated when the change of state on a preference page affects other elements on the same page. In order to provide a representation of the underlying model, my GUI design called for a lot of this. So, suddenly we had a complexity explosion!
In order to manage the new levels of complexity, we implemented a modified MVC pattern. In this pattern our Model is the Model, the View is the class that defines the preference page class. This contains only the elements that are static. The Controller is a level of abstraction behind the preference page class. This class is responsible for changing the View when items are selected, it is also responsible for implementing changes to the model when items are selected. It's also responsible for instantiating all of the initial data on the preference page from the model.
Obviously, this is not the traditional MVC pattern. However, implementing such a pattern was impractical for a number of reasons. The model is essentially static while edits are taking place so the view does not have to respond to changes within it. In addition, these preference classes require a superclass which arguably breaks Don't burn your base class. This meant that implementing a standard Java Observer pattern was implausible as that also uses the base class.
GUI Design - Part 2 - The Viewpart
Designing the view to display of the system for arguably one of the greatest challenges of the project. Maybe I am just not cut out for GUIs at all?
Eclipse presents a number of elements that one has to use or extend to implement a table in a viewpart. The underlying OO structure is not well documented and therefore creating functionality is quite difficult. Interestingly, one can not simply roll your own graphical content to the viewpart as Eclipse literally refuses to allow it to update (25+ hrs wasted...).
I will not detail the steps involved here, it involves implementing various different types of data handling classes, sorting classes, column and row layouts, methods to dynamically load, update and refresh content.
I have not previously included any detailed information on how, on the receipt of new metrics information, the MetricsOverlay component updates itself. Suffice to say this is also being restructured.
Where it fits in Refresher... This component is the bridging component between the EditorListener and the MetricOverlay components. While only one of each of those modules are allowed to be in operation, this module is designed to be reimplemented by clients and there can also be a selection of these modules operating at any time. It is also intended to be flexible enough that multiple implementations can be provided by a single plugin or by multiple plugins.
What it does It provides a mechanism for detecting changes to the underlying source code (detected by EditorListener) and then updating them as the client sees fit. This information is then passed to MetricsOverlay to be displayed.
I have implemented a simple version of this as an exemplar. The tool is primarily design to allow others to add information, but this should give some ideas. Currently this system provides metrics that are related to the:
- Current line of code
- The length of a line of code
- Opening/closing code blocks
- Activator - This class is responsible for the connection to the Eclipse plugin.
- MetricsGenerator - This abstract superclass represents an object that can be subclassed to add metrics information to a model.
- ListenerObserver - This class listens for changes provided by the EditorListener and tells the system to update.
- MetricGenerator subclasses - These classes provide concrete implementations of metrics data to the system.
- Observer - This observes the EditorListener.
- Observer - This is observable by the MetricsOverlay
- Singleton - Activator class.
- Separation of concerns - dividing the observer from the observable sections of the code.
Final Design & Conclusions
What still remains to be done?
- I have put a lot of thought into how the system, and in particular the model, could be extended to improve cross visualisation functionality. What I mean by this is supporting multiple editors, IDEs, languages etc. All that remains to be done is add the appropriate superclasses to provide the functionality.
- Lots of bug fixes.
- Additional augmentation types is always a nice to have.
- Continue working on more complete system documentation.
- Implement AST or JST functionality into the system.
- Integrate existing process tools and product metric libraries.
God said let there be light and there was and there was much rejoicing. While this system has a lot of underlying problems and bugs still to work out, it is vastly improved since the start of the design project and I feel I understand the concepts involved to a much greater degree. Due the sheer size of the project I will not provide an entire system model, but take a look at earlier uml that shows parts of the system and also have a look at the included package level diagram... We are also currently writing a paper on this system and the rationale behind it, during which it has finally gained a decent name - CoderChrome. I will leave you with some examples of what the system actually does...