blob: 755235e7d8b80cd9080d7aa49edad8aca609bc2c [file] [log] [blame] [view]
Elly Fong-Jonesede232e62020-10-15 16:00:261## Inversion of Control
2
3"Inversion of control" is a design pattern used to allow users of a framework
4or library (often called clients) to customize the behavior of the framework.
5
6### Our Example
7
8Examples in this document will be given by extending or modifying this example
9API, which is hopefully self-explanatory:
10
Jan Wilken Dörried02749a2020-10-26 15:51:0311```cpp
12class StringKVStore {
13 public:
14 StringKVStore();
15 virtual ~StringKVStore();
Elly Fong-Jonesede232e62020-10-15 16:00:2616
Jan Wilken Dörried02749a2020-10-26 15:51:0317 using KeyPredicate = base::RepeatingCallback<bool(const string&)>;
Elly Fong-Jonesede232e62020-10-15 16:00:2618
Jan Wilken Dörried02749a2020-10-26 15:51:0319 void Put(const string& key, const string& value);
20 void Remove(const string& key);
21 void Clear();
Elly Fong-Jonesede232e62020-10-15 16:00:2622
Jan Wilken Dörried02749a2020-10-26 15:51:0323 string Get(const string& key) const;
24 set<string> GetKeys() const;
25 set<string> GetKeysMatching(const KeyPredicate& predicate) const;
Elly Fong-Jonesede232e62020-10-15 16:00:2626
Jan Wilken Dörried02749a2020-10-26 15:51:0327 void SaveToPersistentStore();
28};
29```
Elly Fong-Jonesede232e62020-10-15 16:00:2630
31### What is inversion of control?
32
33Normally, client code calls into the library to do operations, so control flows
34from a high-level class to a low-level class:
35
Jan Wilken Dörried02749a2020-10-26 15:51:0336```cpp
37void YourFunction() {
38 // GetKeys() calls into the StringKVStore library
39 for (const auto& key : kv_store_.GetKeys()) {
40 ...
41 }
42}
43```
Elly Fong-Jonesede232e62020-10-15 16:00:2644
45In "inverted" control flow, the library calls back into your code after you
46call into it, so control flows back from a low-level class to a high-level
47class:
48
Jan Wilken Dörried02749a2020-10-26 15:51:0349```cpp
50bool IsKeyInteresting(const string& key) { ... }
Elly Fong-Jonesede232e62020-10-15 16:00:2651
Jan Wilken Dörried02749a2020-10-26 15:51:0352void 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-Jonesede232e62020-10-15 16:00:2662
63It is also often inverted in the Chromium dependency sense. For example, in
64Chromium, code in //content can't call, link against, or generally be aware of
65code in //chrome - the normal flow of data and control is only in one direction,
66from //chrome "down" to //content. When //content calls back into //chrome, that
67is an inversion of control.
68
69Abstractly, inversion of control is defined by a low-level class defining an
70interface that a high-level class supplies an implementation of. In the example
71fragment given above, `StringKVStore` defines an interface called
72`StringKVStore::KeyPredicate`, and `YourFunction` supplies an implementation of
73that interface - namely the bound instance of `IsKeyInteresting`. This allows
74the low-level class to use functionality of the high-level class without being
75aware of the specific high-level class's existence, or a high-level class to
76plug logic into a low-level class.
77
78There 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
86for solving specific problems, but in general it is overused in Chromium.**
87
88### Callbacks
89
90Callbacks are one of the simplest ways to do inversion of control, and often are
91all you need. Callbacks can be used to split out part of the framework's logic
92into the client, like so:
93
Jan Wilken Dörried02749a2020-10-26 15:51:0394```cpp
95void 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-Jonesede232e62020-10-15 16:00:26104
105where `predicate` was supplied by the client of
106`StringKVStore::GetKeysMatching`. They can also be used for the framework
107library to notify clients of events, like so:
108
Jan Wilken Dörried02749a2020-10-26 15:51:03109```cpp
110void 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-Jonesede232e62020-10-15 16:00:26118
119making use of [Subscription].
120
121Callbacks can also be used to supply an implementation of something deliberately
122omitted, like so:
123
Jan Wilken Dörried02749a2020-10-26 15:51:03124```cpp
125class StringKVStore {
126 using SaveCallback = base::RepeatingCallback<void(string, string)>;
127 void SaveToPersistentStore(const SaveCallback& callback);
128};
129```
Elly Fong-Jonesede232e62020-10-15 16:00:26130
131### Observers
132
133An "observer" receives notifications of events happening on an object. For
134example, an interface like this might exist:
135
Jan Wilken Dörried02749a2020-10-26 15:51:03136```cpp
137class 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-Jonesede232e62020-10-15 16:00:26149
150and then on the StringKVStore class:
151
Jan Wilken Dörried02749a2020-10-26 15:51:03152```cpp
153class StringKVStore {
154 public:
155 ...
156 void AddObserver(Observer* observer);
157 void RemoveObserver(Observer* observer);
158}
159```
Elly Fong-Jonesede232e62020-10-15 16:00:26160
161So an example of a `StringKVStore::Observer` might be:
162
Jan Wilken Dörried02749a2020-10-26 15:51:03163```cpp
164class 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-Jonesede232e62020-10-15 16:00:26181
182where the `StringKVStore` arranges to call the relevant method on each
183`StringKVStore::Observer` that has been added to it whenever a matching event
184happens.
185
186Use 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
194A listener is an observer that only observes a single type of event. These were
195very common in C++ and Java before the introduction of lambdas, but these days
196are not as commonly seen, and you probably should not introduce new listeners -
197instead, use a plain [Callback].
198
199Here's an example:
200
Jan Wilken Dörried02749a2020-10-26 15:51:03201```cpp
202class StringKVStore::ClearListener {
203 public:
204 virtual void OnCleared(StringKVStore* store) = 0;
205}
206```
Elly Fong-Jonesede232e62020-10-15 16:00:26207
208Use 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
215A delegate is responsible for implementing part of the framework that is
216deliberately missing. While observers and listeners are generally passive with
217respect to the framework object they are attached to, delegates are generally
218active.
219
220One very common use of delegates is to allow clients to make policy decisions,
221like so:
222
Jan Wilken Dörried02749a2020-10-26 15:51:03223```cpp
224class 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-Jonesede232e62020-10-15 16:00:26232
233Another common use is to allow clients to inject their own subclasses of
234framework objects that need to be constructed by the framework, by putting
235a factory method on the delegate:
236
Jan Wilken Dörried02749a2020-10-26 15:51:03237```cpp
238class StringKVStore::Delegate {
239 public:
240 virtual unique_ptr<StringKVStoreBackend>
241 CreateBackend(StringKVStore* store);
242}
243```
Elly Fong-Jonesede232e62020-10-15 16:00:26244
245And then these might exist:
246
Jan Wilken Dörried02749a2020-10-26 15:51:03247```cpp
248class MemoryBackedStringKVStoreDelegate : public StringKVStore::Delegate;
249class DiskBackedStringKVStoreDelegate : public StringKVStore::Delegate;
250...
251```
Elly Fong-Jonesede232e62020-10-15 16:00:26252
253Use 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
262If every call to the client could be made asynchronous and the API would still
263work fine for your use case, you have an observer or listener, not a delegate.
264
265If there might be multiple interested client objects instead of one, you have an
266observer, not a listener or delegate.
267
268If any method on your interface has any return type other than `void`, you have
269a delegate, not an observer or listener.
270
271You can think of it this way: an observer or listener interface *notifies* the
272observer or listener of a change to a framework object, while a delegate usually
273helps *cause* the change to the framework object.
274
275### Callbacks vs Observers/Listeners/Delegates
276
277Callbacks 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
295They 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
3031. 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
3092. 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
3143. 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
3224. 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
3305. 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
3376. 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
3477. 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
352Some key classes in `//base`:
353
Sigurdur Asgeirssonfb9a9f72021-05-20 20:45:17354* [base::ScopedObservation]
Elly Fong-Jonesede232e62020-10-15 16:00:26355* [ObserverList] and [CheckedObserver]
356* [Subscription] and [CallbackList]
357
358And 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
367Inverted control can be harder to reason about, and more expensive at runtime,
368than other approaches. In particular, beware of using delegates when static data
369would be appropriate. For example, consider this hypothetical interface:
370
Jan Wilken Dörried02749a2020-10-26 15:51:03371```cpp
372class StringKVStore::Delegate {
373 virtual bool ShouldSaveAtDestruction() { return true; }
374}
375```
Elly Fong-Jonesede232e62020-10-15 16:00:26376
377It should be clear from the naming that this method will only be called once per
378StringKVStore instance and that its value cannot meaningfully change within the
379lifetime of a given instance; in this case, "should save at destruction" should
380instead be a parameter given to StringKVStore directly.
381
382A 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
389should 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 Asgeirssonfb9a9f72021-05-20 20:45:17397[base::ScopedObservation]: ../../base/scoped_observation.h
Elly Fong-Jonesede232e62020-10-15 16:00:26398[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