| # The Associated Data Pattern | |
| The associated data pattern allows a class C that is used by multiple other | |
| classes *outside* its own module (which don't control or know the lifetime of C) | |
| to store data that is logically "per instance of C". This document will call | |
| those other classes the "consumer classes" of C. | |
| Imagine that you have a class Pokemon, which is used by two other classes, | |
| PokemonGroomer and PokemonNurse. A PokemonGroomer wishes to store the last time | |
| a given Pokemon got a bath, and a PokemonNurse wishes to store the last time a | |
| given Pokemon got a checkup. | |
| You might implement that this way: | |
| class Pokemon { | |
| ... | |
| Time last_bath_; | |
| Time last_checkup_; | |
| ... | |
| }; | |
| bool PokemonNurse::NeedsCheckup(Pokemon* p) { | |
| return Time::Now() - p->last_checkup_ > kCheckupInterval; | |
| } | |
| but then Pokemon ends up containing (and being responsible for enforcing | |
| invariants on) data that is only for the use of other objects, which might live | |
| in entirely separate modules. | |
| You might instead do this: | |
| class PokemonNurse { | |
| ... | |
| map<Pokemon*, Time> last_checkup_; | |
| ... | |
| }; | |
| bool PokemonNurse::NeedsCheckup(Pokemon* p) { | |
| if (!last_checkup_.contains(p)) | |
| last_checkup_[p] = 0; | |
| return Time::Now() - last_checkup_[p] > kCheckupInterval; | |
| } | |
| but another problem appears: PokemonNurse has to know when Pokemon are released | |
| into the wild so it can clean up the map, which adds extra bookkeeping. | |
| The associated data pattern would look like this: | |
| class Pokemon { | |
| ... | |
| struct Data { | |
| virtual ~Data(); | |
| }; | |
| map<Key, unique_ptr<Data>> user_data_; | |
| ... | |
| }; | |
| class PokemonNurse::PokemonData : public Pokemon::Data { | |
| Time last_checkup_; | |
| }; | |
| bool PokemonNurse::NeedsCheckup(Pokemon* p) { | |
| if (!p->user_data_[PokemonNurse::kDataKey]) | |
| p->user_data_[PokemonNurse::kDataKey] = make_unique<PokemonData>(); | |
| return Time::Now() - p->user_data_[PokemonNurse::kDataKey].last_checkup_ | |
| > kCheckupInterval; | |
| } | |
| This way, Pokemon manages the *lifetime* of the per-Pokemon data, but | |
| PokemonNurse manages the *invariants* of the data, and only PokemonNurse is | |
| aware of the per-Pokemon data belonging to PokemonNurse. | |
| ## Use this pattern when: | |
| * You have a central class C, and many places in your code want to store some | |
| data for each instance of C. | |
| * C can't directly contain your data for layering or code structure reasons. | |
| * The data being stored is ephemeral and your consumer classes don't care | |
| about its lifetime. | |
| ## Don't use this pattern when: | |
| * The data being stored must expose any behavior other than a destructor to | |
| the central class; in that case, you need a richer pattern than this one. | |
| * The data being stored has a complex lifetime. | |
| * Deleting the data being stored has side effects. | |
| * There might be a very large amount (thousands) of associated data entries | |
| for a particular client class; in that case, a domain-specific data model | |
| will provide better efficiency. | |
| * The central class C and the consumer classes are close enough that it would | |
| make sense for C to store the data directly. | |
| ## Alternatives / See also: | |
| * Storing data for consumer classes directly on the object of class C. | |
| * Having the consumer store a map between objects of class C and the | |
| consumer's C-specific data. | |
| ## How to use this pattern in Chromium: | |
| The two most commonly-used instances of this pattern in Chromium are | |
| [SupportsUserData] (especially [WebContentsUserData] and [WebStateUserData]) and | |
| [KeyedService] (usually via [BrowserContextKeyedServiceFactory] and | |
| [BrowserStateKeyedServiceFactory]). | |
| ### SupportsUserData | |
| SupportsUserData is a mixin-type class that allows consumer classes to stash | |
| data on the class with the mixin. It exposes a very small interface | |
| `SupportsUserData::Data` which stored data items implement (in fact, any class | |
| with a virtual destructor already implements it), and adds a handful of new | |
| methods to the class with the mixin: | |
| Data* GetUserData(const void* key); | |
| void SetUserData(const void* key, std::unique_ptr<Data> data); | |
| void RemoveUserData(const void* key); | |
| For example, in //net, URLRequest inherits from SupportsUserData, so if you were | |
| running a RequestNicenessService that wanted to annotate URLRequests with a | |
| niceness value at one point and use that value later, you might do: | |
| class RequestNicenessService { | |
| static char kRequestDataKey; | |
| void SetRequestNiceness(net::URLRequest* request, int niceness); | |
| int GetRequestNiceness(net::URLRequest* request); | |
| }; | |
| struct NicenessData : public SupportsUserData::Data { | |
| int niceness; | |
| }; | |
| void RequestNicenessService::SetRequestNiceness(...) { | |
| request->SetUserData(&kRequestDataKey, | |
| make_unique<NicenessData>(niceness)); | |
| } | |
| int RequestNicenessService::GetRequestNiceness(...) { | |
| SupportsUserData::Data* data = request->GetUserData(&kRequestDataKey); | |
| if (data) | |
| return static_cast<NicenessData*>(data)->niceness; | |
| return -1; | |
| } | |
| Or, if there might be multiple RequestNicenessService instances, you could use | |
| the address of the RequestNicenessService instance itself as the key for the | |
| UserData on the URLRequest. That's actually a more common pattern, but for a | |
| singleton instance using the address of a static is also fine. | |
| ### WebContentsUserData & TabHelper | |
| For the specific very common case of wanting to attach a SupportsUserData::Data | |
| to a WebContents, the helper class [WebContentsUserData] exists. This templated | |
| class handles casting to and from your concrete type for you. It is commonly | |
| used with the related [TabHelper] pattern. | |
| ### KeyedService & BrowserContextKeyedServiceFactory | |
| KeyedService is an abstract interface that implements this pattern, but with an | |
| additional notion of dependencies between different pieces of associated data. | |
| It is usually concretely used via BrowserContextKeyedServiceFactory, by | |
| subclassing BrowserContextKeyedServiceFactory and overriding the | |
| `BuildServiceInstanceFor` method. Since a Profile is a BrowserContext, this | |
| provides a way to store data associated with a Profile. For example, if you | |
| were creating a UserNicenessController, you might do: | |
| class UserNicenessData : public KeyedService { | |
| public: | |
| static UserNicenessData* FromProfile(Profile* profile); | |
| int niceness; | |
| // Maybe also: | |
| // void Shutdown() override; | |
| // if you need to participate in KeyedService's two-phase destruction | |
| // protocol. | |
| }; | |
| class UserNicenessController : | |
| public BrowserContextKeyedServiceFactory { | |
| ... | |
| }; | |
| // static | |
| UserNicenessData* | |
| UserNicenessData::FromProfile(Profile* profile) { | |
| return static_cast<UserNicenessData*>( | |
| UserNicenessController::GetInstance()-> | |
| GetServiceForBrowserContext(profile, true)); | |
| } | |
| // in code somewhere: | |
| UserNicenessData::FromProfile(profile)->niceness++; | |
| Any code that needs the UserNicenessData for a given Profile can call | |
| `UserNicenessData::FromProfile`, and KeyedService and | |
| BrowserContextKeyedServiceFactory will handle creation and lifetime as needed, | |
| as well as handling any dependencies UserNicenessData might declare on other | |
| services. | |
| Note that the naming of KeyedService **implies** that it is intended for | |
| services, as does the presence of a dependency mechanism, but the same approach | |
| can be used to store plain data, as in this example. | |
| Another note: Since `Profile` already SupportsUserData, if you simply want to | |
| store data on the profile, it is easier to do it that way rather than use | |
| KeyedService. | |
| A third note: on iOS, use [BrowserStateKeyedServiceFactory] instead, which | |
| attaches a KeyedService to a [BrowserState]. | |
| A fourth note: on Android, C++ keyed services often have a corresponding Java | |
| object. The C++ part should own the Java one, as detailed in [JavaCppOwnership]. | |
| [BrowserContextKeyedServiceFactory]: https://chromium.googlesource.com/chromium/src/+/HEAD/components/keyed_service/content/browser_context_keyed_service_factory.h | |
| [BrowserStateKeyedServiceFactory]: https://chromium.googlesource.com/chromium/src/+/HEAD/components/keyed_service/ios/browser_state_keyed_service_factory.h | |
| [BrowserState]: https://chromium.googlesource.com/chromium/src/+/HEAD/ios/web/public/browser_state.h | |
| [JavaCppOwnership]: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/android_jni_ownership_best_practices.md | |
| [KeyedService]: https://chromium.googlesource.com/chromium/src/+/HEAD/components/keyed_service/core/keyed_service.h | |
| [SupportsUserData]: https://chromium.googlesource.com/chromium/src/+/HEAD/base/supports_user_data.h | |
| [TabHelper]: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/tab_helpers.md | |
| [WebContentsUserData]: https://chromium.googlesource.com/chromium/src/+/HEAD/content/public/browser/web_contents_user_data.h | |
| [WebStateUserData]: https://chromium.googlesource.com/chromium/src/+/HEAD/ios/web/public/web_state_user_data.h |