Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 1 | ## Inversion of Control |
| 2 | |
| 3 | "Inversion of control" is a design pattern used to allow users of a framework |
| 4 | or library (often called clients) to customize the behavior of the framework. |
| 5 | |
| 6 | ### Our Example |
| 7 | |
| 8 | Examples in this document will be given by extending or modifying this example |
| 9 | API, which is hopefully self-explanatory: |
| 10 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 11 | ```cpp |
| 12 | class StringKVStore { |
| 13 | public: |
| 14 | StringKVStore(); |
| 15 | virtual ~StringKVStore(); |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 16 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 17 | using KeyPredicate = base::RepeatingCallback<bool(const string&)>; |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 18 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 19 | void Put(const string& key, const string& value); |
| 20 | void Remove(const string& key); |
| 21 | void Clear(); |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 22 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 23 | string Get(const string& key) const; |
| 24 | set<string> GetKeys() const; |
| 25 | set<string> GetKeysMatching(const KeyPredicate& predicate) const; |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 26 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 27 | void SaveToPersistentStore(); |
| 28 | }; |
| 29 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 30 | |
| 31 | ### What is inversion of control? |
| 32 | |
| 33 | Normally, client code calls into the library to do operations, so control flows |
| 34 | from a high-level class to a low-level class: |
| 35 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 36 | ```cpp |
| 37 | void YourFunction() { |
| 38 | // GetKeys() calls into the StringKVStore library |
| 39 | for (const auto& key : kv_store_.GetKeys()) { |
| 40 | ... |
| 41 | } |
| 42 | } |
| 43 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 44 | |
| 45 | In "inverted" control flow, the library calls back into your code after you |
| 46 | call into it, so control flows back from a low-level class to a high-level |
| 47 | class: |
| 48 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 49 | ```cpp |
| 50 | bool IsKeyInteresting(const string& key) { ... } |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 51 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 52 | void YourFunction() { |
| 53 | StringKVStore::KeyPredicate predicate = |
| 54 | base::BindRepeating(&IsKeyInteresting); |
| 55 | // GetKeysMatching() calls into the StringKVStore library, but it calls |
| 56 | // back into IsKeyInteresting defined in this file! |
| 57 | for (const auto& key : kv_store_.GetKeysMatching(predicate)) { |
| 58 | ... |
| 59 | } |
| 60 | } |
| 61 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 62 | |
| 63 | It is also often inverted in the Chromium dependency sense. For example, in |
| 64 | Chromium, code in //content can't call, link against, or generally be aware of |
| 65 | code in //chrome - the normal flow of data and control is only in one direction, |
| 66 | from //chrome "down" to //content. When //content calls back into //chrome, that |
| 67 | is an inversion of control. |
| 68 | |
| 69 | Abstractly, inversion of control is defined by a low-level class defining an |
| 70 | interface that a high-level class supplies an implementation of. In the example |
| 71 | fragment given above, `StringKVStore` defines an interface called |
| 72 | `StringKVStore::KeyPredicate`, and `YourFunction` supplies an implementation of |
| 73 | that interface - namely the bound instance of `IsKeyInteresting`. This allows |
| 74 | the low-level class to use functionality of the high-level class without being |
| 75 | aware of the specific high-level class's existence, or a high-level class to |
| 76 | plug logic into a low-level class. |
| 77 | |
| 78 | There are a few main ways this is done in Chromium: |
| 79 | |
| 80 | * Callbacks |
| 81 | * Observers |
| 82 | * Listeners |
| 83 | * Delegates |
| 84 | |
| 85 | **Inversion of control should not be your first resort. It is sometimes useful |
| 86 | for solving specific problems, but in general it is overused in Chromium.** |
| 87 | |
| 88 | ### Callbacks |
| 89 | |
| 90 | Callbacks are one of the simplest ways to do inversion of control, and often are |
| 91 | all you need. Callbacks can be used to split out part of the framework's logic |
| 92 | into the client, like so: |
| 93 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 94 | ```cpp |
| 95 | void StringKVStore::GetKeysMatching(const KeyPredicate& predicate) { |
| 96 | set<string> keys; |
| 97 | for (const auto& key : internal_keys()) { |
| 98 | if (predicate.Run(key)) |
| 99 | keys.insert(key); |
| 100 | } |
| 101 | return keys; |
| 102 | } |
| 103 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 104 | |
| 105 | where `predicate` was supplied by the client of |
| 106 | `StringKVStore::GetKeysMatching`. They can also be used for the framework |
| 107 | library to notify clients of events, like so: |
| 108 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 109 | ```cpp |
| 110 | void StringKVStore::Put(const string& key, const string& value) { |
| 111 | ... |
| 112 | // In real code you would use CallbackList instead, but for explanatory |
| 113 | // purposes: |
| 114 | for (const auto& callback : key_changed_callbacks_) |
| 115 | callback.Run(...); |
| 116 | } |
| 117 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 118 | |
| 119 | making use of [Subscription]. |
| 120 | |
| 121 | Callbacks can also be used to supply an implementation of something deliberately |
| 122 | omitted, like so: |
| 123 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 124 | ```cpp |
| 125 | class StringKVStore { |
| 126 | using SaveCallback = base::RepeatingCallback<void(string, string)>; |
| 127 | void SaveToPersistentStore(const SaveCallback& callback); |
| 128 | }; |
| 129 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 130 | |
| 131 | ### Observers |
| 132 | |
| 133 | An "observer" receives notifications of events happening on an object. For |
| 134 | example, an interface like this might exist: |
| 135 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 136 | ```cpp |
| 137 | class StringKVStore::Observer { |
| 138 | public: |
| 139 | virtual void OnKeyChanged(StringKVStore* store, |
| 140 | const string& key, |
| 141 | const string& from_value, |
| 142 | const string& to_value) {} |
| 143 | virtual void OnKeyRemoved(StringKVStore* store, |
| 144 | const string& key, |
| 145 | const string& old_value) {} |
| 146 | ... |
| 147 | } |
| 148 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 149 | |
| 150 | and then on the StringKVStore class: |
| 151 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 152 | ```cpp |
| 153 | class StringKVStore { |
| 154 | public: |
| 155 | ... |
| 156 | void AddObserver(Observer* observer); |
| 157 | void RemoveObserver(Observer* observer); |
| 158 | } |
| 159 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 160 | |
| 161 | So an example of a `StringKVStore::Observer` might be: |
| 162 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 163 | ```cpp |
| 164 | class HelloKeyWatcher : public StringKVStore::Observer { |
| 165 | public: |
| 166 | void OnKeyChanged(StringKVStore* store, |
| 167 | const string& key, |
| 168 | const string& from_value, |
| 169 | const string& to_value) override { |
| 170 | if (key == "hello") |
| 171 | ++hello_changes_; |
| 172 | } |
| 173 | void OnKeyRemoved(StringKVStore* store, |
| 174 | const string& key, |
| 175 | const string& old_value) override { |
| 176 | if (key == "hello") |
| 177 | hello_changes_ = 0; |
| 178 | } |
| 179 | } |
| 180 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 181 | |
| 182 | where the `StringKVStore` arranges to call the relevant method on each |
| 183 | `StringKVStore::Observer` that has been added to it whenever a matching event |
| 184 | happens. |
| 185 | |
| 186 | Use an observer when: |
| 187 | |
| 188 | * More than one client may care to listen to events happening |
| 189 | * Clients passively observe, but do not modify, the state of the framework |
| 190 | object being observed |
| 191 | |
| 192 | ### Listeners |
| 193 | |
| 194 | A listener is an observer that only observes a single type of event. These were |
| 195 | very common in C++ and Java before the introduction of lambdas, but these days |
| 196 | are not as commonly seen, and you probably should not introduce new listeners - |
| 197 | instead, use a plain [Callback]. |
| 198 | |
| 199 | Here's an example: |
| 200 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 201 | ```cpp |
| 202 | class StringKVStore::ClearListener { |
| 203 | public: |
| 204 | virtual void OnCleared(StringKVStore* store) = 0; |
| 205 | } |
| 206 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 207 | |
| 208 | Use a listener when: |
| 209 | |
| 210 | * There is only a single client listener instance at most per framework object |
| 211 | * There is only a single event being listened for |
| 212 | |
| 213 | ### Delegates |
| 214 | |
| 215 | A delegate is responsible for implementing part of the framework that is |
| 216 | deliberately missing. While observers and listeners are generally passive with |
| 217 | respect to the framework object they are attached to, delegates are generally |
| 218 | active. |
| 219 | |
| 220 | One very common use of delegates is to allow clients to make policy decisions, |
| 221 | like so: |
| 222 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 223 | ```cpp |
| 224 | class StringKVStore::Delegate { |
| 225 | public: |
| 226 | virtual bool ShouldPersistKey(StringKVStore* store, const string& key); |
| 227 | virtual bool IsValidValueForKey(StringKVStore* store, |
| 228 | const string& key, |
| 229 | const string& proposed_value); |
| 230 | }; |
| 231 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 232 | |
| 233 | Another common use is to allow clients to inject their own subclasses of |
| 234 | framework objects that need to be constructed by the framework, by putting |
| 235 | a factory method on the delegate: |
| 236 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 237 | ```cpp |
| 238 | class StringKVStore::Delegate { |
| 239 | public: |
| 240 | virtual unique_ptr<StringKVStoreBackend> |
| 241 | CreateBackend(StringKVStore* store); |
| 242 | } |
| 243 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 244 | |
| 245 | And then these might exist: |
| 246 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 247 | ```cpp |
| 248 | class MemoryBackedStringKVStoreDelegate : public StringKVStore::Delegate; |
| 249 | class DiskBackedStringKVStoreDelegate : public StringKVStore::Delegate; |
| 250 | ... |
| 251 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 252 | |
| 253 | Use a delegate when: |
| 254 | |
| 255 | * There needs to be logic that happens synchronously with what's happening in |
| 256 | the framework |
| 257 | * It does not make sense to have a decision made statically per instance of a |
| 258 | framework object |
| 259 | |
| 260 | ### Observer vs Listener vs Delegate |
| 261 | |
| 262 | If every call to the client could be made asynchronous and the API would still |
| 263 | work fine for your use case, you have an observer or listener, not a delegate. |
| 264 | |
| 265 | If there might be multiple interested client objects instead of one, you have an |
| 266 | observer, not a listener or delegate. |
| 267 | |
| 268 | If any method on your interface has any return type other than `void`, you have |
| 269 | a delegate, not an observer or listener. |
| 270 | |
| 271 | You can think of it this way: an observer or listener interface *notifies* the |
| 272 | observer or listener of a change to a framework object, while a delegate usually |
| 273 | helps *cause* the change to the framework object. |
| 274 | |
| 275 | ### Callbacks vs Observers/Listeners/Delegates |
| 276 | |
| 277 | Callbacks have advantages: |
| 278 | |
| 279 | * No separate interface is needed |
| 280 | * Header files for client classes are not cluttered with the interfaces or |
| 281 | methods from them |
| 282 | * Client methods don't need to use specific names, so the name-collision |
| 283 | problems above aren't present |
| 284 | * Client methods can be bound (using [Bind]) with any needed state, including |
| 285 | which object they are attached to, so there is no need to pass the framework |
| 286 | object of interest back into them |
| 287 | * The handler for an event is placed in object setup, rather than being implicit |
| 288 | in the presence of a separate method |
| 289 | * They sometimes save creation of "trampoline" methods that simply discard or |
| 290 | add extra parameters before invoking the real handling logic for an event |
| 291 | * Forwarding event handlers is a lot easier, since callbacks can easily be |
| 292 | passed around by themselves |
| 293 | * They avoid multiple inheritance |
| 294 | |
| 295 | They also have disadvantages: |
| 296 | |
| 297 | * They can lead to deeply-nested setup code |
| 298 | * Callback objects are heavyweight (performance and memory wise) compared to |
| 299 | virtual method calls |
| 300 | |
| 301 | ### Design Tips |
| 302 | |
| 303 | 1. Observers should have empty method bodies in the header, rather than having |
| 304 | their methods as pure virtuals. This has two benefits: client classes can |
| 305 | implement only the methods for events they care to observe, and it is |
| 306 | obvious from the header that the base observer methods do not need to be |
| 307 | called. |
| 308 | |
| 309 | 2. Similarly, delegates should have sensible base implementations of every |
| 310 | method whenever this is feasible, so that client classes (subclasses of the |
| 311 | delegate class) can concern themselves only with the parts that are |
| 312 | relevant to their use case. |
| 313 | |
| 314 | 3. When inverting control, always pass the framework object of interest back to |
| 315 | the observer/listener/delegate; that allows the client, if it wants to, to |
| 316 | reuse the same object as the observer/listener/delegate for multiple |
| 317 | framework objects. For example, if ButtonListener (given above) didn't pass |
| 318 | the button in, the same ButtonListener instance could not be used to listen |
| 319 | to two buttons simultaneously, since there would be no way to tell which |
| 320 | button received the click. |
| 321 | |
| 322 | 4. Large inversion-of-control interfaces should be split into smaller |
| 323 | interfaces when it makes sense to do so. One notorious Chromium example |
| 324 | is [WebContentsObserver], which observes dozens of different events. |
| 325 | Whenever *any* of these events happens, *every* registered |
| 326 | WebContentsObserver has to be notified, even though virtually none of them |
| 327 | might care about this specific event. Using smaller interfaces helps with |
| 328 | this problem and makes the intent of installing a specific observer clearer. |
| 329 | |
| 330 | 5. The framework class *should not* take ownership of observers or listeners. |
| 331 | For delegates the decision is less clear, but in general, err on the side of |
| 332 | not taking ownership of delegates either. It is common to hold raw pointers |
| 333 | to observers and listeners, and raw or weak pointers to delegates, with |
| 334 | lifetime issues managed via AddObserver/RemoveObserver or the helper classes |
| 335 | discussed below. |
| 336 | |
| 337 | 6. Depending on your application and how widely-used you expect your observer, |
| 338 | listener, or delegate to be, you should probably use names that are longer |
| 339 | and more specific than you might otherwise. This is because client classes |
| 340 | may be implementing multiple inversion-of-control interfaces, so it is |
| 341 | important that their method names not collide with each other. For example, |
| 342 | instead of having `PageObserver::OnLoadStarted`, you might have |
| 343 | `PageObserver::OnPageLoadStarted` to reduce the odds of an unpleasant |
| 344 | collision with `NetworkRequestObserver::OnLoadStarted` (or similar). Note |
| 345 | that callbacks entirely avoid this problem. |
| 346 | |
| 347 | 7. A callback is probably a better fit for what you're trying to do than one |
| 348 | of the other patterns given above! |
| 349 | |
| 350 | ### Inversion of Control in Chromium |
| 351 | |
| 352 | Some key classes in `//base`: |
| 353 | |
Sigurdur Asgeirsson | fb9a9f7 | 2021-05-20 20:45:17 | [diff] [blame] | 354 | * [base::ScopedObservation] |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 355 | * [ObserverList] and [CheckedObserver] |
| 356 | * [Subscription] and [CallbackList] |
| 357 | |
| 358 | And some production examples: |
| 359 | |
| 360 | * [WebContentsObserver] and [WebContentsDelegate] |
| 361 | * [BrowserListObserver] |
| 362 | * [URLRequestJobFactory::ProtocolHandler] |
| 363 | * [WidgetObserver] and [ViewObserver] |
| 364 | |
| 365 | ### When Not To Use This Pattern |
| 366 | |
| 367 | Inverted control can be harder to reason about, and more expensive at runtime, |
| 368 | than other approaches. In particular, beware of using delegates when static data |
| 369 | would be appropriate. For example, consider this hypothetical interface: |
| 370 | |
Jan Wilken Dörrie | d02749a | 2020-10-26 15:51:03 | [diff] [blame] | 371 | ```cpp |
| 372 | class StringKVStore::Delegate { |
| 373 | virtual bool ShouldSaveAtDestruction() { return true; } |
| 374 | } |
| 375 | ``` |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 376 | |
| 377 | It should be clear from the naming that this method will only be called once per |
| 378 | StringKVStore instance and that its value cannot meaningfully change within the |
| 379 | lifetime of a given instance; in this case, "should save at destruction" should |
| 380 | instead be a parameter given to StringKVStore directly. |
| 381 | |
| 382 | A good rule of thumb is that any method on a delegate that: |
| 383 | |
| 384 | * Will only be called once for a given framework object, or |
| 385 | * Has a value that can't meaningfully change for a given framework object, and |
| 386 | * Serves primarily to return that value, rather than doing some other work |
| 387 | like constructing a helper object |
| 388 | |
| 389 | should be a property on the framework object instead of a delegate method. |
| 390 | |
| 391 | [Bind]: ../../base/bind.h |
| 392 | [BrowserListObserver]: ../../chrome/browser/ui/browser_list_observer.h |
| 393 | [CallbackList]: ../../base/callback_list.h |
| 394 | [Callback]: ../../base/callback.h |
| 395 | [CheckedObserver]: ../../base/observer_list_types.h |
| 396 | [ObserverList]: ../../base/observer_list.h |
Sigurdur Asgeirsson | fb9a9f7 | 2021-05-20 20:45:17 | [diff] [blame] | 397 | [base::ScopedObservation]: ../../base/scoped_observation.h |
Elly Fong-Jones | ede232e6 | 2020-10-15 16:00:26 | [diff] [blame] | 398 | [Subscription]: ../../base/callback_list.h |
| 399 | [URLRequestJobFactory::ProtocolHandler]: ../../net/url_request/url_request_job_factory.h |
| 400 | [Unretained]: ../../base/bind.h |
| 401 | [ViewObserver]: ../../ui/views/view_observer.h |
| 402 | [WebContentsDelegate]: ../../content/public/browser/web_contents_delegate.h |
| 403 | [WebContentsObserver]: ../../content/public/browser/web_contents_observer.h |
| 404 | [WidgetObserver]: ../../ui/views/widget/widget_observer.h |