This is an attempt to explain the differences between an interface, an abstract class and a non-abstract class in the PHP language. Other languages may have slightly different implementations.
Each of these constructs may contain only certain types of method, so it is necessary to explain what these are first.
An abstract method is defined as a method signature without any method body - i.e. it has no implementation.
When defined within an abstract class it requires to be prefixed with the keyword abstract. This is not necessary when defined within an interface as all methods within an interface are abstract by default.
When something containing an abstract method is incorporated into a non-abstract class, either by using the implements or extends keyword, that abstract method must be defined in that class with the same signature, at which point the method body can also be defined.
A static method can be accessed without having to first instantiate its class into an object. It is accessed using the expression <classname>::<method> such a singleton::getInstance().
A static method cannot use the pseudo-variable
A plain method has a signature and a body, but the body may be empty.
A plain method can use any of the visibility modifiers public, private or protected.
An interface defines one or more abstract methods. It cannot contain any plain or static methods.
An interface can contain constants, but not any variables.
An interface cannot be instantiated into an object.
No methods in an interface can have visibility modifiers as every method is public by default.
An interface can be inherited by another interface using the implements keyword.
An interface can be inherited by a class, either abstract or non-abstract, using the implements keyword.
An abstract class is identified by having the keyword abstract in front of the class name
An abstract class cannot be instantiated into an object.
It can inherit one or more interfaces using the implements keyword.
It can inherit no more than one abstract class using the extends keyword.
An abstract class cannot inherit from a non-abstract class.
Any abstract methods which are inherited, either from an interface or another abstract class, do not need to be defined as signatures and their implementations are only required when inherited by a non-abstract class.
It can contain any number of constants, variables and methods (either abstract or otherwise).
This can contain any number of static or plain methods.
It cannot contain any abstract methods otherwise it would have to be an abstract class.
It can contain any number of constants and variables.
It can be instantiated into an object.
It can extend no more than one class, abstract or non-abstract.
It can implement any number of interfaces.
It can be inherited by another class, but not an abstract class.
A class which is inherited is known as the superclass or parent class, while the class which does the inheriting is known as the subclass or child class.
If any abstract methods are inherited then these must be defined with exactly the same signature and with whatever implementation is desired.
If any plain methods are inherited then these need not be defined, but if they are they must have the same signature and with whatever implementation is desired.
If a method in the superclass is overridden in the subclass then the implementation in the subclass will be executed at runtime, not the implementation in the superclass.
Constants, variables and methods which are not inherited can be given any visibility modifiers.
An abstract method inherited from an interface must be defined with the same visibility (public).
An abstract method inherited from an abstract class can be defined with the same or less restrictive visibility.
My first OO-capable language was PHP 4 which did not support interfaces, only encapsulation, inheritance and polymorphism, and I have been able to produce reliable and cost-effective software using nothing but these three. However, since I have been publishing articles on my approach I have been constantly bombarded with accusations saying that I am not a "proper" OO programmer because I am not using interfaces. I choose to ignore these accusations because they are based on a bunch of myths and are completely without merit.
This to me is a total abuse of the term "inheritance" as nothing is actually inherited with interfaces. If you look up the definition of inheritance you will see that it entails something of substance being given to someone without any effort on their part. If a relative dies and leaves you a fortune in their will then you suddenly find yourself in possession of this fortune for no good reason other than being named as the beneficiary.
In OO programming inheritance works slightly differently as instead of the current owner of this "thing of substance" (the superclass) naming you as their beneficiary, any number of subclasses can choose to inherit (using the keyword extends) from the same superclass. This means that the entire contents of the superclass is then shared by any number of subclasses without any of these subclasses being known to the superclass.
There is no such thing as interface inheritance as nothing is actually passed down to the beneficiary. Not only is there no implementation (you have to code it yourself), you don't even "inherit" the method signature as you have construct your own duplicate. It's like saying to someone "Congratulations, you have just inherited a briefcase full of money" and when they ask "OK, so where is it?" you tell them that they have got to provide both the money and the briefcase themselves. So if nothing is actually given to you because you have had to provide it yourself, what exactly is it that you have inherited? There is nothing of substance, only a concept.
The use of interfaces allows for a programming style called programming to the interface. The idea behind this approach is to base programming logic on the interfaces of the objects used, rather than on internal implementation details. Programming to the interface reduces dependency on implementation specifics and makes code more reusable.
This to me is a load of
bollocks balderdash. Firstly, when you call a method in an object you have no knowledge of how that method is implemented in that object, just that a method with that signature exists. This allows you to call the same method on a different object which then has a different implementation. This is what polymorphism is all about. Secondly, the idea of code reuse involves more than duplicated method signatures, it involves a block of code (the implementation of an idea) being defined just once and then being called from multiple places instead of that code being duplicated in multiple places. This is what the DRY principle is all about.
In the article Interface inheritance vs Implementation inheritance the author states the following:
The concept of preferring interface inheritance probably came first from the C++ and COM programming days, where an abstract class would be used to define methods in a base class and the inheriting classes would be required to implement the methods.
This is rubbish as the subclass is only required to provide implementations for abstract methods, and an abstract class need not contain any abstract methods, only concrete ones. Concrete methods in an abstract class are automatically inherited by a subclass, and need only be defined in the subclass should the implementation be changed.
The simple answer to this is to avoid coupling between two components of an application.
The idea that you must avoid coupling at all costs tells me that this person does not understand what coupling actually means. When you have an application comprised of many modules which must interact between themselves then you automatically have coupling and dependencies. This is unavoidable in a modular application. It is not possible to eliminate coupling entirely as a completely decoupled application will not work. Coupling is not a binary condition which can be stated as either "Yes, there is coupling" or "No, there is no coupling". It is instead stated as being tight or loose. Two modules which are tightly coupled have less chance of being reused, so the only way to guarantee more code reuse is to have coupling which is as loose as possible. For example, if I have a controller C1 which is tightly coupled to a model M1 then I cannot use that controller with any other model, and I cannot use that model with any other controller. In my RADICORE framework I have moved to the other end of the spectrum by allowing any of my 40+ reusable controllers to be used with any of my model classes (of which there are currently 350+). So by minimising (which is NOT the same as eliminating) the level of coupling I have maximised the level of code reusability.
If you read through those articles you will see where one says the following:
If a derived class is allowed to access members inherited from a base class, changes in the base class may require maintenance of the derived class as well.
Note the use of the words if and may. This shows that problems are only possible under certain circumstances, not guaranteed under all circumstances. The response to that should be obvious - be careful when you make changes and you won't have any problems.
Others say something like the following:
It breaks encapsulation since it exposes a subclass to implementation details of its superclass.
Encapsulation is about information hiding, and when you inherit from a superclass the information inside that superclass is no longer hidden in the subclass.
These people fail to understand that when you inherit the contents of class C1 into class C2, then the resulting object combines the contents of C1 with the contents of C2. Anything in C1 which should be hidden from C2 should have its visibility set correctly. Any problem is therefore with the programmer's implementation of the concept, not the concept itself.
To me it is a question of balance. You have to weigh the advantages of inheritance against the disadvantages. The advantages of having large amounts of code which is reused in multiple places, thus avoiding the need for code duplication, is guaranteed at all times. The disadvantages of having large amounts of shared code is that if you make any changes to the base class you may have to make corresponding changes in all derived classes, but that is only a possibility when you change the base class. In other words, the advantages are guaranteed whereas the disadvantages are only possibilities. I'm afraid that, IMHO, the advantages outweigh the disadvantages by a huge margin, so I will continue to use inheritance as it was designed to be used.
I hate interfaces, and I do not use them. Refer to A minimalist approach to Object Oriented Programming with PHP for a detailed explanation.
I love abstract classes. In my RADICORE framework, which deals with database applications, every Model component in the Business layer represents a single database table, and all the code which is common to database tables has been defined once in a single abstract table class which is then inherited by every one of my 350+ concrete table classes. This abstract class is filled with 200+ concrete methods, not abstract methods, so none of them need be defined and implemented in the subclass. This provides a huge amount of reusable/sharable code, and as this is supposed to be the objective of OOP I absolutely refuse to acknowledge any criticisms which say that my implementation of OOP is wrong.
I never inherit from one concrete class in order to create a different concrete class. I only ever extend a concrete class when a different user transaction requires a different implementation in one or more of the custom methods.
© Tony Marston
16th September 2017