When a GUI is composed of several subcomponents that I treat as individual Views with their own Presenter and Models, is there a pattern for gluing them together? Some subcomponents are persistently on the screen while others would get swapped in and out.
What would be a good factory pattern for instantiating the respective MVP triad for a subcomponent that gets added to the GUI at runtime?
How do you glue the subcomponents with the persistent "container" part of the GUI and with each other? Would there be a "God Presenter" that ties other presenters together?
Update: I'm now shooting for something similar to Eclipse's extension mechanism. Plug-ins register themselves to a global registry for the functionality that they provide. When a functionality such as returning data or rendering a View is needed, the registry is queried and the returned functions are invoked (this is pure JavaScript, so I'm not using interfaces). I'm going with a pure-plug-in approach where everything (even the main View) is a plug-in. I might also use an Event Bus to let the various Presenters communicate agnostically.
Now my more specific question becomes, when a plug-in is about to contribute a View, how should I go about initializing the MVP triad and getting the View rendered into a parent container (outside the module). I probably need to let the View render itself into a container passed from outside and inject the View and Model (if needed) into the Presenter. An alternative would be for the View to return a component that can be placed inside a container, but this would be against my tentative ideal of keeping everything that is GUI-framework-specific inside View implementations. I prefer if the factory/glue mechanism can be framework-agnostic.
OK I'll stop yammering now and wait for some feedback, and then perhaps add more clarifications on where exactly I'm stuck...
I think the design pattern you're about is mediator.
I've written a javascript framework that consisted of a mediator.
It works like this:
You create a global instance of the
mediator,
register objects under
certain names,
use the mediator in your implementation to call methods
from registered objects within any of
the objects.
If something isn't present - no errors fly around.
If there are multiple instances - they all get the call.
This is the basic code for that:
(An extract of my code. I will make a jquery plugin including that in a while. If you're willing to use it push me to do it faster ;) )
function Mediator(){
function log(a){
try {console.log(a);} catch(e){
try {opera.postError(a);} catch(e){
//alert(a);
}
}
}
var __reg={}; // { "what": [object, ...], ... } //registers an object
//what=key that will identify, obj=an object
this._register = function(what,obj){
if(__reg[what]===undefined){
__reg[what]=[];
}
__reg[what].push(obj);
} //unregisters multiple objects and deletes a key
this._unregisterAll = function(what){
if(__reg[what]===undefined){log('Overlord:_unregisterAll - no registers'); return false; }
__reg[what]=null;
return true;
}
//unregisters a single element key
this._unregister = function(what){
if(this._countRegisters()==1){
__reg[what]=null;
return true;
} else { log('Overlord:_unregister - no registers'); return false; }
}
//unregisters last added element
this._unregisterLast = function(what){
var cnt=this._countRegisters(what);
if(cnt==0) { log('Overlord:_unregisterLast - no registers'); return false; }
if(cnt==1) {
__reg[what]=null;
return true;
} else {
__reg[what][cnt-1]=null;
return true;
}
}
//returns number of registered items
this._countRegisters = function(what){
try{
return __reg[what].length;
} catch(e){log(e);
return 0;
}
} //calls a method from all objects registered under 'what' with an array of parameters. returns true if there was at least one successful call
this._call = function(what,method,params){
var occured=false;
for(var i in __reg[what]) {
try {
__reg[what][i][method](params);
occured=true;
} catch(e) {log(e);//auto reakcja
}
}
return occured;
}
//does the call, but also returns an array of values retuurned by function
this._returnAll = function(what,method,params){
var re=[];
for(var i in __reg[what]){
try {
re.push(__reg[what][i][method](params));
} catch(e) {log(e);//auto reakcja
}
}
return re;
}
//runs a method from first object for a given key
this._returnFirst = function(what,method,params){
try {
return __reg[what][0][method](params);
} catch(e) {log(e);//auto reakcja
return null;
}
}
}
I guess that "keeping the GUI-framework-specific inside View implementations" is an overall application-level design choice, rather than an absolute must (at least when you think to "view implementation" as "plugin view implementation").
You could - for example - have a very thin view layer at plugin level, and implement a super-view layer within the parent that calls the plugins: thinking to a system of plugins that all add a column to a table, you could well have the bulk of the view code at parent level ("table") and have your plugins to just pass little more than raw data: you would avoid to repeat yourself and would make your code more flexible and maintainable.
On the other hand, if your plugins provide very different types of functionality that never interact directly (for example if they are the different subsystems of a flight simulator) you will want to keep everything that is related to views at plugin level, so that the parent object would not have to even know what a given plugin deals with, but just place it's returned value somewhere in the GUI.
Other factors that would probably influence your choice are the language and framework (if any) that you are using: in my personal experience, design patterns tend to be far from language-agnostic, as each language (and framework) has its own strengths / weaknesses which make certain choices obvious and certain others very difficult to implement.
Just my 2¢ to the discussion, anyhow! :)
For now, I'm going with this approach:
An extender (an extension implementation that a plug-in exposes) that is contributing a GUI component has a getTriad (will come up with a better name later) method that instantiates, wires and returns a MVP triad. The View has a getComponent method that renders and returns a framework-specific GUI element container that can be added to a parent container (the framework-specific details of which are encapsulated within the parent Views). I'm not letting the View render itself into a container but instead letting the parent View render the child into itself. I think this is better in terms of ensuring child Views don't directly mess with parent Views.
Related
I'm a fan of Vue which a try to use on some occasions. Anyway, there is something I always found not so handy with it: reactivity lies within $data. Well not always, as external data can be tracked by Vue, as in computed properties, in templates… But I found this way uncomfortable and not always consistent (see another question about it, here Reactivity on Variables Not Associated With Data, Computed, etc). So my decision now is use $data as the main source of reactivity and stop trying to find other ways.
However, reactivity within $data poses me a problem in what is a common case for me: many pieces of data here and there in other imported objects. This makes even more sense as I consider Vue as the View end not the business logic. Those imported objects are sometimes complex and within Vue components, I found no way to cherry pick pieces of information and kind of ask Vue to bind to them. The only way was to declare entire objects in the $data section which makes tracking very heavy: loads of setters/getters when only one would be enough in a simple component, for example.
So I designed a class called 'Reactor' whose instances role is to install getter/setters on any piece data of my wish in a complex object (or more than one). Those instances are imported into Vue components and then $watchers of Reactor instances have properties which can contain as many functions as I wish which are called when pieces of data are altered through the Reactor. To make things simple by default is filled with the same property name as the data it bounds to. This precisely those function which will update $data when external data change.
class Reactor {
constructor() {
this.$watchers = {};
}
addProperty(originalObject, keyString, aliasKeyString) {
if(aliasKeyString === undefined) {
aliasKeyString = keyString;
}
if(this[aliasKeyString] !== undefined || originalObject[keyString] === undefined) {
const errorMessage = `Reactor: cannot add property '${aliasKeyString}'!`;
console.error(errorMessage);
throw errorMessage;
}
this.$watchers[aliasKeyString] = [];
Object.defineProperty(this, aliasKeyString, {
set(newValue) {
const oldValue = originalObject[keyString];
originalObject[keyString] = newValue;
this.$watchers[aliasKeyString].forEach((f) => {
if(typeof f === "function") {
f(newValue, oldValue, aliasKeyString);
}
});
},
get() {
return originalObject[keyString];
},
});
}
}
An example can be seen in the codepen here: https://codepen.io/Djee/pen/gyVZMG
So it's sort of an 'inverted' Vue which allows updating $data on external conditions.
This pattern also helped me resolve a case which was rather difficult before: have a double-bind on an input with a filter in-between which will set the input and its attached external value straight upon #change event only. This can be seen in the same codepen given above.
I was a little surprised to have found nothing taking this in charge in Vue itself. Did I miss something obvious? This is mainly the purpose of this somewhat long introduction. I had no time to check whether Vuex would solve this nicely.
Thanks for any comments as well.
Consider, if you will, an app with a few unique views/states - let's call it a game. You have an overworld screen, a battle screen, a multiplayer interface, and maybe a minigame or two.
For the sake of argument, there isn't a lot of code in common between each view, so it lends itself well to AMD - a central controller/dispatcher, and each game state split into a separate file/view.
dispatcher.core.js
> overworld.view.js
> battle.view.js
> tournament.view.js
> minigame.view.js
Input and key commands get routed to the dispatcher, and trickle down to the current active view, which in turn manipulates the DOM as needed. One-way AMD relationships, so far so good.
The thing I'm getting hung up on is the response flow. The API response data that goes through the system is diverse, often affecting multiple views at the same time. Consider this case:
User presses buttons to move
Key commands gets routed to map view for movement animation
Map sends AJAX request to server for movement result
AJAX returns "battle commence" response to dispatcher
Dispatcher tells map view to disable itself, then battle view to init
The dispatcher was designed for this - to receive instruction and distribute. It seems like the obvious choice, much more than letting views affect each other directly.
However, there's a fundamental flaw here - the one-way relationship between the dispatcher and the views is violated as soon as the AJAX result is sent from the view to the dispatcher. You can either use the dispatcher for your AJAX callback, or you can instruct the dispatcher to make the AJAX call for you - but either way the view requires a way to reference the dispatcher, which as I understand it, violates the core tenet of AMD. For the life of me, I can't figure out how this would be implemented correctly!
My question is this - how would one implement such a structure correctly? Is this a limitation of AMD, or am I misunderstanding it's use on a deeper level?
This question is intended to be for more of the general case, but if it affects answers at all, I'm using Require and jQuery for AMD and AJAX, respectively.
Is this a limitation of AMD, or am I misunderstanding it's use on a deeper level?
AMD does not by any means impose one way relationship between object instances in general. What it does strongly recommend to avoid (because even this is not an absolute requirement) is circular dependencies between modules. And the type of dependencies that matter for AMD are loading dependencies.
You can certainly have a module named dispatcher that goes:
define(function () {
function Dispatcher(views) {
this.views = views;
for (var ix = 0, view; (view = views[ix]); ++ix)
view.init(this);
}
return Dispatcher;
});
And viewA, viewB, that are structured like this:
define(function () {
function View() {
// ...
}
View.prototype.init = function (dispatcher) {
this.dispatcher = dispatcher;
};
// Etc...
return View;
});
Your main module could do:
define(['dispatcher', 'viewA', 'viewB'], function (Dispatcher, ViewA, ViewB) {
var viewA = new ViewA();
var viewB = new ViewB();
var dispatcher = new Dispatcher([viewA, viewB]);
});
The above is meant to be a schematic example of what is possible, not a prescription for a good design. At any rate, the point is that is is perfectly feasible as far as AMD is concerned to have circular references between objects.
There's nothing about AMD that is limiting here; it's entirely about the design of your modules themselves.
A common way to handle this is with an event-emitter.
The dispatcher can call methods directly on a view, but the view emits events which the dispatcher can listen and respond to, removing the need for a circular reference (as the view doesn't care where the events go, so it doesn't require a reference to the dispatcher.)
Fitted to your example workflow, it might look like this:
overworld tracks keypress
overworld animates in response to keypress
overworld emits 'move' event for dispatcher
// overworld.view
this.emit('move', {data});
// dispatcher
overworld.on('move', getMoveResult) // getMoveResult fires AJAX request
response tells dispatcher it's time to battle
dispatcher updates views
overworld.hide()
battle.show()
I have a single page web app with multiple backbone.js views. The views must sometimes communicate with each other. Two examples:
When there are two ways views presenting a collection in different ways simultaneously and a click on an item in one view must be relayed to the other view.
When a user transitions to the next stage of the process and the first view passes data to the second.
To decouple the views as much as possible I currently use custom events to pass the data ($(document).trigger('customEvent', data)). Is there a better way to do this?
One widely used technique is extending the Backbone.Events -object to create your personal global events aggregator.
var vent = {}; // or App.vent depending how you want to do this
_.extend(vent, Backbone.Events);
Depending if you're using requirejs or something else, you might want to separate this into its own module or make it an attribute of your Application object. Now you can trigger and listen to events anywhere in your app.
// View1
vent.trigger('some_event', data1, data2, data3, ...);
// View2
vent.on('some_event', this.reaction_to_some_event);
This also allows you to use the event aggregator to communicate between models, collections, the router etc. Here is Martin Fowler's concept for the event aggregator (not in javascript). And here is a more backboney implementation and reflection on the subject more in the vein of Backbone.Marionette, but most of it is applicable to vanilla Backbone.
Hope this helped!
I agree with #jakee at first part
var vent = {};
_.extend(vent, Backbone.Events);
however, listening a global event with "on" may cause a memory leak and zombie view problem and that also causes multiple action handler calls etc.
Instead of "on", you should use "listenTo" in your view
this.listenTo(vent, "someEvent", yourHandlerFunction);
thus, when you remove your view by view.remove(), this handler will be also removed, because handler is bound to your view.
When triggering your global event, just use
vent.trigger("someEvent",parameters);
jakee's answer suggests a fine approach that I myself have used, but there is another interesting way, and that is to inject a reference to an object into each view instance, with the injected object in turn containing references to as many views as you want to aggregate.
In essence the view-aggregator is a sort of "App" object, and things beside views could be attached, e.g. collections. It does involve extending the view(s) and so might not be to everybody's taste, but on the other hand the extending serves as a simple example for doing so.
I used the code at http://arturadib.com/hello-backbonejs/docs/1.html as the basis for my ListView and then I got the following to work:
define(
['./listView'],
function (ListView) {
var APP = {
VIEWS : {}
}
ListView.instantiator = ListView.extend({
initialize : function() {
this.app = APP;
ListView.prototype.initialize.apply(this, arguments);
}
});
APP.VIEWS.ListView = new ListView.instantiator();
console.log(APP.VIEWS.ListView.app);
}
);
Please consider the Javascript code excerpt at the bottom. Roughly it consists of two modules, one for handling messages. What is the benefit of the filtersUpdateSuccess method within the messages module?
Currently it merely delegates to the overwriteAll method of the tplPanels module. One idea that strikes me is that within the filtersUpdateSuccess method, the call to tplPanels.overwriteAll could be wrapped in a try/catch. Could this benefit me, and are there any other benefits to the extra level of indirection?
PS .... I am familiar with the following question and have consulted it and followed the links within it, but now I want an answer in a specific context as opposed to the more general: Level of Indirection solves every Problem
function msgHandlers() {
var scope
,msgs;
return {
SET_CONTEXT: function(context) {
scope = context.scope;
msgs = context.messages;
}
,SUBSCRIBE_ALL: function() {
me.subscribe(scope, msgs.FILTERS_UPDATE_SUCCESS, this.filtersUpdateSuccess);
//me.subscribe(scope, msgs.FILTERS_UPDATE_SUCCESS, tpl.overwriteAll);
}
,filtersUpdateSuccess: function(filterValues) {
tplPanels().overwriteAll(filterValues);
}
}
}
function tplPanels() {
var instances = {};
return {
locationInstance: function() {
if (!instances.locationPanel) {
instances.locationPanel = locationPanel();
}
return instances.locationPanel;
}
//more instances, etcetera
,overwriteAll: function(filterValues) {
//do something useful
}
}
//etcetera
The case here seems to be one of abstraction of interface, not extra layers of reference indirection.
Interface abstraction serves several purposes:
To cause a piece of code to fit an interface required by one generic client or one client whose shape is out of your control.
To cause a piece of code to fit an interface shared by multiple clients.
To cause a piece of code to fit a maintainer's mental model by abstracting away implementation details.
To cause a piece of code to fit an interface required by a planned (not just possible) future use-case.
If another module in your system already takes different inputs with the context-setting-subscribing-filtering interface then it can be valuable because it allows your code to plug in there: (1) or (2) above.
If you have concreate plans to have multiple providers or consumers of this interface, then it can be valuable to just make it conform to the interface now since you've already done the work to page the code into memory: (4) above.
If other modules are already structured this way, or if filtering is a common operation other modules use, then code maintainers can leverage that to learn your code quickly: (3) above.
If none of those apply, then, since filtersUpdateSuccess doesn't close over anything that tplPanels doesn't, there's no value in the interface abstraction.
I've run into a headache with Backbone. I have a collection of specified records, which have subrecords, for example: surgeons have scheduled procedures, procedures have equipment, some equipment has consumable needs (gasses, liquids, etc). If I have a Backbone collection surgeons, then each surgeon has a model-- but his procedures and equipment and consumables will all be plain ol' Javascript arrays and objects after being unpacked from JSON.
I suppose I could, in the SurgeonsCollection, use the parse() to make new ProcedureCollections, and in turn make new EquipmentCollections, but after a while this is turning into a hairball. To make it sensible server-side there's a single point of contact that takes one surgeon and his stuff as a POST-- so propagating the 'set' on a ConsumableModel automagically to trigger a 'save' down the hierarchy also makes the whole hierarchical approach fuzzy.
Has anyone else encountered a problem like this? How did you solve it?
This can be helpful in you case: https://github.com/PaulUithol/Backbone-relational
You specify the relations 1:1, 1:n, n:n and it will parse the JSON accordingly. It also create a global store to keep track of all records.
So, one way I solved this problem is by doing the following:
Have all models inherit from a custom BaseModel and put the following function in BaseModel:
convertToModel: function(dataType, modelType) {
if (this.get(dataType)) {
var map = { };
map[dataType] = new modelType(this.get(dataType));
this.set(map);
}
}
Override Backbone.sync and at first let the Model serialize as it normally would:
model.set(response, { silent: true });
Then check to see if the model has an onUpdate function:
if (model.onUpdate) {
model.onUpdate();
}
Then, whenever you have a model that you want to generate submodels and subcollections, implement onUpdate in the model with something like this:
onUpdate: function() {
this.convertToModel('nameOfAttribute1', SomeCustomModel1);
this.convertToModel('nameOfAttribute2', SomeCustomModel2);
}
I would separate out the different surgeons, procedures, equipment, etc. as different resources in your web service. If you only need to update the equipment for a particular procedure, you can update that one procedure.
Also, if you didn't always need all the information, I would also lazy-load data as needed, but send down fully-populated objects where needed to increase performance.