3 ms·
It's not about how many ISomething's exist - it's about defining a contract between Something and the rest of the system. When it comes to testing, how are you
by meaty 14y ago
It's not about how many ISomething's exist - it's about defining a contract between Something and the rest of the system.
When it comes to testing, how are you going to test your ClassX if it's tightly coupled to ClassY rather than IClassY (which can be mocked/stubbed)? Oh, you have to test both.
Also, an interface is a great way of implementing "worry about that shit later - just let me talk to it now". You can define and evolve the contracts before you have to refactor expensive implementations.
As for AbstractSomethingFactory, I've not written a bit of code like that for night on 10 years. The container gives me what shit I need, ready configured with all dependencies.
Blindly following rules is dangerous, but burning entire books for what appears to be lack of experience is much more dangerous.
- w0utert 14y ago>> It's not about how many ISomething's exist - it's about defining a contract between Something and the rest of the system. Why would I need to introduce a separate interface class to define this contract, instead of making it explicit through the public class interface? What problem do I solve by adding another layer of abstraction between Something (the class) and Something (the interface), if Something is just some internally used entity that isn't part of a framework or public API? I've seen millions of lines of code to date, and the number of times hiding public class interfaces behind interface classes (note how those sound almost identical, wonder why that would be?) can be counted on the fingers of one hand. Interface classes only make sense if you expect classes to be used like you would use components in a component-based architecture, e.g. for plugins, or polymorphic, adaptable structures that may be implemented by combinations of classes, etc. >> When it comes to testing, how are you going to test your ClassX if it's tightly coupled to ClassY rather than IClassY (which can be mocked/stubbed)? Oh, you have to test both. You don't always need interface classes for loose coupling, abstract base classes cover many cases, and in many other cases loose coupling is simply not required. There is nothing wrong in testing multiple classes that are tightly coupled together, without stubbing. In those cases where loose coupling is absolutely essential (frameworks for example) and you want to compose classes to provide some kind of interface, use interface classes. My point is not that interfaces classes are wrong, but they are overused, and more often than not, they add complexity without benefits. >> Also, an interface is a great way of implementing "worry about that shit later - just let me talk to it now". You can define and evolve the contracts before you have to refactor expensive implementations. Again, I don't see why you would need interface classes for any of this. I can write a stub class that let's me 'worry about shit later' simply by defining its public interface and leaving its implementation empty. If I have a lot of classes adhering to the same (semi-) abstract interface and I don't want to stub them all with empty methods, I simply create a temporary base class I can remove later. Eventually I'll have to implement everything anyway, and I'm not writing interfaces just for the fun of it, or the remote possibility that some interface may be required later, even though no-one asked for it. >> As for AbstractSomethingFactory, I've not written a bit of code like that for night on 10 years. The container gives me what shit I need, ready configured with all dependencies. Interesting that you should say that, because I used AbstractSomethingFactory simply as a caricature of an often overused design pattern, without implying that you should never write an abstract factory. In fact, abstract factories are a useful solution in many common designs, even though they are so overused. >> Blindly following rules is dangerous, but burning entire books for what appears to be lack of experience is much more dangerous. Which means we have to aim somewhere in between, which is exactly what I was trying to say in the first place. Unfortunately many developers somehow seem to drift to either one of the extremes, the Java/C#-like language programmers to the 'create moar classes!' side, everyone else to the 'just make it work' side. My point is that both lead to unmaintainable, bug-ridden code.
- meaty 14y agoFirstly the interface isn't a layer of abstraction - it's a contract, a boundary. An abstraction is an arbitrary concept which may or may not apply to an interface. Secondly, the abstract base concept is actually a deadly form of coupling. As many people have suggested since the dawn of the problem, composition is better than inheritance and what sits at the composition boundaries? Interfaces! Inheritance usually turns into an LSP-violating [1] clusterfuck. I know - I have spent 2 weeks refactoring one into something which doesn't stick a fork in your eye every time you change something. And this is a non trivial one with over 100 classes in the inheritance graph (this is a roughly vomited out version of the Party archetype from the MDA book [2]). So your tests are chock full of empty stub classes rather than mocks? Yuck. If you look at ASP.Net's native API (before System.Web.Abstractions hid all the crimes away), you will see the sort of shit you'll get yourself into if you follow your own advice. Agree with your last point though. For reference, yes I am an architecture astronaut with one foot nailed to the ground :) [1] http://en.wikipedia.org/wiki/Liskov_substitution_principle http://en.wikipedia.org/wiki/Liskov_substitution_principle [2] http://www.amazon.com/Enterprise-Patterns-MDA-Building-Archetype/dp/032111230X http://www.amazon.com/Enterprise-Patterns-MDA-Building-Arche...
- w0utert 14y ago>> Firstly the interface isn't a layer of abstraction - it's a contract, a boundary. An abstraction is an arbitrary concept which may or may not apply to an interface. That's just semantics, let's concentrate on why I would always want to have an interface class instead of using the public class interface as the contract between a class and the rest of the system using it. Remember that many classes in any software design don't have (or need to have) base subclasses, composed classes, abstract interfaces, whatever. They are often nothing more than pieces of data with related methods, and nothing else. Writing lots of fluff around them to make such classes extensible, flexible, replaceable, loosely coupled, easy to stub for testing, whatever, does not add any value for you (as the programmer), your colleagues (when reading the code) or the customer (when running it). You'll know it when you'll need to do all of this anyway, and the moment you know it is early enough to think about it, no need to have contingency plans for things your classes are not ever going to be used for. >> Secondly, the abstract base concept is actually a deadly form of coupling. As many people have suggested since the dawn of the problem, composition is better than inheritance and what sits at the composition boundaries? Interfaces! Interfaces, not interface classes. We have many classes in our codebase that composite other classes, and none of them have interfaces at all, because the whole codebase is C++, which doesn't even have the concept of interface classes. Yet, our codebase is very clean and robust, fully covered by unit and integration tests, and rarely (if ever) contains bugs that are caused by bad or inflexible architecture. It does happen, on occasion, that some part of the code or its design doesn't satisfy new requirements anymore, but when that happens we simply refactor it, and if needed (which is extremely rare) write a facade around it to support its old public interface. Note that I'm not saying I'm happy with the fact C++ doesn't have interface classes, because there definitely are situations I would want to use it, for example where a component-based design would best fit the problem at hand. In those cases we usually introduce a pure-abstract class that mimics an interface class, which isn't ideal but does the job. I'm not sold on the 'abstract base class is a deadly form of coupling' at all. This may hold in some situations where you have many classes interacting in an architecture or framework that has a high probability of changing/evolving over time, but as I've said before: most code isn't like that. Most code is actually pretty plain and boring, without much need for architecture at all. Two or three levels in your class hierarchy and not more than a handful of class dependencies can solve a surprising amount of programming problems. Indiscriminately discarding inheritance or abstract base classes as 'evil' is naive at best, ignorant at worst. I can come up with many programming problems that are a perfect fit for concrete and/or polymorphic classes deriving from (semi-) abstract base classes, that would most definitely not be better implemented using composition. >> I have spent 2 weeks refactoring one into something which doesn't stick a fork in your eye every time you change something Similarly, I've spent a whole lot more than 2 weeks refactoring big balls of mud created by developers who threw as many classes against the wall as they could come up with, introducing interfaces, delegates, composites, strategy classes, and whatever other design pattern they found in the GoF book, only to arrive at the conclusion that 80% of the code was boilerplate, and the solution that was spread out over all those classes could be implemented by 1/10th the lines of code, concentrated in one or two classes. YMMV depending on the application domain you work in, but having worked on large business logic applications, web applications, and (now) scientific simulation software, my experience is that most complexity in software systems is only there because someone introduced it, not because it is necessary to solve the problem it was supposed to solve. >> So your tests are chock full of empty stub classes rather than mocks? Yuck. Our test cases aren't full of stub classes, and they arent full of interface classes either. Most classes can be perfectly tested in isolation by simply instantiating them and feeding them with static inputs. Some others are tightly coupled and covered by integration tests. None of this is rocket science, we're at over 1M SLOC and we have no problems covering them in our tests.