search

src/lab/util/Subject.js

1// Copyright 2016 Erik Neumann. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the 'License');
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an 'AS IS' BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15goog.provide('myphysicslab.lab.util.Subject');
16
17goog.require('myphysicslab.lab.util.Printable');
18
19goog.scope(function() {
20
21/** A Subject notifies its {@link myphysicslab.lab.util.Observer Observers} when
22something changes in the Subject. This can be a change in the value of a
23{@link myphysicslab.lab.util.Parameter Parameter}, or the occurrence of an
24{@link myphysicslab.lab.util.GenericEvent GenericEvent}. The Subject maintains a list
25of its Observers. An Observer is connected to the Subject via the {@link #addObserver}
26method, which is typically called by the Observer's constructor or the entity that
27creates the Observer.
28
29See also the myPhysicsLab documentation about
30[Subject, Observer, Parameter](Architecture.html#subjectobserverparameter).
31
32When a change occurs in the Subject, the {@link #broadcast} method should be called to
33inform all Observers of the change. For a Parameter, the 'setter' method of the Subject
34should call {@link #broadcastParameter} at the end of the setter method.
35
36To enable universal scripting of simulations, we need Parameters and SubjectEvents to
37have language independent names. Therefore `Parameter.getName()` and
38`SubjectEvent.getName()` return a language independent name which is derived from the
39English localized name by converting the English name to uppercase and replacing spaces
40and dashes by underscore. You can use the function
41{@link myphysicslab.lab.util.UtilityCore#toName} to convert an English name to the
42universal name. Or use `SubjectEvent.nameEquals()` which handles the conversion
43to language independent name.
44
45The Subject and Observer interfaces are an implementation of the well known
46[Observer design pattern](http://en.wikipedia.org/wiki/Observer_pattern).
47
48
49
50Example Scenario
51----------------
52
53This section shows an example usage of the Subject and Observer interfaces, along
54with Parameters and user interface controls.
55
56TO DO: ***ELASTICITY IS NO LONGER DEFINED ON RIGIDBODYSIM NEED TO UPDATE THIS***
57
58<img src="Subject_Observer_Parameters.svg" alt="Subject/Observer Relationships with Parameters" />
59
60The diagram shows a {@link myphysicslab.lab.engine2D.RigidBodySim RigidBodySim} as the
61Subject. It has a list of Parameters, including a Parameter representing the elasticity
62of the rigid bodies. The Subject also has a list of Observers, including a
63{@link myphysicslab.lab.controls.NumericControl NumericControl} which is connected to the elasticity
64{@link myphysicslab.lab.util.ParameterNumber ParameterNumber}. In its constructor, the
65NumericControl adds itself to the list of Observers by calling {@link #addObserver} on
66the Subject.
67
68Whenever the elasticity is changed, the Subject should notify each Observer by
69calling {@link #broadcast}. In this example only the NumericControl cares about the
70elasticity Parameter. The NumericControl will then call `ParameterNumber.getValue` to
71get the new value, and the ParameterNumber in turn finds the value by calling a
72certain 'getter' method on the Subject.
73
74If the user modifies the value of the NumericControl, the NumericControl will then
75call `ParameterNumber.setValue` to modify the value; the ParameterNumber in turn calls
76a certain 'setter' method on the Subject to change the value. This also causes the
77Subject to notify all Observers of this change by calling `Subject.broadcast`.
78
79This design is very decoupled. The Subject knows nothing about the NumericControl
80except that it is an Observer. The Parameter is entirely unaware of the
81NumericControl. The NumericControl only knows that the Parameter is of type
82ParameterNumber and that it has a Subject providing notification of changes.
83
84* @interface
85* @extends {myphysicslab.lab.util.Printable}
86*/
87myphysicslab.lab.util.Subject = function() {};
88var Subject = myphysicslab.lab.util.Subject;
89
90/** Adds the given Observer to the Subject's list of Observers, so that the
91Observer will be notified of changes in this Subject. Does nothing if the Observer
92is already on the list. An Observer may call `Subject.addObserver` during its
93`observe` method.
94@param {!myphysicslab.lab.util.Observer} observer the Observer to add to
95 list of Observers
96*/
97Subject.prototype.addObserver;
98
99/** Notifies all Observers that the Subject has changed by calling
100{@link myphysicslab.lab.util.Observer#observe} on each Observer.
101An Observer may call `Subject.addObserver` or `Subject.removeObserver` during its
102`observe` method.
103@param {!myphysicslab.lab.util.SubjectEvent} evt a SubjectEvent
104 with information relating to the change
105*/
106Subject.prototype.broadcast;
107
108/** Notifies all Observers that the Parameter with the given `name` has changed by
109calling {@link myphysicslab.lab.util.Observer#observe} on each Observer.
110@param {string} name the universal or English name of the Parameter that has changed
111@throws {Error} if there is no Parameter with the given name
112*/
113Subject.prototype.broadcastParameter;
114
115/** Return the language-independent name of this Subject for scripting purposes.
116@return {string} name the language-independent name of this Subject
117*/
118Subject.prototype.getName;
119
120/** Returns a copy of the list of Observers of this Subject.
121@return {!Array<!myphysicslab.lab.util.Observer>} a copy of the list of Observers of
122 this Subject.
123*/
124Subject.prototype.getObservers;
125
126/** Returns the Parameter with the given name.
127@param {string} name the language-independent or English name of the Parameter
128@return {!myphysicslab.lab.util.Parameter} the Parameter with the given name
129@throws {Error} if there is no Parameter with the given name
130*/
131Subject.prototype.getParameter;
132
133/** Returns a copy of the list of this Subject's available Parameters.
134@return {!Array<!myphysicslab.lab.util.Parameter>} a copy of the list of
135 available Parameters for this Subject
136*/
137Subject.prototype.getParameters;
138
139/** Returns the ParameterBoolean with the given name.
140@param {string} name the universal or English name of the ParameterBoolean
141@return {!myphysicslab.lab.util.ParameterBoolean} the ParameterBoolean with
142 the given name
143@throws {Error} if there is no ParameterBoolean with the given name
144*/
145Subject.prototype.getParameterBoolean;
146
147/** Returns the ParameterNumber with the given name.
148@param {string} name the universal or English name of the ParameterNumber
149@return {!myphysicslab.lab.util.ParameterNumber} the ParameterNumber with
150 the given name
151@throws {Error} if there is no ParameterNumber with the given name
152*/
153Subject.prototype.getParameterNumber;
154
155/** Returns the ParameterString with the given name.
156@param {string} name the universal or English name of the ParameterString
157@return {!myphysicslab.lab.util.ParameterString} the ParameterString with
158 the given name
159@throws {Error} if there is no ParameterString with the given name
160*/
161Subject.prototype.getParameterString;
162
163/** Removes the Observer from the Subject's list of Observers. An Observer may
164call `Subject.removeObserver` during its `observe` method.
165@param {!myphysicslab.lab.util.Observer} observer the Observer to
166 detach from list of Observers
167*/
168Subject.prototype.removeObserver;
169
170}); // goog.scope