View difference between Paste ID: vZjBXLwi and LL2Gv71g
SHOW: | | - or go back to the newest paste.
1
/**
2
 * A few examples that illustrate several styles to chain four events/callbacks.
3
 * 
4
 * This class compiles, but intentionally doesn't execute, because I'm skipping the
5
 * implementation for the Caller and Bus interfaces.
6
 * 
7
 * @author Chris Lercher
8
 */
9
public class CallbackStyleExamples {
10
11
  interface Caller {
12
    public void call(final String arg, final Callback callback);
13
  }
14
  
15
  interface Callback {
16
    void onSuccess();
17
  }
18
  
19
  enum BusEvent {
20
    ALICE_SUCCESSFUL_EVENT,
21
    BOB_SUCCESSFUL_EVENT,
22
    CHARLIE_SUCCESSFUL_EVENT,
23
    DAISY_SUCCESSFUL_EVENT,
24
  }
25
  
26
  interface BusEventListener {
27
    void onEvent(final BusEvent busEvent);
28
  }
29
  
30
  interface Bus {
31
    void addEventListener(final BusEvent busEvent, final BusEventListener busEventListener);
32
    void fireEvent(final BusEvent busEvent);
33
  }
34
  
35
  Caller alice;
36
  Caller bob;
37
  Caller charlie;
38
  Caller daisy;
39
  
40
  Bus bus;
41
  
42
  private CallbackStyleExamples() { // avoid actual instantiation of this test ;)
43
  }
44
  
45
  /**
46
   * Nested callbacks can be very hard to read if you nest them too deeply.
47
   */
48
  public void nestedCallbacksExample() {
49
    
50
    alice.call("a", new Callback() {
51
      
52
      @Override
53
      public void onSuccess() {
54
        bob.call("b", new Callback() {
55
          
56
          @Override
57
          public void onSuccess() {
58
            charlie.call("c", new Callback() {
59
              
60
              @Override
61
              public void onSuccess() {
62
                daisy.call("d", new Callback() {
63
                  
64
                  @Override
65
                  public void onSuccess() {
66
                    // finished
67
                  }
68
                  
69
                });
70
              }
71
              
72
            });
73
          }
74
          
75
        });
76
      }
77
      
78
    });
79
    
80
  }
81
  
82
  /**
83
   * You can use your IDE to refactor this easily into named callbacks.
84
   * 
85
   * - Problem: The control flow is not so immediately obvious anymore.
86
   * - Still, an IDE can help by using "Search References" (Ctrl-G in Eclipse)
87
   *   or something similar.
88
   */
89
  public void namedCallbacksExample() {
90
    
91
    final Callback daisyCallback = new Callback() {
92
      
93
      @Override
94
      public void onSuccess() {
95
        // finished
96
      }
97
    };
98
    
99
    final Callback charlieCallback = new Callback() {
100
      
101
      @Override
102
      public void onSuccess() {
103
        daisy.call("d", daisyCallback);
104
      }
105
    };
106
    
107
    final Callback bobCallback = new Callback() {
108
      
109
      @Override
110
      public void onSuccess() {
111
        charlie.call("c", charlieCallback);
112
      }
113
    };
114
    
115
    final Callback aliceCallback = new Callback() {
116
      
117
      @Override
118
      public void onSuccess() {
119
        bob.call("b", bobCallback);
120
      }
121
    };
122
    
123
    alice.call("a", aliceCallback);
124
  }
125
  
126
  /**
127
   * This is how it looks when using an event bus (or Observer/Publish-Subscribe pattern).
128
   * 
129
   * - Under the right circumstances (when it's very clear what each event means, and
130
   *   if you don't have too many), this pattern can make things very nice and clear.
131
   * - But in other cases, it can make the control flow more confusing (and you easily get twice the lines of code).
132
   * - It's harder to use your IDE to find out about the control flow.
133
   * - The GWT History mechanism is a very positive example for where to use this technique reasonably.
134
   */
135
  public void eventBusExample() {
136
    
137
    class FireEventCallback implements Callback {
138
139
      private final BusEvent eventToFire;
140
141-
        bus.fireEvent(BusEvent.ALICE_SUCCESSFUL_EVENT);
141+
      public FireEventCallback(BusEvent eventToFire) {
142
        this.eventToFire = eventToFire;
143
      }
144
      
145
      @Override
146
      public void onSuccess() {
147
        bus.fireEvent(eventToFire);
148
      }
149
      
150
    }
151
    
152
    alice.call("a", new FireEventCallback(BusEvent.ALICE_SUCCESSFUL_EVENT));
153-
            bus.fireEvent(BusEvent.BOB_SUCCESSFUL_EVENT);
153+
154
      
155
      @Override
156
      public void onEvent(BusEvent busEvent) {
157
        bob.call("b", new FireEventCallback(BusEvent.BOB_SUCCESSFUL_EVENT));
158
      }
159
    });
160
    bus.addEventListener(BusEvent.BOB_SUCCESSFUL_EVENT, new BusEventListener() {
161
      
162
      @Override
163
      public void onEvent(BusEvent busEvent) {
164-
        charlie.call("c", new Callback() {
164+
        charlie.call("c", new FireEventCallback(BusEvent.CHARLIE_SUCCESSFUL_EVENT));
165
      }
166
    });
167
    bus.addEventListener(BusEvent.CHARLIE_SUCCESSFUL_EVENT, new BusEventListener() {
168-
            bus.fireEvent(BusEvent.CHARLIE_SUCCESSFUL_EVENT);
168+
169
      @Override
170
      public void onEvent(BusEvent busEvent) {
171
        daisy.call("d", new FireEventCallback(BusEvent.DAISY_SUCCESSFUL_EVENT));
172
      }
173
    });
174
    
175
    bus.addEventListener(BusEvent.DAISY_SUCCESSFUL_EVENT, new BusEventListener() {
176
      
177
      @Override
178
      public void onEvent(final BusEvent busEvent) {
179
        // finished
180
      }
181
    });
182
  }
183-
            bus.fireEvent(BusEvent.DAISY_SUCCESSFUL_EVENT);
183+
184
  /**
185
   * In my experience, it's often a good idea to "divide and conquer"
186
   * by mixing nesting and named callbacks.
187
   * 
188
   * Depending on the situation, two nested callbacks are often still
189
   * readable, and they reduce jumping around between methods when
190
   * reading the code by 50%.
191
   */
192
  public void mixedExample() {
193
    
194
    final Callback charlieCallback = new Callback() {
195
      
196
      @Override
197
      public void onSuccess() {
198
        
199
        daisy.call("d", new Callback() {
200
              
201
          @Override
202
          public void onSuccess() {
203
            // finished
204
          }
205
        });
206
      }
207
    };
208
    
209
    alice.call("a", new Callback() {
210
      
211
      @Override
212
      public void onSuccess() {
213
        
214
        bob.call("b", new Callback() {
215
          
216
          @Override
217
          public void onSuccess() {
218
            
219
            charlie.call("c", charlieCallback);
220
          }
221
        });
222
      }
223
    });
224
  }
225
}