Any object can, as it sees fit, implement multiple persistence models by implementing a combination of the four IPersist* interfaces. However, each of these interfaces is considered mutually exclusive in that a client will use only one of them, depending on the client's prioritization of each model.
Generally speaking, IPersistStorage is the most powerful of the four interfaces, so a client might ask for it first when attempting to save or reload an object. However, storage-based persistence is expensive, so a client might request IPersistStreamInit or IPersistStream, and then IPersistStorage. Or it can give IPersistFile preference over the others.
Regardless of the client's prioritization, it must use only one of the interfaces with any given instance of an object, although it is allowed to use a different interface with different instances. The object must enforce this rule by remembering which interface was used to initialize it. If IPersistStorage::InitNew or IPersistStorage::Load has been called, the object must fail any calls to IPersistStream[Init] and IPersistFile. If IPersistStream[Init]::Load or IPersistStreamInit::InitNew has been called, the object fails calls to IPersistStorage and IPersistFile, and so on.
Remember that an object implementing all of these interfaces can use them to implement each other regardless of what the client does. For example, an implementation of IPersistFile can open a compound file and pass the IStorage pointer to its own IPersistStorage implementation, which may in turn do little more than open streams and pass them to IPersistStream. In this way, objects can support as many persistence models as they like without having a lot of redundant code.
Furthermore, an object's implementations of IPersistStorage and IPersistStream[Init] are completely independent of how the client obtains IStorage and IStream pointers. The client might be passing pieces of a storage hierarchy created on an ILockBytes, or it might pass an IStream implemented on a piece of global memory or some other medium. The object cannot assume that the storage and stream elements refer to a portion of a compound file in which a stream is at least 512 bytes. A stream implemented on a database field may be only 10 bytes by default, so writing 500 bytes of information to it can potentially fail if there is no more memory or no more space in the storage medium. This is why IPersistStorage::InitNew should call IStream::SetSize after opening the stream. This is not necessary with IPersistStream[Init], however, because the client is responsible for preallocating any streams based on what objects return from IPersistStream::GetSizeMax.