# 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