Skip to content

ExperimentView Refactoring Highlights

FollowSteph edited this page Jan 6, 2021 · 9 revisions

IMPORTANT -> STILL BEING WRITTEN

Although this is for the ExperimentView the goal is that this should be the same across all views. Other views will be adjusted as needed and as time permits. This document will only highlight the more interesting and important information.

View and Components ONLY Contain GUI Code.

Firstly the view should be limited to just GUI components and custom sub-components. Whenever there is a group of basic GUI components that go together they should be grouped together in a sub-component. There should be NO inner eventbus subscriber classes, no code to handle button presses, and so on. All subscribers should be in their respective subscriber classes and all buttons should be in their respective Action classes (more on these later). The ExperimentView code should ideally just but some basic components combined with subcomponents.

Subscribers vs Actions -> Subscribers are used when something outside of the current tabbed browser (view) updates the view such as an experiment being updated by the backend. Actions are classes to store the code of an individual user initiated action such as clicking on the save draft button, start training, and so on.

DefaultExperimentView, ExperimentView, NewExperimentView, and SharedExperimentViews

ExperimentView and NewExperimentView extend from DefaultExperimentView (SharedExperimentView extends ExperimentView). The default view manages the setting and updating of the experiment for all components. All subcomponents of these views need to implement ExperimentComponent which consists of two methods: setExperiment() and updateExperiment(). This is to allow the DefaultExperimentView to be able to loop through all the experimentComponents and/or experimentComparisonComponents and update accordingly.

The DefaultExperimentView also includes several getters that are commonly used such as getSegmentIntegrator(), getExperimentId(), getExperimentDAO(), and so on so that we can pass the view in some cases to reduce the number of method parameters.

All data is fully loaded before the components are rendered. That is to say the components no longer need to know what has been updated within the component such as the status, etc. and can safely assume that everything is fully loaded. This removes the need for all components to try and re-render items multiple times (which can be expensive).

The experiment instance is shared across all components rather than being cloned everywhere. This not only saves in memory but also means that say a subscriber updates the experiment then all components can safely just re-render themselves and no longer have to worry about updating different parts and data of the experiment.

The setExperiment() method is both reloads the experiment from the database and calls ExperimentComponent.setExperiment() on each component from the experiment lists. In contrast the updateComponents() just calls ExperimentComponent.setExperiment() but does NOT do any database calls. The updateComponents() assumes that the experiment has had it's values all updated internally such as the status. An example of this is an eventbus subscriber update in which there is no need to reload everything from the database. We can instead just update the experiment instance and call experimentView.updateComponents() or experimentView.updateComparisonComponents().

The updateExperimentFromComponents() method is used mainly by the NewExperimentClass to update the experiment instance by the components through the ExperimentComponent interface's updateExperiment() method. As we don't always use Vaadin binders we sometimes have to update the experiment instance manually and this guarantees it works throughout. So for example the main SaveDraftAction can just call newExperimentView.updateExperimentFromComponents() and then save the experiment instance to the database.

Important tidbits about DefaultExperimentView and PathmindDefaultView

Extra methods and flow have been added to DefaultExperimentView to guarantee certain code and methods are called in the right order. Eventually as the rest of the application is refactored some of this code will be pushed up to PathMindDefaultView.

initLoadData() is overriden and calls loadFullExperimentData() so that we can guarantee the loading and modifying of the experiment instance is atomic (synchronized). In fact any code that modifies the experiment instance needs to get the lock through getExperimentLock() and getComparisonExperimentLock().

isValidView() is now called in PathMindDefaultView after the data is loaded. Ideally this could be done before all the data is loaded but due to time constraints it's done after. Ideally we'd want to load just what we need to confirm and then forward to the appropriate page. This is used for example for when a user tries to call NewExperiment/id when it should be Experiment/id, at which point, in which case we auto forward the user to the correct view.

The creation of components is in it's own method called createComponents() which is called before getMainContent().

ExperimentLock and ExperimentComparisonLock

All code that modifies the experiment and/or comparisonExperiments needs to be surrounded by these locks. Keep in mind that components should never modify the experiment instance, at best it should only be Eventbus Subscribers, Actions, and the initial loading of the experiment in the view. This is very important because it was inconsistently done before.

EventBus

ExperimentUtils

Action classes

Subscriber changes

ExperimentComponent

Clone this wiki locally