A software framework for managing reactive dependencies in object-oriented (OO) applications
Table of Contents
REFRAME is a software framework which can be used by application developers to manage reactive dependencies in object-oriented (OO) applications. In object-oriented (OO) applications, objects collaborate by invoking each other's behaviour and data, thus forming dependencies. Sometimes there is a need (or benefit) to express these dependencies as reactive, i.e. in a way that when one object changes its data or invokes a metode, its dependent objects automatically react and update their state or invoke their own methods. In practice, such dependencies are often found in event-driven systems, graphical user interfaces, animation, spreadsheet systems, embedded systems, etc.
When need arises to treat dependencies as reactive dependences in OO applications, developers usually end up developing their own ad-hoc solutions inspired by Observer (or similar) design pattern. Such solutions may be challenging to develop form scratch. However, more importantly, such solutions often do not live up to the challenge of handling large and complex dependency graphs that may form when combining sufficient number of reactive dependencies. Some of the issues that frequently arise: not performing updates that are necessary, performing updates that are not necessary, performing updates redundantly, causing glitches (temporary inconsistencies) due to incorrect update order, creating infinite loops due to circular dependences. These issues may heavily distort results we get from the underlying software, as well as its performance. In order to avoid creating their own solutions from scratch, developers can use REFRAME to: specify individual reactive dependencies, combine reactive dependencies into a dependency graph, perform update process (sequentially or in parallel), analyse constructed dependency graph, visualize constructed dependency graph.
- C# .NET
- Visual Studio 2019/2022 with DGML Editor installed
- .NET Framework
This is an example of how you may give instructions on setting up your project locally. To get a local copy up and running follow these simple example steps.
- C# .NET
- Visual Studio 2019/2022 with DGML Editor installed
- .NET Framework
In order to use Reframe framework you need to obtain framework binary files (.dll and .exe files). This can be done either by downloading ready to use binaries from GitHub Release page (ReframeCore.zip and ReframeTools.zip files), or by cloning the project and building these files by yourself.
Reframe framework consists of 12 .dll files and one .exe file. However, which of these files will be necessary to use depends on the use scenario.
Component | Description |
---|---|
ReframeCore | The core component of the framework which enables end-user application to construct dependency graphs and perform update process. It contains abstractions representing members of reactive node hierarchy, dependency graph, update, scheduler, reactor and other essential parts of the framework. |
ReframeBaseExceptions | Contains the definition of the root REFRAME exception which is inherited by specific exceptions in other components. |
ReframeFluentAPI | Contains classes with extension methods which allow us to use reactor in a declarative style. |
IPCServer | Contains interfaces and abstract classes with reusable part of the server side of inter-process communication. |
ReframeServer | Contains concrete classes with REFRAME-specific implementation of the server side of inter-process communication. |
ReframeExporter | Contains classes responsible for exporting dependency graph data and update process data in a form of XML content. |
ReframeToolsGUI | Contains graphical user interface classes and coordinates the rest of the components. |
IPCClient | Contains interfaces and abstract classes with reusable part of the client side of inter-process communication. |
ReframeClient | Contains concrete classes with REFRAME-specific implementation of the client side of inter-process communication. |
ReframeAnalyzer | Contains abstractions representing members of analysis graph and analysis node hierarchies, factories for creating graph and node objects, filter specifications, analysis and metrics implementations, and other parts related to graph and update analysis. |
ReframeImporter | Contains utility classes which interpret XML content fetched from end-user application. |
ReframeVisualizer | Contains interfaces and abstract classes with reusable and technology-independent part of the visualizer implementation. |
VisualizerDGML | Contains concrete classes implementing visualizer using DGML technology. |
From the perspective of end-user (host) application, there are three main scenarios.
- Scenario - Core features
The first, and the most basic use scenario assumes using only two components from REFRAME, namely: ReframeCore.dll and ReframeBaseExceptions.dll. These are mandatory components which allow end-user application to access core features of REFRAME, i.e. to construct dependency graphs and perform update process.
In order to use the core features of REFRAME it is necessary to reference ReframeCore.dll and ReframeBaseExceptions.dll libraries in the project we want to use them in. In order to do that, we first need to obtain dll libraries. This can be done either by downloading ready-to-use binaries from GitHub Release page (ReframeCore.zip and ReframeTools.zip files), or by cloning the project and building these files by yourself. When we have required dll files, we need to reference them in our project. Dependening on our IDE of choice (e.g. Visual Studio, Visual Studio Code, Resharper, etc.) this process might be different. In case of Visual Studio we would need to right-click at our project in Solution explorer, choose Add Reference option, and then find the dll files we want to reference.
- Scenario - Core features + Fluent API
The second use scenario introduces optional ReframeFluentAPI.dll component, which (as its name imply) contains implementation of Fluent interface. This allows us to, in addition to traditional imperative style, also use alternative, more declarative approach when specifying individual reactive dependencies between nodes.
Extending the base use scenario with fluent API requires referencing one additional component - ReframeFluentAPI.dll. This is done exactly the same as with the first two dll files.
- Scenario - Core features + Fluent API + Reframe Tools
The third use scenario is only relevant if we want to use REFRAME tools. It allows us to setup inter-process communication between end-user application and REFRAME tools. By introducing IPCServer.dll, ReframeServer.dll and ReframeExporter.dll components, we can start the server in end-user application which will respond to requests from the Analyzer tool (client) and send dependency graph data. Since integration of server components into end-user application and starting the server is a trivial task, application developers can switch to and from this use scenario easily.
In addition to referencing required dll files, in order for Reframe Tools to exchange information with end-user application we need to start Reframe server in end-user application. This can be done by adding following lines of code in Program Main method or any other convenient point in code.
var server = new ReframePipeServer;
server.StartServer();
Creating new reactor object with identifier "default" and registering it in ReactorRegistry to be available for further use.
var reactor = ReactorRegistry.Instance.CreateReactor("default");
var reactor = ReactorRegistry.Instance.GetReactor("default");
In order to perform update process of entire graph we need to fetch reactor object from registry and invoke PerformUpdate method.
var reactor = ReactorRegistry.Instance.CreateReactor("default");
reactor.PerformUpdate();
public int A
{
get {return _a; }
set
{
_a = value;
reactor.Update(this);
}
}
var reactor = ReactorRegistry.Instance.GetReactor("default");
(reactor.Updater as Updater).Strategy = UpdateStrategy.Asynchronous;
await reactor.PerformUpdate();
var reactor = ReactorRegistry.Instance.GetReactor("default");
(reactor.Updater as Updater).Strategy = UpdateStrategy.Parallel;
await reactor.PerformUpdate();
Reactor exposes three events that can be used to report on the progress and status of update process. These events are: UpdateStarted, UpdateCompleted and UpdateFailed.
var reactor = ReactorRegistry.Instance.GetReactor("default");
reactor.UpdateStarted += Reactor_UpdateStarted;
reactor.UpdateCompleted += Reactor_UpdateCompleted;
reactor.UpdateFailed += Reactor_UpdateFailed;
...
private void Reactor_UpdateStarted(object sender, EventArgs e)
{
var graph = (sender as IUpdater).Graph;
MessageBox.Show($"Update process for graph {graph.Identifier} has started!");
}
private void Reactor_UpdateFailed(object sender, EventArgs e)
{
var error = sender as UpdateError;
var graph = error.Graph;
var failedNode = error.FailedNode;
MessageBox.Show($"There was and error in node {failedNode.Identifier} during update process for graph {graph.Identifier}!");
}
private void Reactor_UpdateCompleted(object sender, EventArgs e)
{
RefreshGui();
}
After starting REFRAME Tools a graphical user interface appeares showing the list of registered reactors in end-user application. For each registered reactor a detailed data about its structure can be displayed.
After selecting registered reactor, available menu offers six levels at which reactive nodes can be shown, e.g. object-member level. List of nodes are then displayed in a table with data available at chosen level of abstraction.
After selecting registered reactor and a level of abstraction, it is possible to show only reactive nodes with particular role (filtering by role) in a dependency graph, e.g. source nodes.
After any list of reactive nodes is displayed, and any reactive node is selected, it is possible to display reactive nodes which are in various ways associated with selected node (filtering by association).
After selecting registered reactor we can show information about the latest update process. Here we can see information about the overall update process, such as update status, cause, duration, error, etc. Also, we can see the list of all reactive nodes which participated in the update process. In addition to usual information about reactive nodes, for each node we can see the duration of update, as well as whether the update resulted in a value change.
After displaying any list of reactive nodes we can visualize it in a form of DGML graph shown in Visual Studio’s DGML Viewer.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
Distributed under the MIT License. See LICENSE.txt
for more information.
Marko Mijač, email: mmijac [at] foi.hr
Project Link: https://github.com/MarkoMijac/REFRAME