Not everything you do with interfaces can be done with classes. An interface is a contract, it does not imply any implementation, whereas a class implies an implementation, even if it has the possibility of using abstract and virtual methods. An interface, therefore, can be implemented by any class, in any class hierarchy. Already using only classes would imply that absolutely ALL objects involved in some operation would necessarily have to belong to the same class hierarchy.
Generally speaking:
- Inheritance implies a relationship "was" (Official is a Person)
- Interface implies a relationship "can" (Bitmap can be displaced)
I will respond without implementation code (and perhaps not 100% syntax) to avoid limiting this answer to Delphi, since your question is for general use for Object Orientation.
Imagine you want to implement a standardized form for serialization of classes for JSON (or any other format). If you choose the path of inheritance you must, obligatorily, force that all objects inherit from the same parent. This may be undesirable as it will introduce a class coupling with no relation to each other. Your object hierarchy will no longer be pure, we can say.
With classes you’d have to have something like this:
TSuperClassePaiDeTodos : class
published
function Serializa : String; virtual; abstract;
end;
TUsuario : class(TSuperClassePaiDeTodos)
published
function Serializa : String; override;
end;
TLancamentoFiscal : class(TSuperClassePaiDeTodos)
published
function Serializa : String; override;
end;
TOperadorMatematico : class(TSuperClassePaiDeTodos)
published
function Serializa : String; override;
end;
There is no relationship between a Tax Release and a User (much less a mathematical operator you use, for example, to interpret user-created dynamic expressions), but using class inheritance to force "a contract" (or an interface)in this case forced us to create a relationship between these classes: all inherit from the same parent class and require the implementation of the Serializa method.
That wouldn’t be so bad, since we want them all to be serializable. But what if we need some behavior that is common among some classes, but not in others (imagine you have hundreds of classes, divided into several hierarchies, all inherited from the parent class). What would you do in this case? You would go there in the "father of all" class and add an abstract method. The ripple effect of this is huge: all its hundreds of classes would have to implement this new method, even those who don’t need it.
Imagine that we want to add, for example, audit functionality, which is common to several objects, but not all. For example, it makes sense for a User and an Accounting Release to be audited, but never a Math Operator. How to Elegantly Solve This?
You could simply make an empty implementation of the audit method, but it already starts to sound like a gambit. And it can still have unexpected effects if the code that calls the audit method is expecting some specific behavior from those who implement this contract/interface/function, such as updating some counter, allocating some area of memory, etc. You end up having an object that implements the audit method in theory, but not in fact. It sounds bad, no?
You could try modifying the class hierarchy to figure out a way to put a common parent only among classes that need auditing. This is still easy with only three classes:
TSuperClassePaiDeTodos : class
published
function Serializa : String; virtual; abstract;
end;
TSofreAuditoria : clas(TSuperClassePaiDeTodos)
published
procedure Audita: virtual; abstract;
end;
TUsuario : class(TSofreAuditoria)
published
function Serializa : String; override;
procedure Audita: override;
end;
TLancamentoFiscal : class(TSofreAuditoria)
published
function Serializa : String; override;
procedure Audita: override;
end;
TOperadorMatematico : class(TSuperClassePaiDeTodos)
published
function Serializa : String; override;
end;
Now, with a dozen classes this becomes an extremely confusing process, with a hundred classes is unviable. In addition, your class hierarchy is already getting weird, difficult to understand. At a certain point, you will lose control.
And all this because you are trying to replace the functionality of Interfaces with a hierarchy of classes.
With interfaces you can ensure that objects have some, or some methods in common, that is, follow a contract, without forcing those objects to belong to the same predefined class hierarchy. With interfaces you define capabilities that can be implemented by several uncorrelated classes.
The problem we use of example can be solved simply:
ISerializavel : Interface(IInterface)
published
function Serializa : String;
end;
IAuditavel : Interface(IInterface)
published
procedure Audita;
end;
TUsuario : class(TInterfacedObject, ISerializavel, IAuditavel)
published
function Serializa : String;
procedure Audita;
end;
TLancamentoFiscal : class(TInterfacedObject, ISerializavel, IAuditavel)
published
function Serializa : String;
procedure Audita;
end;
TOperadorMatematico : class(TInterfacedObject, ISerializavel)
published
function Serializa : String;
end;
Note that we have removed any and all undesirable relationships between classes. The hierarchy no longer needs to follow a rigid definition. And the introduction of the audit feature did not require changes in the hierarchy, nor its implementation in objects that are not auditable. The Code was also much more readable and maintainable.
As a class can implement more than one interface, this becomes a very efficient way to organize functionalities that are common among objects that have no relation to each other, that is, that have no relation "was" among themselves. Keep your class hierarchy strongly cemented in the object orientation concepts: if an object is not, in fact, from the same family of another, do not make them brothers, or parents and children! Use aggregation or interfaces.
Advantages, disadvantages and applications
Interfaces are a way of specifying a contract (a standardized and expected way of interacting with some object), but classes also define contracts. It is a myth that interfaces are mandatory to specify a contract, as we have seen, classes can define abstract methods, which necessarily need to be implemented in the descendants. It forms a contract.
In my opinion we should first opt for classes, as they are easier to maintain than interfaces. Only when needs arise that are better met by interfaces (such as the example used here) would it introduce interfaces. Often, when introducing interfaces, I create a parent/parent class that provides a standard implementation for such an interface in order to facilitate its implementation (but this does not force that all classes implementing such an interface also inherit from this class: they can implement the interface from scratch).
The book .NET Design Framework Guidelines agrees with my position and provides some more arguments about favoring the use of classes instead of interfaces:
In general classes are the preferred construction to expose abstractions.
The main disadvantage of interfaces is that they are much less
than classes concerning the future evolution of an API.
Once you publish an interface, your set of members is
frozen forever. Any additions to the interface would break types
existing that implement that interface.
A class gives us more flexibility. You can add members to
classes you have already published. As long as the method is not abstract (or
is provided that you provide a standard implementation for the method),
any derived classes will continue to function without modifications.
[ . . . ]
One of the most common arguments in favor of interfaces is that they
allow separate contract implementation. However, the argument
assumes incorrectly that you cannot separate contracts from
implementation using classes. Abstract classes residing in a
Assembly (a unit, library or a component in Delphi) separate from
its concrete implementations is a great way to achieve such
separation.
Note: free translation made by me!
The problem of API evolution can be seen in the widespread mess that are the Direct X Apis. You have a lot of interfaces, with the same name, followed by a number only because the interfaces already published could not be modified with the new features introduced to each version of Direct X. Now we have Idirect3d7, Idirect3d8, Idirect3d9, etc.
In the case of Direct X, as interfaces need to be used by different programming languages, it would be very difficult to publish this functionality without resorting to the use of interfaces, but this same problem can happen in our API if we don’t take care to use classes where classes are more pertinent than interfaces!
Therefore, use interfaces when they are the right tool for the case. Every time you have a contract that applies to totally independent (non-correlated) classes the interfaces will be the best solution to the case, preventing you from engulfing your class hierarchy by forcing artificial inheritances.
Also use interfaces when you need to implement multiple inheritance (or something similar) in languages that do not provide support for it.
Also, use interfaces when immutability is a desired effect: you do not want that contract to be modified in the future, but a new contract needs to be signed (a new interface introduced) in case new functionalities arise.
Avoid, at all costs, premature generalization, because this is, like premature optimization, a waste of time most of the time. Design your object structure well, but only generalize when you realize the real need. Remember that it is possible (and desirable) to introduce generalizations during refactoring, and this is even indicated in the paradigms Agile, XP, finally, the most modern.
@Danielomine doesn’t think it’s duplicate. This question here is much more specific. The other is completely generic (and too broad), since those who asked did not specify any parameters for their doubts. The other doesn’t know the difference between class and interface. Here one wonders the advantage of interfaces if it is possible to accomplish "everything" with class inheritance.
– Loudenvier
@Danielomine, if that’s possible, please clear my question as duplicate. I already edited it to clarify that it is not a comparison between Interfaces and abstract classes. I think removing that duplicate statement will help other people who, like me, have the same question. Following the link indicated in the duplicate text will only disturb the understanding. Thank you!
– Carlos B. Feitoza Filho
As @Loudenvier said, this question is not duplicate anything, firstly because the context is not generic and secondly, I want to know the advantages (if any) of using interfaces, because I already know that an abstract class mixes the concepts of interface and ordinary classes. Thank you!
– Carlos B. Feitoza Filho
Sorry Carlos, but the question is conceptual. So it is duplicate of the suggested link.
– Daniel Omine
All right, I’ll just ignore it then, since from my point of vita (and others) it has nothing to do with the other except the fact of citing classes and interfaces
– Carlos B. Feitoza Filho
@Danielomine just because it is conceptual does not make this question a duplicate of the other. I think they were too rigid in this marking. However, I also find it interesting that those who see this question can access the other, much broader and generic, to understand the concepts of classes and interface itself, and not the advantages of both in specific contexts. I also suggest to the "questioner" to get used to having their questions and answers under the scrutiny of others and not take it personally! Stack Overflow is revolutionary and works great!!!
– Loudenvier
Take a tour to better understand http://answall.com/tour
– Daniel Omine
The questions may be different but the answers are "equal".
– Daniel Omine
The answer given here could have been given there. The answers there answer what was asked here. So it’s duplicate according to the definition of duplicate that we use. The question doesn’t need to be exactly equal to be considered duplicate.
– Maniero
@Loudenvier, just to be clear, I didn’t take it personally, I just won’t accept something that doesn’t make any sense. In my holy stupidity, I see no relation between the supposed duplicate and my question other than the fact that both involve classes and interfaces. I didn’t want to know the difference between a class with abstract methods and an interface. I learned this in "pre". I really wanted to understand what the usefulness of an interface was when I lived until today without ever actually having used it and you gave the explanation I needed. Mission Accomplished
– Carlos B. Feitoza Filho