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 | } |