Just a few reasons to avoid interfaces
Use the Right Tool
An interface
is a tool, when used in the right way it both simplifies, and enables greater scale of development.
When used poorly it increases complexity, and inhibits usage.
DTO
A Data-Type Object is not a Object as in the instance of a class - because software engineers love to overload meaning...
- Object a.k.a. Object Instance from Object Orientated programming is a modelling technique for capturing the state of a thing, and how that thing responds to messages given that state (a communication protocol).
- Object a.k.a. Memory Object is a region/s of Zero's and One's organised in a particular layout.
In the first case an interface
can be used to substitute in different object instances that still support the same communication protocol. The quintessential example would be a Shape
interface allowing those functions that desire to talk to a Shape
to communicate equally well with a Square
as with a Circle
, or a Rhombus
.
The second case is a description about how to perceive the bits at given locations. This includes expectations about what states those bits should be in, or how they should be modified. eg: Whatever the field x is byte 6 to 6 + valueof(byte 5). Field y at byte 16 can be seen as a signed two's-complement little-endian 64bit integer
. However there is no implementation forcing these expectations to be true. Without an implementation, the meaning is literally in the eye of the beholder. Hence no communication is happening here, and hence there can be no interface
.
Note: There is technically an interface
around memory objects particularly when you reach down. Its just so permissive that anything goes, so it really does nothing as a communication protocol at this level of abstraction, and so there is no point in it other than making it look like an Object-Orientated object.
Why the confusion?
Just to be obvious, and this is quite likely why the term Object
is so easily confused - a class
fuses an interface
to data
with implementation
.
- The
interface
are the names and signatures of the accessable functions and properties along with how they change through interaction, providing the Object-Orientatedness.
- The
data
are the private variables held inside the class these form a predictable memory object that the functions manipulate.
- The
implementation
are the function bodies and property accessors that supply the Object-Orientated behaviour based on the state of the internal Memory Object and other communication.
Security
Always a good objection
Now object instances are not usually sent across a network.
To be sent across the network they would also have to carry their own interface, and implementation. Javascript is pretty much the iconic example here, and the plethora of articles online discussing why you should not eval(my_received_js_object)
can give us a sense that sending objects, is bad idea...
But this is trivial, there is a much more central problem to serialising object instances.
How to Receive an instance of an Object Interface?
Here is the poser:
How do you send a message across time/space (be that a hardrive or a network) that will allow the recipient to precisely recreate the Object Instance (or at least a reasonable analogue) that the sent message represents?
The problem here is that an interface
could have hundreds of different implementations. So which implementation do you mean?
If you were the serialisation framework receiving such a message you could solve this problem by:
- Reflecting about the currently available implementations for that interface, and deducing the correct one.
- Ask the program using the framework to indicate the appropriate implementation.
- Provide an implementation for every knowable
interface
...
- throw your hands up in the air and give up.
How to Send an instance of an Object Interface?
On the other foot, someone had to make the message. How would they have interrogated the interface in order to send it? As a horror story, consider trying to serialise interface IDispose { void dispose(); };
- Reflecting on the type information about the object and hoping that accessing all of the properties is enough to store the object.
- Ask the program to label the correct properties/methods to obtain data from.
- Ask the program to hand craft a message for you to send on its behalf.
- Have a mechanism for cracking into the implementation behind the interface (completely braking encapsulation)
- send the entire object, including implementation and state, wait... that's insecure.
- give up and write c++ template meta-programs instead...
The Alternative?
Dictate the exact memory objects that your serialisation framework will send/receive. Then provide support to the sender/recipient to handle the mapping of their domain from and to those memory objects.
It doesn't really mater which serialisation framework you use, they all fundamentally work this way. They have a few core data objects that they can trivially serialise/deserialise.
These core representations are obtained from the object instances through some form of mapping. Sometimes the mapping is trivially automated (just grab all the properties), but the more Object-Orientated your implementation behaves, the more you will need to provide special directives like:
- don't touch this property,
- call this function to ask for my serialisation,
- use this constructor
- use the default constructor and call this method.
At the recipient those core models are generated in terms of function calls, or some form of DOM (Document Object Model). Which are then mapped either automatically, or through special directives/handlers back into Object instances that the recipient can work with.
Architecturally
An Object instance is a self-contained unit of state, state that is only known to that object, and completely managed by that object. Which means that any serialisation/deserialisation is also handled by that object's implementation - no exceptions.
Of course the object implementation could leverage the serialisation framework, but that does impose coupling between what is probably a domain entity and the framework. That is never a good sign.
Keeping the code sound and clean would then mean that the Object instances return Memory Objects which are then trivially serialised by the framework. This has low coupling, ensuring that the framework can be easily changed.