Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

implicit vs explicit interfaces [closed]

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

like image 347
Chris Morris Avatar asked Feb 10 '12 18:02

Chris Morris


People also ask

What is implicit and explicit interface implementation in C#?

With implicit interface implementations, the members of the interface are public in the class. With explicit implementations, in the class the interface members are not declared as public members and cannot be directly accessed using an instance of the class, but a cast to the interface allows accessing the members.

Why are go interfaces implicit?

A type implements an interface by implementing its methods. There is no explicit declaration of intent, no "implements" keyword. Implicit interfaces decouple the definition of an interface from its implementation, which could then appear in any package without prearrangement.

What is an implicit class interface in C#?

Implicit interface implementation This is the most regular or obvious way to implement members of an interface. Here we don't specify the interface name of the members and implement implicitly. The method can be declared at any interface (s) the class implements.

Can I declare any of the members of an interface Public explicitly why?

Interface is a contract and anywhere where you can access the interface, you should be able to access all the methods in it. In other words, all the methods declared in the interface are supposed to be public so it doesn't make sense stating it explicitly.


2 Answers

Some considerations that came to my mind for why you could prefer the Case 1:

  • If CoolClass is not a pure interface, i.e. part of implementation is also inherited (though you might provide it for Case 2/3 too, e.g. in the form of a base class);
  • if there are reasons to have 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.);
  • if you want to store the pointers and use it later, then also Case 1 seems better: (a) it's easier to keep them all in the same container, and (b) you would need to store the actual data type as well, and for Case 2/3 the solution that comes to mind is to convert it to "explicit" interface (i.e. Case 1) with help of a template wrapper.

Reasons why Case 2/3 might be preferrable:

  • if you later decide that 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.
  • Case2/3 might have slightly better performance, though at the expense of bigger code size.

In some cases, it might be purely the matter of personal preferences, either yours or your users.

like image 131
Alexey Kukanov Avatar answered Sep 27 '22 18:09

Alexey Kukanov


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 ... i.e., 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.

like image 36
Jason Avatar answered Sep 27 '22 17:09

Jason