Extending the current interface
Now consider a scenario where the vendor decides to
provide an additional utility called
FindN. This will find a character at N position.
One of the very initial option that one can think of is
to extend the current interface. To add one more function in the interface
definition.
class IFastString {
public:
// faux version 1.0
virtual void Delete(void) = 0;
virtual int Length(void) = 0;
virtual int Find(const char *psz) = 0;
// faux version 2.0
virtual int
FindN(const char *psz, int n)
} ;
This will work as long we do not change the sequence of
the earlier definition and keep the new definition at the bottom. The new
application will crash with the old DLL, because when it calls the FindN it
will crash.
Also, this approach defies
the encapsulation by modifying the public interface.
Exposing Multiple Interfaces
The most reasonable way to manage these kind of updates is to expose multiple interfaces. Consider a scenario where you might want to expose Save and Load functionality. You can very well go ahead and add it in the IFastString interface, but that would make much less sense as there might be other interfaces which need Save/Load other than FastString. The best way to do this is by introducing a new interface IPersistent interface.
class IPersistentObject {
public:
virtual void Delete(void) = 0;
virtual bool Load(const char *pszFileName) 0;
virtual bool Save(const char *pszFileName) 0;
} ;
class IFastString {
public:
virtual void Delete(void) = 0;
virtual int Length(void) const = 0;
virtual int Find(const char *psz) const 0;
} ;
class FastString : public IFastString,public IPersistentObject
{
int m_cch; // count of characters
char ";"'m_psz;
public:
FastString(const char *psz);
~FastString(void);
//Common methods
void Delete(void); II deletes this instance
//IFastString methods
int Length(void) canst; II returns # of characters
int Find(const char *psz) canst; II returns offset
//IPersistentObject methods
bool Load(const char *pszFileName);
bool Save(const char *pszFileName);
} ;
The FastString will now compromise of two virtual pointer for each interface. We can link to the desired interface using RTTI. Using dynamic_cast we can link to the requested interface of the object.
The dynamic_cast in C++ helps to determine if an object is in-fact inherited by any base class. If the Interface is not supported by the class NULL will be returned.
Now to access the interfaces , client must code something like :
bool SaveString(IFastString *pfs, canst char *pszFN){
bool bResult = false;
IPersistentObject *ppo =
dynamic_cast<IPersistentObject*>(pfs);
if (ppo)
bResult = ppo->Save(pszFN);
return bResult;
}
This will work fine without any issue. BUT the use of RTTI mechanism by Client is a blocking factor because the RTTI is intensely compiler dependent. This brings us back to the same problem again. Each vendor has very complex way of implementing the RTTI.
Simulate RTTI
The most effective alternative is to expose a functionality from each interface which can be then implemented by class to mimic dynamic_cast.
class IExtensibleObject {
public:
virtual void *Dynamic_Cast(const char* pszType) =0;
virtual void Delete(void) = 0;
} ;
class IPersistentObject : public IExtensibleObject {
public:
virtual bool Load(const char *pszFileName) 0;
virtual bool Save(const char *pszFileName) 0;
} ;
class IFastString : public IExtensibleObject {
public:
virtual int Length(void) = 0;
virtual int Find(const char *psz) 0;
} ;
It made more sense to move the new function Dynamic_Cast and Delete to another base class , as these functionality is desired both by IFastString and IPersistentObject .
The client now perform casting in the following way:
bool SaveString(IFastString *pfs, const char *pszFN){
bool bResult = false;
IPersistentObject *ppo = (IPersistentObject*)
pfs->Dynamic_Cast("IPersistentObject");
if (ppo)
bResult = ppo->Save(pszFN);
return bResult;
}
Now in the DLL , since we have the implementing class deriving from all the Interfaces we can use static_cast instead.
void *FastString: :Dynamic_Cast(const char *pszType) {
if (strcmp(pszType, "IFastString") == 0)
return static_cast<IFastString*>(this);
else if (strcmp(pszType, "IPersistentObject") 0)
return static_cast<IPersistentObject*>(this);
else if (strcmp(pszType, "IExtensibleObject") 0)
return static_cast<IFastString*>(this);
else
return 0; // request for unsupported interface
}
Note: When asked for IExtensibleObject , IFastString is returned because IExtensibleObject is present in both IFastString and IPersistentObject making it ambiguous.
No comments:
Post a Comment