The Observer Pattern Event-driven design will set you free 1 BDD-style Testing This is feature testing, not unit testing (yes, you have to do it this way on the project) 2 Example: Game of Life Asides: Gives actual inputs and outputs Progressive when-then clauses Follow BDD scenarios structure and content when forming a testcase Each Given, When, and Then will have its own method Given: initialization When: action to be tested (do the action) Then: check that action made
intended change Make the test sound like the scenario 3 Example: Game of Life, II Scenario 1: // Given a $width by $height game void gameIsRunning(int width, int height) { game = new Game(width, height); renderer = new StringRenderer(); game.registerObserver(renderer); } // When I toggle the cell at ($column, $row); void iToggleTheCellAt(int column, int row) { game.toggleCellAt(column, row); } // Then the grid should look like $grid void theGridShouldLookLike(String grid) { assertThat(renderer.asString(),
equalTo(grid)); } 4 @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestGameOfLifeScenario1 { Game game; Example: Game of Life, III Scenario 1: @BeforeClass public static void GivenA5by5Game() { gameIsRunning(5, 5); } @Test public void A_WhenIToggleTheCellAt2comma3() { iToggleTheCellAt(2, 3); theGridShouldLookLike(); } @Test public void B_WhenIToggleTheCellAt2comma4() {
iToggleTheCellAt(2, 4); theGridShouldLookLike(); } @Test public void C_WhenIToggleTheCellAt2comma3() { iToggleTheCellAt(2, 3); theGridShouldLookLike(); } } Ive asserted an alphabetic ordering of the tests (and named them thusly) so that the progression holds up Note how the test cases look and sound like the scenario More test cases can be written with same base methods
5 Observer pattern Event-driven programming, call-backs, listening, and all that 6 Event-Driven Software Rules Whats an event? Its a kind of broadcast message that can be heard by any object that chooses to listen A typical event message from an object is I changed! This enables objects who depend on that object to update themselves E.g., update the maps center when location changes; GPSs event message is, in essence, I changed! Android depends heavily on events Every sensor on phone sends events Like above: GPS periodically announces changes in location User interfaces run almost entirely on events
Every mouse motion, click, etc., But wait a second, Java doesnt have events! 7 Simulating Events with Callbacks Classic software technique (OS, games, OOD) If the map control wants to receive events from location manager, it registers with it conceptually: locationMgr.register(mapControl) Later, when location changes, locationMgr calls back mapControl (and all others who registered): conceptually (in locationMgr): foreach (LocListener L : Listeners) { L.changed(this); } This is how we implement events with methods Note: many details omitted, but forthcoming
8 Actual Example Android Code (yuck!) Just the registration part // Acquire reference to the Location Manager LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); // Create a callback that handles location updates LocationListener locationListener = new LocationListener() { public void onLocationChanged(Location location) { // Called when a new location is found by the network location provider. makeUseOfNewLocation(location); } }; // Register the callback with Location Manager to receive location updates locationManager.requestLocationUpdates( http://developer.android.com/guide/topics/location/strategies.html 9
Why go to all this trouble? Knows About We say component A knows about component B if A references an interface defined by B In example below, WeatherData knows about Weather Station and Display One, Two, & Three 10 Whats wrong with this design? A. A.update() update() for for each each Display Display should should take take only only the
the parameters parameters itit needs needs B. B.Will Will have have to to edit edit WeatherData WeatherData to to add add aa new new Display Display C. C.Will Will have have to to edit edit
WeatherData WeatherData to to add add aa new new weather weather measurement measurement Because WeatherData knows about each individual display, adding a display means that WeatherData has to be updated. Unfortunately, we anticipate adding more displays (e.g., more users) 11 We have a knows-about problem Because WeatherData knows about the individual displays, adding a new display requires changing WeatherData as well Violates our design goal of localizing change (Adding features shouldnt affect internals of a
class.) 12 WeatherData needs a way to yell, and others to hear Its design patterns, its gotta involve interfaces But have to handle 0, 1, 2, 3 displays Hey, these all kind of have the same interface! 13 Loop over interfaced objects for (weatherUpdateInterface : weatherUpdateInterfaces) weatherUpdateInterface.update(temp, humidity, pressure);
Now fully dynamic can add and delete any time, as many or few as we want But how does WeatherData get access to the weatherUpdateInterface objects to call? 14 Three interfaces (first two in the pattern) Since update() is kind of specific, Id call this WeatherObserver Interface for the thing being observed Not part of pattern, dont really need it 15 Refactored Design
also to call removeObserver 16 Class Diagram View 17 Coupling in Observer Coupling is the property that when one thing changes, another thing may have to change (they are coupled) Want loosely coupled designs Note that Observer is coupled to weather generically, and hence to WeatherData generally But the designer of WeatherData chose the Observer interface ForecastDisplay cant be
implemented until Observer has been worked out We have another pattern later that lets ForecastDisplay be preexisting Critical for using existing dials, etc. 18 Actual Example Android Code (yuck!) Just the registration part // Acquire reference to the Location Manager LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); make an object instance of class interface // Create a listener that handles location updates LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) { // Called when a new location is found by the network location provider. anonymous class makeUseOfNewLocation(location); declaration of } type LocationListener public void onStatusChanged(String provider, int status, Bundle extras) {} public void onProviderEnabled(String provider) {} public void onProviderDisabled(String provider) {} }; // Register the listener with the Location Manager to receive location updates locationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER, 0, 0, locationListener); http://developer.android.com/guide/topics/location/strategies.html 19
Android Loc Listener Class Diagram _____LocationManager_____ LocationListener Ls__________ requestLocationUpdates() <> ___LocationListener___ onStatusChanged() onProviderEnabled() onProviderDisabled( ) ________ _ onStatusChanged() onProviderEnabled() onProviderDisabled()
Breaks Breaks the the pattern pattern on on the the subject subject side side Theres Theres no no Subject Subject interface interface implemented implemented by by LocManager LocManager Makes Makes building building new new location location subjects subjects harder
harder But But Android Android really really only only needs needs one, one, so so why why bother? bother? (tradeoffs) (tradeoffs) Also, Also, concrete concrete LocListener LocListener doesnt doesnt have have ref ref to to subject subject to
to unregister unregister 20 Android also lets you do it HFDP way public class MainActivity extends Activity implements LocationListener { public void onLocationChanged(Location location) { // Called when new location is found by network location provider. makeUseOfNewLocation(location); } public void onStatusChanged(String provider, int status, Bundle extras) {} public void onProviderEnabled(String provider) {} public void onProviderDisabled(String provider) {} Now Now the the concrete concrete LocationListener
LocationListener has has aa name name Now Now its its the the outer outer class class that that is is LocationListener LocationListener Means Means it it has has reference reference to to LocationManager LocationManager to to unregister unregister itself
itself Still Still no no Subject Subject interface interface implemented implemented by by LocationManager LocationManager 21 Take-Aways Revisited lessons: Classes: bad; Interfaces: good Program to interfaces, not implementations Aggregate/compose, dont subclass New lessons: Advanced communication concepts can be emulated (callbacks: events with methods) Observer pattern is an elegant, reusable solution Use of interfaces ensures that (changeable)
concrete classes only refer to (stable) interfaces Gets right knows about relationship Localization of change (low coupling) 22