announcements
play

Announcements Lecture 16 Callbacks and Observers Leah Perlmutter / - PowerPoint PPT Presentation

CSE 331 Software Design and Implementation Announcements Lecture 16 Callbacks and Observers Leah Perlmutter / Summer 2018 Announcements Quiz 6 due Thursday 8/2 Homework 7 due Thursday 8/2 Callbacks The limits of scaling Concept


  1. CSE 331 Software Design and Implementation Announcements Lecture 16 Callbacks and Observers Leah Perlmutter / Summer 2018 Announcements • Quiz 6 due Thursday 8/2 • Homework 7 due Thursday 8/2 Callbacks

  2. The limits of scaling Concept Overview Coupling – dependency between different parts What prevents us from building huge, • Use coupling only where necessary intricate structures that work perfectly and • Decouple needlessly coupled components indefinitely? Reusability – Not just friction • Uncoupled components are more reusable – Not just gravity Modularity – Not just wear-and-tear • The resulting design is modular because each component does its own functionality (no more, no less) … it’s the difficulty of managing complexity! Callbacks • The concept of passing in a method that will be called later • (to be illustrated soon) So we split designs into sensible parts and reduce interaction among the parts Today we will apply the concept of callbacks to decouple needlessly – More cohesion within parts coupled components! – Less coupling across parts Design exercise #1 Design exercise #1 Write a typing-break reminder program Write a typing-break reminder program Offer the hard-working user occasional reminders of the perils of Offer the hard-working user occasional reminders of the perils of Repetitive Strain Injury, and encourage the user to take a break Repetitive Strain Injury, and encourage the user to take a break from typing. from typing. Naive design: – Make a method to display messages and offer exercises – Make a loop to call that method from time to time (Let's ignore multithreaded solutions for this discussion)

  3. TimeToStretch suggests exercises Timer calls run() periodically public class Timer { public class TimeToStretch { private TimeToStretch tts = new TimeToStretch(); public void run() { public void start() { System.out.println("Stop typing!"); while (true) { suggestExercise(); ... } if (enoughTimeHasPassed) { public void suggestExercise() { tts.run(); … } } ... } } } } Main class puts it together Module dependency diagram (MDD) class Main { An arrow in a module dependency diagram (MDD) indicates “depends on” or “knows about” public static void main(String[] args) { – Simplistically: “any name mentioned in the source code” Timer t = new Timer(); t.start(); Main depends on Timer } Main } Timer This program, as designed, will work... TimeToStretch Timer depends But we can do better on TimeToStretch What’s wrong with this diagram? – Does Timer really need to depend on TimeToStretch ? – Is Timer re-usable in a new context?

  4. Decoupling TimeToStretch (version 2) Timer needs to call the run method public class TimeToStretch extends TimerTask { – Timer does not need to know what the run method does public void run() { System.out.println("Stop typing!"); Weaken the dependency of Timer on TimeToStretch suggestExercise(); – Introduce a weaker specification, in the form of an interface or abstract class } public abstract class TimerTask { public void suggestExercise() { public abstract void run(); ... } } Timer only needs to know that something (e.g., TimeToStretch ) } meets the TimerTask specification Timer (version 2) Module dependency diagram (version 2) public class Timer { • Timer depends on TimerTask , not TimeToStretch private TimerTask task; – Unaffected by implementation details of TimeToStretch public Timer(TimerTask task) { – Now Timer is much easier to reuse this.task = task; – Main depends on the constructor for TimeToStretch } public void start() { • Main still depends on Timer (is this necessary?) while (true) { ... task.run(); Main } } } TimerTask Timer Main creates a TimeToStretch object and passes it to Timer : Timer t = new Timer(new TimeToStretch()); Dependence TimeToStretch t.start(); Subclassing Pass timer task into timer

  5. Callbacks The callback design pattern Callback: “Code” provided by client to be used by library Going farther: use a callback to invert the dependency • In Java, pass an object with the “code” in a method TimeToStretch creates a Timer , and passes in a reference to itself Synchronous callbacks: so the Timer can call it back • Examples: HashMap calls its client’s hashCode , equals – This is a callback – a method call from a module to a client that • Useful when library needs the callback result immediately it notifies about some condition Asynchronous callbacks: The callback inverts a dependency • Examples: GUI listeners – Inverted dependency: TimeToStretch depends on Timer • Register to indicate interest and where to call back (not vice versa) • Useful when the callback should be performed later, when • Less obvious coding style, but more “natural” dependency some interesting event occurs – Side benefit: Main does not depend on Timer TimeToStretch (version 3) Main (version 3) public class TimeToStretch extends TimerTask { TimeToStretch tts = new TimeToStretch(); private Timer timer; Pass self into timer tts.start(); public TimeToStretch() { (“Registration”) – Uses a callback in TimeToStretch to invert a dependency timer = new Timer(this); } – This MDD shows the inversion of the dependency between public void start() { Timer and TimeToStretch (compare to version 1) timer.start(); Callback entry point } Main does not depend on Timer public void run() { Main TimeToStretch depends on Timer System.out.println("Stop typing!"); suggestExercise(); TimerTask Timer } ... } TimeToStretch

  6. Version 1 again For the sake of illustration • The dependency inversion would be more obvious to see if we had not first created TimerTask • After dependency inversion (without TimerTask): • Before dependency inversion: Main Main Timer Timer TimeToStretch TimeToStretch Main (version 3) Concept Summary (example 1) Coupling – dependency between different parts TimeToStretch tts = new TimeToStretch(); • Use coupling only where necessary tts.start(); • Decouple needlessly coupled components – Uses a callback in TimeToStretch to invert a dependency Reusability – This MDD shows the inversion of the dependency between • Uncoupled components are more reusable Timer and TimeToStretch (compare to version 1) Modularity • The resulting design is modular because each component does Main does not depend on Timer its own functionality (no more, no less) Main TimeToStretch depends on Timer Callbacks • The concept of passing in a method that will be called later TimerTask Timer We have applied the concept of callbacks to decouple needlessly coupled components! TimeToStretch

  7. Design exercise #2 A program to display information about stocks – Stock tickers – Spreadsheets Example 2 – Graphs Naive design: – Make a class to represent stock information – That class updates all views of that information (tickers, graphs, etc.) when it changes Module dependency diagram Weaken the coupling • Main class gathers information and stores in Stocks What should Stocks class know about viewers? • Stocks class updates viewers when necessary – Only needs an update method to call with changed data – Old way: Main void updateViewers() { ticker.update(newPrice); Stocks StockTicker spreadsheet.update(newPrice); graph.update(newPrice); Spreadsheet // Edit this method to // add a new viewer. L StockGraph } Problem: To add/change a viewer, must change Stocks Better: insulate Stocks from the details of the viewers

  8. Weaken the coupling The observer pattern What should Stocks class know about viewers? • Stocks not responsible for viewer creation – Only needs an update method to call with changed data • Main passes viewers to Stocks as observers – New way: The “observer pattern” • Stocks keeps list of PriceObservers , notifies them of changes interface PriceObserver { Create viewers and get observers void update(PriceInfo pi); Main } Create Stocks and PriceObserver class Stocks { add observers update private List<PriceObserver> observers; Stocks void addObserver(PriceObserver pi) { StockTicker observers.add(pi); Register a } Create (or be) callback Spreadsheet void notifyObserver(PriceInfo i) { observers for (PriceObserver obs : observers) StockGraph obs.update(i); Execute callbacks } • Issue: update method must pass enough information to … (unknown) viewers } CSE331 Fall 2015 30 A different design: pull versus push Concept Summary (example 2) • The Observer pattern implements push functionality Coupling – dependency between different parts A pull model: give viewers access to Stocks , let them extract the • • We decoupled Stocks from the viewer components data they need Reusability • Uncoupled components are more reusable new(Stocks) Main Modularity Stocks.new • The resulting design is modular because each component does its own functionality (no more, no less) Stocks Extensibility – ability to easily add new features StockTicker • (different from concept of extending a class to make subclass) • The application is more extensible now because we could add Spreadsheet more viewers without modifying Stocks StockGraph We used the Observer Pattern to improve the Stocks applicaiton! “Push” versus “pull” efficiency can depend on frequency of operations (Also possible to use both patterns simultaneously.)

Recommend


More recommend