View difference between Paste ID: yNc9Cqtb and vZjBXLwi
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 {
137+
138
      
139-
      private final BusEvent eventToFire;
139+
140
      public void onSuccess() {
141-
      public FireEventCallback(BusEvent eventToFire) {
141+
        bus.fireEvent(BusEvent.ALICE_SUCCESSFUL_EVENT);
142-
        this.eventToFire = eventToFire;
142+
143
    });
144
    
145
    bus.addEventListener(BusEvent.ALICE_SUCCESSFUL_EVENT, new BusEventListener() {
146
      
147-
        bus.fireEvent(eventToFire);
147+
148
      public void onEvent(final BusEvent busEvent) {
149
        bob.call("b", new Callback() {
150-
    }
150+
151
          @Override
152-
    alice.call("a", new FireEventCallback(BusEvent.ALICE_SUCCESSFUL_EVENT));
152+
153
            bus.fireEvent(BusEvent.BOB_SUCCESSFUL_EVENT);
154
          }
155
        });
156-
      public void onEvent(BusEvent busEvent) {
156+
157-
        bob.call("b", new FireEventCallback(BusEvent.BOB_SUCCESSFUL_EVENT));
157+
158
    });
159
    
160
    bus.addEventListener(BusEvent.BOB_SUCCESSFUL_EVENT, new BusEventListener() {
161
      
162
      @Override
163-
      public void onEvent(BusEvent busEvent) {
163+
164-
        charlie.call("c", new FireEventCallback(BusEvent.CHARLIE_SUCCESSFUL_EVENT));
164+
        charlie.call("c", new Callback() {
165
          
166
          @Override
167
          public void onSuccess() {
168
            bus.fireEvent(BusEvent.CHARLIE_SUCCESSFUL_EVENT);
169
          }
170-
      public void onEvent(BusEvent busEvent) {
170+
171-
        daisy.call("d", new FireEventCallback(BusEvent.DAISY_SUCCESSFUL_EVENT));
171+
172
      }
173
    });
174
    
175
    bus.addEventListener(BusEvent.CHARLIE_SUCCESSFUL_EVENT, new BusEventListener() {
176
      
177
      @Override
178
      public void onEvent(final BusEvent busEvent) {
179
        daisy.call("d", new Callback() {
180
          
181
          @Override
182
          public void onSuccess() {
183
            bus.fireEvent(BusEvent.DAISY_SUCCESSFUL_EVENT);
184
          }
185
        });
186
        
187
      }
188
    });
189
    
190
    bus.addEventListener(BusEvent.DAISY_SUCCESSFUL_EVENT, new BusEventListener() {
191
      
192
      @Override
193
      public void onEvent(final BusEvent busEvent) {
194
        // finished
195
      }
196
    });
197
  }
198
  
199
  /**
200
   * In my experience, it's often a good idea to "divide and conquer"
201
   * by mixing nesting and named callbacks.
202
   * 
203
   * Depending on the situation, two nested callbacks are often still
204
   * readable, and they reduce jumping around between methods when
205
   * reading the code by 50%.
206
   */
207
  public void mixedExample() {
208
    
209
    final Callback charlieCallback = new Callback() {
210
      
211
      @Override
212
      public void onSuccess() {
213
        
214
        daisy.call("d", new Callback() {
215
              
216
          @Override
217
          public void onSuccess() {
218
            // finished
219
          }
220
        });
221
      }
222
    };
223
    
224
    alice.call("a", new Callback() {
225
      
226
      @Override
227
      public void onSuccess() {
228
        
229
        bob.call("b", new Callback() {
230
          
231
          @Override
232
          public void onSuccess() {
233
            
234
            charlie.call("c", charlieCallback);
235
          }
236
        });
237
      }
238
    });
239
  }
240
}