mirror of
https://github.com/nasa/openmct.git
synced 2025-03-22 03:55:31 +00:00
Update description of imperative plugins
This commit is contained in:
parent
4579b4fabc
commit
c99b6df9fc
@ -10,87 +10,155 @@ Developers will want to use bundles/plugins to (in rough order
|
||||
of occurrence):
|
||||
|
||||
1. Add new extension instances.
|
||||
2. Add new service implementations.
|
||||
3. Decorate service implementations.
|
||||
4. Add new types of services.
|
||||
5. Add new extension categories.
|
||||
2. Use existing services
|
||||
3. Add new service implementations.
|
||||
4. Decorate service implementations.
|
||||
5. Decorate extension instances.
|
||||
6. Add new types of services.
|
||||
7. Add new extension categories.
|
||||
|
||||
Notably, bullets 4 and 5 above are currently handled implicitly,
|
||||
which has been cited as a source of confusion.
|
||||
|
||||
## Interfaces
|
||||
|
||||
```javascript
|
||||
Two base classes may be used to satisfy these use cases:
|
||||
|
||||
/**
|
||||
* Something that can be installed in a running instance of MCT.
|
||||
* @interface Installable
|
||||
*/
|
||||
* The `CompositeServiceFactory` provides composite service instances.
|
||||
Decorators may be added; the approach used for compositing may be
|
||||
modified; and individual services may be registered to support compositing.
|
||||
* The `ExtensionRegistry` allows for the simpler case where what is desired
|
||||
is an array of all instances of some kind of thing within the system.
|
||||
|
||||
/**
|
||||
* Install this plugin in an instance of MCT.
|
||||
* @method Installable#install
|
||||
* @param mct the instance of MCT in which to install
|
||||
*/
|
||||
Note that additional developer use cases may be supported by using the
|
||||
more general-purpose `Registry`
|
||||
|
||||
/**
|
||||
* A bundle is a set of related features that can
|
||||
* be installed in MCT.
|
||||
* @class
|
||||
* @implements {Installable}
|
||||
* @param {Metadata} metadata metadata about this bundle
|
||||
*/
|
||||
function Bundle(metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
```nomnoml
|
||||
[Function.<T, V>]
|
||||
|
||||
Bundle.prototype.service
|
||||
Bundle.prototype.install = function (mct) {};
|
||||
[Factory.<T, V>
|
||||
|
|
||||
- factoryFn : Function.<T, V>
|
||||
|
|
||||
+ decorate(decoratorFn : Function.<T, T>, options? : RegistrationOptions)
|
||||
]-:>[Function.<T, V>]
|
||||
|
||||
/**
|
||||
* Data about a given entity within the system.
|
||||
* @typedef Metadata
|
||||
* @property {string} name the human-readable name of the entity
|
||||
* @property {string} key the machine-readable identifier for the entity
|
||||
* @property {string} description a human-readable summary of the entity
|
||||
*/
|
||||
|
||||
|
||||
[Dependency<T> |
|
||||
get() : T
|
||||
[RegistrationOptions |
|
||||
+ priority : number or string
|
||||
]
|
||||
|
||||
[Registry<T> |
|
||||
register(
|
||||
dependencies : Dependency[],
|
||||
factory : Function<T>
|
||||
)
|
||||
]
|
||||
[Registry.<T, V>
|
||||
|
|
||||
- compositorFn : Function.<V, Array.<T>>
|
||||
|
|
||||
+ register(item : T, options? : RegistrationOptions)
|
||||
+ composite(compositorFn : Function.<V, Array.<T>>, options? : RegistrationOptions)
|
||||
]-:>[Factory.<V, Void>]
|
||||
[Factory.<V, Void>]-:>[Factory.<T, V>]
|
||||
|
||||
[ExtensionRegistry<T> |
|
||||
]
|
||||
|
||||
[ExtensionRegistry]
|
||||
[ExtensionRegistry.<T>]-:>[Registry.<T, Array.<T>>]
|
||||
[Registry.<T, Array.<T>>]-:>[Registry.<T, V>]
|
||||
|
||||
[CompositeServiceFactory.<T>]-:>[Registry.<T, T>]
|
||||
[Registry.<T, T>]-:>[Registry.<T, V>]
|
||||
```
|
||||
|
||||
Creating a bundle then looks like:
|
||||
## Examples
|
||||
|
||||
```javascript
|
||||
define([
|
||||
'mct',
|
||||
'./SomeExtension',
|
||||
'./SomeService'
|
||||
], function (mct, SomeExtension, SomeService) {
|
||||
var plugin = new mct.Bundle({
|
||||
key: 'myBundle',
|
||||
name: "My bundle",
|
||||
description: "A bundle that I made."
|
||||
});
|
||||
### 1. Add new extension instances.
|
||||
|
||||
plugin.extension
|
||||
|
||||
return plugin;
|
||||
```js
|
||||
// Instance-style registration
|
||||
mct.types.register(new mct.Type({
|
||||
key: "timeline",
|
||||
name: "Timeline",
|
||||
description: "A container for activities ordered in time."
|
||||
});
|
||||
|
||||
// Factory-style registration
|
||||
mct.actions.register(function (domainObject) {
|
||||
return new RemoveAction(domainObject);
|
||||
}, { priority: 200 });
|
||||
```
|
||||
|
||||
### 2. Use existing services
|
||||
|
||||
```js
|
||||
mct.actions.register(function (domainObject) {
|
||||
var dialogService = mct.ui.dialogServiceFactory();
|
||||
return new PropertiesAction(dialogService, domainObject);
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Add new service implementations
|
||||
|
||||
```js
|
||||
// Instance-style registration
|
||||
mct.persistenceServiceFactory.register(new LocalPersistenceService());
|
||||
|
||||
// Factory-style registration
|
||||
mct.persistenceServiceFactory.register(function () {
|
||||
var $http = angular.injector(['ng']).get('$http');
|
||||
return new LocalPersistenceService($http);
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Decorate service implementations
|
||||
|
||||
```js
|
||||
mct.modelServiceFactory.decorate(function (modelService) {
|
||||
return new CachingModelDecorator(modelService);
|
||||
}, { priority: 100 });
|
||||
```
|
||||
|
||||
### 5. Decorate extension instances
|
||||
|
||||
```js
|
||||
mct.capabilities.decorate(function (capabilities) {
|
||||
return capabilities.map(decorateIfApplicable);
|
||||
});
|
||||
```
|
||||
|
||||
This use case is not well-supported by these API changes. The most
|
||||
common case for decoration is capabilities, which are under reconsideration;
|
||||
should consider handling decoration of capabilities in a different way.
|
||||
|
||||
### 6. Add new types of services
|
||||
|
||||
```js
|
||||
myModule.myServiceFactory = new mct.CompositeServiceFactory();
|
||||
|
||||
// In cases where a custom composition strategy is desired
|
||||
myModule.myServiceFactory.composite(function (services) {
|
||||
return new MyServiceCompositor(services);
|
||||
});
|
||||
```
|
||||
|
||||
### 7. Add new extension categories.
|
||||
|
||||
```js
|
||||
myModule.hamburgers = new mct.ExtensionRegistry();
|
||||
```
|
||||
|
||||
## Evaluation
|
||||
|
||||
### Benefits
|
||||
|
||||
* Encourages separation of registration from declaration (individual
|
||||
components are decoupled from the manner in which they are added
|
||||
to the architecture.)
|
||||
* Minimizes "magic." Dependencies are acquired, managed, and exposed
|
||||
using plain-old-JavaScript without any dependency injector present
|
||||
to obfuscate what is happening.
|
||||
* Offers comparable expressive power to existing APIs; can still
|
||||
extend the behavior of platform components in a variety of ways.
|
||||
* Does not force or limit formalisms to use;
|
||||
|
||||
### Detriments
|
||||
|
||||
* Does not encourage separation of dependency acquisition from
|
||||
declaration; that is, it would be quite natural using this API
|
||||
to acquire references to services during the constructor call
|
||||
to an extension or service. But, passing these in as constructor
|
||||
arguments is preferred (to separate implementation from architecture.)
|
||||
* Adds (negligible?) boilerplate relative to declarative syntax.
|
||||
|
Loading…
x
Reference in New Issue
Block a user