Don't overuse interfaces
blog.hovland.xyz
blog.hovland.xyz
The cons I see are there are now two places where documentation needs to be kept in sync.
And reduced readability because of abstraction. For instances if I'm going through the mail chimp code base it's easier to understand how a mailcampaign interacts with the surrounding code base than something like iclickable. If I see a mailcampaign in code I instantly know what domain object it's represents, and have some idea a out what logic to expect in it. Iclickable I have no idea.
Im anxiously awaiting a thoughtful discussion of interfaces.
I would say that the subset of "good in and of itself" from interfaces -- ignoring those other benefits like multiple-implementations and extendable/reusable/mockable code -- usually relate to separating the mental task of defining what you wanted to exist versus what you were able to achieve so far.
In some ways an interface is similar to test-driven development, since it allows developers to create a scaffold of requirements and goals (interface methods and comments) before getting bogged down in the implementation details, and iteratively cycle between those two viewpoints.
In contrast, classes without an interface are at a higher risk for following a sort of least-effort evolution, such that someday a developer answers: "Why does it work that way? I dunno, that's just how we got it working."
> The cons I see are there are now two places where documentation needs to be kept in sync.
Ideally the in-file documentation is different though: The interface says "this method will take an X and create a new Y based on it", whereas the implementation communicates "I fulfill the interface's requirements by <stuff that makes me interesting and unique>."
Interfaces have many good uses in many situations, but by themselves, with one implementation, is not one of them. (note by themselves. Unit testing, multiple implementations, and so on are reasons beyond in and of themselves). And you can very well do the mental separation by coding without adding an unnecessary interface (unless it has other uses of course).
It's pretty annoying TBQH.
[0] https://www.typemock.com/isolator
[1] http://www.telerik.com/products/mocking.aspx
[2] https://github.com/Fody/Ionad
[3] http://mattwarren.org/2014/08/14/how-to-mock-sealed-classes-...
In practice though I like to think that the re-use of my objects and class hierarchies is a critical part of their design. Just about anything I'm gonna have to override to make a simple mock will also be an issue for other implementers. So why not use the opportunity to abstract and refine the objects internal logic to make mocking the relevant functionality cleaner and more obvious? Create an internal "OnProcessComplete" method which can be overridden easily instead of mocking the whole "Process" command.
Over-mocking is a major code smell and productivity thief. I find that having to manually set it up keeps me honest about cost/benefit, while also looking at an important design aspect that otherwise could be ignored.
Having 1:1 mapping between an interface and a concrete class seems kind of pointless to me.
It is often used by test code, for example, that wants to use a fake version of something the library provides.
Which, by the way, is generally preferable to mocking.
As a result, beginning programmers start their careers thinking that good code is filled with inheritance hierarchies and layers of indirection, and they think that getting better as a coder means getting better at adding more and more of those protective layers to ensure their code is prepared for every possible eventuality. Which is insane. It's like sending your kid to school dressed in a raincoat and rubber boots every day when you live in Arizona, instead of looking at the weather forecast (or just guessing) like the other parents. One day every year you'll be right and they'll be wrong, and if can convince people that makes you smart, you have a promising career as an enterprise Java consultant.
I think it all goes back to the overstated fear of change. I think the fear of adapting straightforward code to new requirements must come from another time when code was harder to change, because nothing in my professional experience supports it. According to this fear, the only way to survive is to guess correctly at how your code will need to evolve in the future and preemptively design for it. All of the inheritance hierarchies and layers of indirection must already be in place before you discover that you need them, or else something very bad happens. Therefore the implementation costs and the overhead in maintenance and confusion are gladly paid. How does this make sense? I've never suffered greatly from code that was too simple, too straightforward, not built out enough. I've suffered many times from code built out in a slightly wrong direction that was the best possible guess at the time. The feeling of safety and prudence that people get from preemptively complexifying their code seems like delusion to me.
The author suggests using virtual, but if your intent is not to allow child classes to override the implementation, this is certainly not "pure".
Visual Studio & ReSharper makes the code generation (Refactor > Extract Interface) and maintenance upkeep trivial anyway.
Plain Old Class Object means no special inheritance chains or attributes, just a "plain old" class.
By way of example: this contrasts with old Entity Framework or other ORMs that made you inherit from a certain kind of class, breaking the objects original intent, and hurting you when it came time to port to another framework, or serialize, or maintain your own class hierarchy.
Even what DDDers would call a Value Object could and should still have relevant business logic attached to them. For test-ability and portability you don't want your classes married to any particular framework, but your objects are still responsible for hiding relevant information.
Combine zealotry with the internet, blog-posts about how you "should always" and you can have yourself a cargo-cult programming-practice of your own choosing.
> Please note that I’m not saying that interfaces are always bad. When they add value, they are useful. I’m only saying that interfaces that mirror one and only one class implementation is waste.
That's not really controversial or interesting.
"Don't write interfaces that have only one implementation" is something I've heard many times. The only well-accepted exception to that rule is interfaces that are expected to be implemented by, say, users of a library you are writing.
EveryClass : IEveryClassSure, I'm adding this shitty console based logger, but since it's wrapped in ILogger I'll later come and write a FileBasedLogger, a DBLogger and a KafkaProducerLogger for when we go webscale.
Except, that never happens.
No, it's the opposite. IClass is the norm, for legacy reasons going all the way back to the early COM days (it all started with IUnknown[1]).
[1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms6...
But you want to, that's the point. You're making a drastic change to your code base, surely you want to inspect every place where that type is used.
//Names changed slightly to protect the wicked
public interface IProvideBoolean {
Boolean True { get; }
Boolean False { get; }
}
public class BooleanProvider : IProvideBoolean {
public Boolean True => true;
public Boolean False => false;
}
So.... SOLID....
If there's ever a third value for Boolean, I Will Be Ready.You can also do "pImpl", which is similar, if not worse, in terms of boilerplate, but doesn't allow DI.
Just don't use these techniques unless you can prove they're needed (e.g private members drag new problematic dependencies, #include <some_volatile_header_file.h>).
OTOH in certain specific cases I see the value of the interface even for just one class when I want to clearly separate the contract (which I'm bound to fulfill now and in the future) and the implementation specific comments.
Let's say that in my little pet programming language I have just one List type. I want to clearly separate what's the contract (indexed ordered sequence) as opposed to "incidental" implementation details (constant cost random index access).
It is to me. So far it seems that everyone takes this for granted. I disagree: I have yet to see a problem created by the "each class has an interface" rule, and I have seen countless problems created by classes without one.
So: what problems are caused by interfaces with only one implementation?
Some people think nothing of adding just one more method to a class. Sure, it's unrelated, which they (may) realize on some level, but for the sake of expediency, it's going in the class. Some of those same people would pause and think a minute before putting the same method into an interface. They wouldn't feel it belongs there.
Sure, in an ideal world, everyone is 100% rational and 100% enlightened and they realize it's really the same thing. But in the real world, people are a bit irrational at times. People often feel that $99.99 is a lot less money than $100.00, for example.
The point is, having to include it in an interface can be an occasion for reflection on whether it really belongs there. It's not unlike how when you move, you go through your belongings, and that triggers a process of justifying to yourself whether you really still need that item. In theory, there is no reason why you can't do that at any other time. In practice, it serves as an occasion that encourages the process to actually happen.
That said, if you don't through this process of examination, and you just create a Foo and think "I'm supposed to have an IFoo" and then just mechanically copy everything over, then you're not getting any value out of that.
Strongly-typed languages like Java or C# really obscure this simple truth, in my opinion. What the OP is talking about is one specific way that it can be obscured. Which is certainly true as far as it goes. But it's not going far enough!
The real problem is that OOP is totally under-constrained for end-user applications. (Although it's perfectly adequate, I think, for modeling formats and protocols).
The only really good way to do applications is as dynamic functions, because there is self-similarity between your program statements and the way you organize them, making your combinatorial problem far more straight-forward. Plus, if you do it right, your function set can fit into on-die cache, and you know enough about your inputs so that you can allocate a fixed, small memory space for any combination of statements are required for a given input (or gracefully error out when your assumptions about input are violated - ideally without even unloading your code).
There’re a lot of them indeed. But outside niches like compilation and text parsing, they are far from majority.
Some problems are bandwidth-bound, like photoshop: if you have 1GB image, you have to process the complete data set no matter the combinatory.
Other problems are IO bound, like databases: if user wrote a query that need to do sequential scan over a table, you’ll have do to that I/O.
For both classes of problems, OOP is just fine.
Another thing is, OOP is better at explicitly managing the state. If you design an app your way, viewing your code as reduction over previous inputs, I expect you’ll struggle implementing state management functionality like undo/redo stacks, and saving/loading your stuff in these formats and protocols.
In the steady-state, your process handles input as a method invocation (the main(String[]) of a CLI program or the handleRequest(req) method of a servlet, for example). In both cases, the thread associated with the input travels through your code, going deeper, then shallower, deeper again (the depth is the stack frame count).
Each time we go deeper (each time we conditionally invoke a method) we are solving a combinatorial problem across our own code-base. We are effectively crafting, at runtime, a unique program (concrete list of statements) to deal with this input. We don't normally learn OOP this way, or think of it this way, but it really is what we are doing. But there are some benefits to thinking in this way.
For example, what is an interface? Like all indirection, it's like lubricant. It decouples one set of statements from another, and makes it easier to substitute (or slide) out the second set. And a factory? Well, it produces bundles of statements that conform to an interface. But since we are strongly-typed, the factory is only selecting from a finite set. (In fact, the factory pattern is a perfect example of how OOP practitioners are doing combinatorics all the time, but we don't know it, and we do it crudely.)
We organize our state using objects.
Many programs have to deal with complex mutable state. The extreme cases are OS kernel state, or game state in an AAA videogame. For such programs, OOP is the only approach allowing people to reason about what’s going on inside these things.
If you don’t have complex mutable state, other approaches might become more practical. One example is compilers; their state is mostly immutable. Another is pure computations without state, especially symbolic computations, like what happens in Maple.
> the thread associated with the input travels through your code, going deeper, then shallower, deeper again
In many programs, input handling is minor part of overall software complexity. Sometimes, most of the code don’t even run on the input thread.
> decouples one set of statements from another, and makes it easier to substitute (or slide) out the second set.
Interfaces abstract away arbitrary stuff (statements, data, hardware devices, etc.), providing strongly-typed API to use that stuff, and hiding complexity of the implementation. In some case (COM interfaces), the statements you’re selecting were written in another language, are implemented in another DLL being loaded on demand, run in different process that starts up on demand, or for DCOM even run on different machine.
> since we are strongly-typed, the factory is only selecting from a finite set
In C#, some class factories take your strongly-typed interface, take other parameters you pass to the factory, and implement the interface in runtime. Because there is infinite set of possible class-factory parameters, that makes the set of statements infinite as well. BTW, this approach is often used in .NET RPC libraries, like WCF.
Unfortunately, OOP doesn't allow people to reason about complex systems, primarily because call-chains are totally unconstrained. An object can contain references to any other object held by the process, and this reference list is dynamic at runtime. In the presence of instantiation indirection it's even worse, even the concrete type of those references is unknown except at runtime.
There are code smells, and then there are architecture smells. One big architecture smell is when the incidental complexity of a solution vastly outstrips the intrinsic complexity of the problem. I have never (and I've been doing this a very long time) seen an OOP code base less than 10x the complexity of the business problem it was built to solve. It's sad to me that instead of asking why, they keep blaming the programmer/architect and inventing new complexity to mitigate old complexity.
The overall fraction might be small, however vast majority of software still contain substantial amount of code that does. Most serialization and DB access libraries do. For C#, this includes all framework-provided serializers (XML, data contract, etc.), the ubiquitous Json.NET, and my own one: https://github.com/Const-me/EsentSerialize
> OOP doesn't allow people to reason about complex systems
Respectfully disagree, ‘coz I have never saw complex systems that aren’t OOP. Even most compilers are OOP, take a look: https://github.com/llvm-mirror/llvm https://github.com/gcc-mirror/gcc/tree/master/gcc https://github.com/dotnet/roslyn
> An object can contain references to any other object held by the process
This is true for very small class of objects, like GUI controls with loosely typed event handlers, or objects that hold lambdas/functions that may capture anything at all (but the latter is not strictly OOP).
An object can only reference other objects of types that can be stored in its properties/fields. For many objects especially simple lower-level ones, this means objects can reference no other objects at all.
> have never seen an OOP code base less than 10x the complexity of the business problem it was built to solve
My guess is, you’re systematically underestimating complexity of the business problems solved by the code you look at.
In every case, codebase complexity was dominated by administrative overhead. My passing experience with apps written in C#, ASP, and PHP, tell me that it is the same story there, too.
This is my subjective view. One way you might understand my perspective (and perhaps even share it, to some degree) is to consider the act of adding a column to a database table, and how a typical OOP/RDB webapp codebase must change to incorporate a new DB value. (And this is only the tip of the iceberg, since usually adding a feature requires a view, an index or two, plus column and table changes.)
As you see, almost nothing common with your background.
For the kind of software I work on, if the code is 10x more complex than necessary, the software might fail the performance targets. Complex code is also prohibitively harder to maintain and debug. And esp.in embedded, quality requirements are just higher: web requests come and go, but embedded software needs to reliably work for days without restarts.
I think the reason why I’m generally OK with other people’s OOP-style code I see in the projects I work on, is that code is just better then what’s in an average web.app. Especially if the web app ain’t Google or Facebook, AFAIK for these guys, software quality translates to substantial saving on electric bills.
If you can overcome this cringeworthy intro about DI, the rest of the article is not as bad as this.
void DoFoo(IInterface bar)
or
T DoFoo(T bar) where T : IInterface
But what I find instead is the same functionality packed into an elaborate and delicate inheritance hierarchy. It's like an object implementing multiple interfaces is still alien to most developers.
Actually, resharper does a disservice sometimes. Most classes have their own methods, which are optimized for that container.
It once bit me in the arse really hard: after following resharper’s advice I got around 3x slowdown of the algo overall (that IEnumerable was in a tight loop, which was slowed down really hard).
If you were calling a method that didn't exist in IEnumerable, why would resharper suggest changing the type to IEnumerable?
The actual suggestions are based on Microsoft Guidelines :-
https://msdn.microsoft.com/en-us/library/dn169389%28v=vs.110...
The difference is when you use IEnumerable<T> user method (provided as extension, using IEnum) has to call IEnumerator GetEnumerator() and allocate it on a heap. But that collection have its own implementation which works faster than generic one.
(For others) Most of .NET frameworks generic collections implement the enumerator as a struct which doesnt get allocated on the heap and subsequently garbage collected, but the explicit interfaces implementations use a class which does.
I don't use resharper so don't know if it watches out for this, but no doubt there are plenty of other examples.
In Stage 1 he realised that a bad understanding of DI has resulted in a ton of pointless Interfaces.
At Stage 2 he realised he didn't need those interfaces to unit test.
Wait until he reaches stage 3 realises that DI itself is fairly pointless and bad coders are still going to make a massively coupled mess even if you force them to use DI.
For example, one of our controllers has 20 odd "services" injected, with each of those services then having a bunch of other services/Dals/etc. injected, causing a massively coupled dependencies. So much for DI cleaning your code.
DI code is no less coupled than if you just coded it cleanly in the first place.
The only good thing I've found it for in C# is that it passes the EF context around per request without you having to manage that, so all your services are using the same context.
DI isn't mean to be silver bullet, and bad coders are going to make a mess, whatever technique you try to impose on them.
[1] http://deliberate-software.com/simplemock-unit-test-mocking/
The author did forget, that all interfaces can be DI'd when the class is an implementation of IService ( or IRepository), so you don't have to specify all classes seperately.
This should make it more obvious: http://simpleinjector.readthedocs.io/en/latest/advanced.html...
And
http://stackoverflow.com/questions/15581505/generic-abstract...
For the rest, I agree to use interfaces when appropriate. But I'm not sure this post adds a lot of value to be on HN. Perhaps it's because I'm a c# developer and this should be basic knowledge in that domain ( eg. Books and Stack overflow). Can someone elaborate on that?
The problem with multiple methods is that you can abstract away the function call, but you can't ensure the caller is the methods in correct order (if that matters, but it often does).
Take the JDBC-interface: It's certainly not possible to call setTransactionIsolation(), executeQuery(), commit(), rollback() etc. in any order.
Or list: add, remove, set, clear etc.
It's still an interface.
I'm writing an Android app using Kotlin (where everything is final by default).
I use mocking extensively when I test, and I had to choose between making an interface for [almost] everything, or explicitly opening everything. I went for the former.
I know mockito has tools now to deal with final classes, but the last time I tried it, it wasn't working so well (at all) for Android.
It's not YAGNI or duplication, if you can't understand why we program to an interface and not an implementation then you just very seriously need to go look at your codebase and decide to extend a core bit of functionality, one that usually comes to mind is Authentication/Entitlement