Description: Creating and Using Adapters, Components, and Protocols
Keywords: ref

### 1.1.3.1 Creating and Using Adapters, Components, and Protocols

Because the adaptation protocol is so simple and flexible, there are a few guidelines you should follow when using `adapt()` or creating `__conform__` and `__adapt__` methods, to ensure that adapted objects are as usable as unadapted objects.

First, adaptation should be idempotent. That is, if you `adapt()` an object to a protocol, and then `adapt()` the return value to the same protocol, the same object should be returned the second time. If you are using the `protocols` declaration API, it suffices to declare that instances of the adapter class provide the protocol they adapt to. That is, if an adapter class provides protocol P for objects of type X, then it should declare that it provides protocol P.

If you are not using the declaration API, but relying only upon your custom `__conform__` and `__adapt__` methods, you need to ensure that any adapters you return will return themselves when asked to support the protocol that they were returned as an adapter for.

Second, adaptation is not automatically symmetric. That is, if I have an object X that provides protocol P1, and I `adapt()` it to protocol P2, it is not guaranteed that I can `adapt()` the resulting object to P1 and receive the original object. Ideally, someone who defines an adapter function would also declare an inverse adapter function to ``unwrap'' an adapted object to its original identity. In practice, however, this can be complex, since the adapter might need some fairly global knowledge of the system to know when it is better to unwrap and rewrap, and when it is better to further wrap the existing wrapper.

Another issue that occurs with such wrapper-based adaptation, is that the wrapper does not have the same object identity as the base object, and may not hash or compare equal to it, either. Further, it is not guaranteed that subsequent calls to `adapt()` will yield the same wrapper object - in fact it's quite unlikely.

These characteristics of adapted objects can be easily dealt with, however, by following a few simple rules:

- Always `adapt()` from the ``original'' object you're supplied; avoid adapting adaptations.

- Always pass ``original'' objects to functions or methods that expect their input to support more than one protocol; only pass adapted objects to functions or methods that expect support for only one protocol.

- Always use ``original'' objects for equality or identity comparisons - or else ensure that callers know they will need to provide you with an equal or identical adapter. (One good way to document this, is to include the requirement in the definition of the interface or protocol that your system requires.)

In some respects, these rules are similar to dealing with objects in statically typed languages like Java. In Java, if one simply has an ``object'', it is not possible to perform operations specific to an interface, without first ``casting'' the object to that interface. But, the object that was ``cast'' can't be stored in the same variable that the ``object'' was in, because it is of a different type.