implicit vs explicit interfaces
What are the pros/cons of using implicit interfaces (Cases 2 and 3; templates) vs using explicit interfaces (Case 1; pointer to abstract class) in the following example?
Code that doesn't change:
class CoolClass
{
public:
virtual void doSomethingCool() = 0;
virtual void worthless() = 0;
};
class CoolA : public CoolClass
{
public:
virtual void doSomethingCool()
{ /* Do cool stuff that an A would do */ }
virtual void worthless()
{ /* Worthless, but must be implemented */ }
};
class CoolB : public CoolClass
{
public:
virtual void doSomethingCool()
{ /* Do cool stuff that a B would do */ }
virtual void worthless()
{ /* Worthless, but must be implemented */ }
};
Case 1: A non-templated class that takes a base-class pointer which provides an explicit interface:
class CoolClassUser
{
public:
void useCoolClass(CoolClass * coolClass)
{ coolClass.doSomethingCool(); }
};
int main()
{
CoolClass * c1 = new CoolA;
CoolClass * c2 = new CoolB;
CoolClassUser user;
user.useCoolClass(c1);
user.useCoolClass(c2);
return 0;
}
Case 2: A templated class whose template type provides an implicit interface:
template <typename T>
class CoolClassUser
{
public:
void useCoolClass(T * coolClass)
{ coolClass->doSomethingCool(); }
};
int main()
{
CoolClass * c1 = new CoolA;
CoolClass * c2 = new CoolB;
CoolClassUser<CoolClass> user;
user.useCoolClass(c1);
user.useCoolClass(c2);
return 0;
}
Case 3: A templated class whose template type provides an implicit interface (this time, not deriving from CoolClass:
class RandomClass
{
public:
void doSomethingCool()
{ /* Do cool stuff that a RandomClass would do */ }
// I don't have to implement worthless()! Na na na na na!
};
template <typename T>
class CoolClassUser
{
public:
void useCoolClass(T * coolClass)
{ coolClass->doSomethingCool(); }
};
int main()
{
RandomClass * c1 = new RandomClass;
RandomClass * c2 = new RandomClass;
CoolClassUser<RandomClass> user;
user.useCoolClass(c1);
user.useCoolClass(c2);
return 0;
}
Case 1 requires that the object being passed in to useCoolClass() be a child of CoolClass (and implement worthless()). Cases 2 and 3, on the other hand, will take any class that has a doSomethingCool() function.
If users of the code were always fine subclassing CoolClass, then Case 1 makes intuitive sense, since the CoolClassUser would always be expecting an implementation of a CoolClass. But assume this code will be part of an API framework, so I cannot predict if users will want to subclass CoolClass or roll their own class that has a doSomethingCool() function.
Some related posts:
https://stackoverflow.com/a/7264550/635125
https://stackoverflow.com/a/7264689/635125
https://stackoverflow.com/a/8009872/635125
Some considerations that came to my mind for why you could prefer the Case 1:
CoolClass
is not a pure interface, ie part of implementation is also inherited (though you might provide it for Case 2/3 too, eg in the form of a base class); CoolClassUser
implemented in a binary rather than a header (and that's not only protection but could also be code size, control of resources, centralized error handling etc.); Reasons why Case 2/3 might be preferrable:
worthless()
is now worth something, and start using it, in Case 2 you will get compile-time errors for classes where it's not implemented. In Case 1, nothing will remind you to implement these functions for real, except maybe run-time errors if you are (un)lucky. In some cases, it might be purely the matter of personal preferences, either yours or your users.
Keep in mind that in cases #2 and #3, you're depending on template parameters, which means that the coder at the time of the call will have to properly instantiate the template argument with the correct type. Depending on how the functions will be used, that could create some issues where you want to create an abstract interface for the user without them having to worry about the type of the object being passed around ... ie, a "handle" or some other pointer to a derived object that is using polymorphism to pass an object around from one API function to another. For instance:
class abstract_base_class;
abtract_base_class* get_handle();
void do_something_with_handle(abstract_base_class* handle);
void do_something_else_with_handle(abstract_base_class* handle);
//... more API functions
Now, your API framework can pass an object back to the user of your code, and they don't need to know what that object is ... they only need to know that it describes some type of interface, which you can of course publicly expose in a header somewhere. But they won't have to know anything about the "guts" of the object you've passed back to them. You can give them a pointer to some derived type that you control the implementation of. You would only need to provide templates for the most generic types of functions in your API. Otherwise having to instantiate a template for functions that are only designed for taking a abstract_base_class*
just makes for more boilerplate code for the user to type.
上一篇: 我如何使用UIPageViewController加载单独的XIB?
下一篇: 隐式与显式接口