问题
I think I understand the difference between interface and abstract. Abstract sets default behavior and in cases of pure abstract, behavior needs to be set by derived class. Interface is a take what you need without the overhead from a base class. So what is the advantage of interface over composition? The only advantage I can think is use of protected fields in the base class. What am I missing?
回答1:
An interface defines how you will be used.
You inherit in order to be reused. This means you want to fit into some framework. If you don't need to fit into a framework, even one of your own making, don't inherit.
Composition is an implementation detail. Don't inherit in order to get the implementation of the base class, compose it. Only inherit if it allows you to fit into a framework.
回答2:
An interface defines behaviour. An abstract class helps to implement behaviour.
In theory there is not a lot of difference between a pure abstract class with no implementation at all, and an interface. Both define an unimplemented API. However, pure abstract classes are often used in languages that don't support interfaces to provide interface like semantics (eg C++).
When you have the choice, generally an abstract base will provide some level of functionality, even if it's not complete. It helps implementation of common behaviour. The downside being you are forced to derive from it. When you are simply defining usage, use an interface. (There's nothing stopping you creating an abstract base that implements an interface).
回答3:
Your title does not make sense, and your explanations are a bit blurry, so let's define the terms (and introduce the key missing one).
There are two different things going on here:
- Abstract Class vs Interface
- Inheritance vs Composition
Let us start with Interfaces and Abstract Classes.
- An Abstract Class (in C++) is a class which cannot be instantiated because at least one its method is a pure virtual method.
- An Interface, in Java-like languages, is a set of methods with no implementation, in C++ it is emulated with Abstract Classes with only pure virtual methods.
So, in the context of C++, there is not much difference between either. Especially because the distinction never took into account free-functions.
For example, consider the following "interface":
class LessThanComparable {
public:
virtual ~LessThanComparable() {}
virtual bool less(LessThanComparable const& other) const = 0;
};
You can trivially augment it, even with free functions:
inline bool operator<(LessThanComparable const& left, LessThanComparable const& right) {
return left.less(right);
}
inline bool operator>(LessThanComparable const& left, LessThanComparable const& right) {
return right.less(left);
}
inline bool operator<=(LessThanComparable const& left, LessThanComparable const& right) {
return not right.less(left);
}
inline bool operator>=(LessThanComparable const& left, LessThanComparable const& right) {
return not left.less(right);
}
In this case, we provide behavior... yet the class itself is still an interface... oh well.
The real debate, therefore, is between Inheritance and Composition.
Inheritance is often misused to inherit behavior. This is bad. Inheritance should be used to model a is-a relationship. Otherwise, you probably want Composition.
Consider the simple use case:
class DieselEngine { public: void start(); };
Now, how do we build a Car
with this ?
If you inherit, it will work. However, suddenly you get such code:
void start(DieselEngine& e) { e.start(); }
int main() {
Car car;
start(car);
}
Now, if you decide to replace DieselEngine
with WaterEngine
, the above function does not work. Compilation fails. And having WaterEngine
inherit from DieselEngine
certainly feels ikky...
What is the solution then ? Composition.
class Car {
public:
void start() { engine.start(); }
private:
DieselEngine engine;
};
This way, noone can write nonsensical code that assumes that a car is an engine (doh!). And therefore, changing the engine is easy with absolutely no customer impact.
This means that there is less adherence between your implementation and the code that uses it; or as it is usually referred to: less coupling.
The rule of thumb is that in general, inheriting from a class which has data or implement behavior should be frown upon. It can be legitimate, but there are often better ways. Of course, like all rule of thumb, it is to be taken with a grain of salt; be careful of overengineering.
回答4:
Interfaces are thin, in C++ they can be described as classes with only pure virtual functions. Thin is good because
- it reduces the learning curve in using or implementing the interface
- it reduces the coupling (dependency) between the user and the implementor of the interface. Therefore, the user is really well insulated from changes in the implementation of the interface that they are using.
This, in conjunction with dynamic library linking, helps facilitate plug and play, one of the unsung but great software innovations of recent times. This leads to greater software interoperability, extensibility etc.
Interfaces can be more work to put in place. Justify their adoption when you have an important subsystem that could have more than one possible implementation, some day. The subsystem should in that case be used through an interface.
Reuse by means of inheiritance requires more knowlegde of the behaviour of the implementation you are overriding so there is greater "coupling". That said it is also a valid approach in cases where interfaces are overkill.
回答5:
If type Y inherits from type X, then code which knows how to deal with objects of type X will, in most cases, automatically be able to deal with objects of type Y. Likewise, if type Z implements interface I, then code which knows how to use objects about which implement I, without having to know anything about them, will automatically be able to use objects of type Z. The primary purpose of inheritance and interfaces is to allow such substitutions.
By contrast, if object of type P contains an object of type Q, code which expects to work with an object of type Q will not be able to work on one of type P (unless P inherits from Q in addition to holding an object of that type). Code that expects to manipulate an object of type Q will be able to operate on the Q instance contained within P, but only if the code for P explicitly either supplies it to that code directly, or makes it available to outside code which does so.
来源:https://stackoverflow.com/questions/10334915/interface-vs-composition