I was reading an interesting interview with the creator of Erlang and something that was suggested was that OOP can lead to code being less rather than more re-usable, by virtue of the creation of highly specific "black boxes" which people are reluctant to tamper with.
Ok, there are a lot of subjective things in that statement.
He hypothesized that OOP can lead to code being less re-usable. Less reusable than what other programming methodologies? How did he gauge how reusable the code was? How did he gauge how much of the code was reusable and how big was/were the codebase(s)? What language was he referring to?
He is comparing classes to "black boxes" and stated that people (assuming programmers) are "reluctant to tamper" with them.
... wow lots of chaos in that statement, let's go through it ...
- By "Black Box" one could assume he is referring to the entire class. I would say the class cannot be compared to a black box because it's very easy to read the code for a particular class. Whether the code is readable or not is another story, but that's not the point.
- He could have meant that the methods were black boxes which once again isn't accurate because it's easy to read the code.
- If he meant that the methods are black boxes because you don't know where else they are used in the codebase, one could easily use grep to find out.
- If he meant that the methods are black boxes because you don't know if they are used outside of the class itself, that's what public, private and protected are for.
- He stated that people are reluctant to tamper with the black boxes, but if the black boxes were reuseable, then why would one need to modify them in the first place?
- Further, when a method is written to perform a function, it should not matter to any outside caller how that function is accomplished, therefore why would it not be acceptable to make modifications (tamper)?
With all that said, what's the best way to reuse code?
My opinion is that when you are working on a codebase adding code, if you find something that has already been done, utilize decomposition by refactoring the code that can be reused. In other words if there is a method of some class that does something, and you create another class that needs to do the same thing, then the code should be moved from the first class to a location where it can be used from both classes. I think where developers get in trouble is when they want *everything* to be reusable and try to write the codebase with that mindset. That flat out doesn't work. It doesn't work because even if you did get it to work, you just doubled or tripled development costs. Then add in TDD. What you bid $10k I can do for $2k. You know damn well if there are any bugs the client will be the first to find them anyway, no matter how much testing you do. You can only minimize them.
The problem with developers who seek perfection of re-useability and extendability is that they end up with codebases that are so complex and large that they are no longer practical. Sure, from a technical perspective it all looks good on paper. The reality is that *cough magento* they are so slow you need a combination of a dedicated server and extreme caching measures to get decent speed out of them, debugging becomes a nightmare, and it ends up costing the client money in the form of lost revenue and increased development costs because all things being equal pages loading slower (measured in milliseconds) can translate into a good percentage of lost sales, and it takes longer to build in the first place.
I can tell you from experience that it's certainly possible to create simple codebases that perform complex tasks very efficiently. Take JSON vs SOAP vs XML. What's easier, faster, less resource intensive, faster to build and easier to debug? An array? Or a glob of files, folders and wsdl crap? Sure, that's one component, but when you create entire systems out of simple components you get raw speed and raw power. Is it reusable? Of course it is. But even if it wasn't, the code is so simple you can modify it quickly without tracing code through dozens of classes. And then my friend, you no longer have to worry about "black boxes".
I'm going to take a huge stab in the dark here and make a guess that you've never worked on a team of developers that actively collaborate with each other and with the client. By actively collaborate, I do mean they - the client - are involved in the development process as often as is humanly possible, not "here's the spec doc, see you in 6 months with the finished product."
Using ATTD, BDD and Pair Programming really, really does remove *all* bugs from the system. For over 4 years we (the team I am currently on) have been bug free. We started using BDD/ATTD 4 years ago. Continuous deployments also assist this.
Doing this actually makes it harder for bugs to get in than not. This has led to us demonstrably cutting development time down. Our lead time (ROI) on user stories/requirements/change requests is roughly half that of what it was before. We have continuous integration servers running our tests the moment we make a commit. Every time they pass, the software is automatically deployed to the clients system for their leisure. This basically means instantaneous software delivery. The moment I have committed some code, and providing the tests pass, boom.. the software has been delivered. No need to wait for a release.
We use TDD for 100% of our code. That's right, we don't have a single line of untested code. We have coverage metrics to assert this. Our Unit tests are derived from our Integration tests, and our Integration tests are derived from our Acceptance tests. New acceptance specs agreed with client added to suite. Obviously, they will fail - so what is it that is stopping it from passing? Remember to have the mindset of only do what is necessary to resolve the immediate issue. Ok, we need an integration test for that small area of functionality (some like to call it a sub-feature - I don't.) That test will now also fail. Ok, what is stopping that from passing? Need a unit test to start fleshing out the code to make that integration test pass. A few unit tests later, the integration test now passes. Great. But the acceptance test is still failing. Ok, next integration test. Etc. etc. until all green => deployed. Customer has what they requested at the soonest possible moment.
The biggest benefit of TDD is the documentation that the tests/specs provide. Nothing documents code better than the executable specs used to create it.
Write your acceptance tests with the client. If you can't be in the same room as each other, then agree them with the user in their executable form. Cucumber or similar testing frameworks make this impossibly easy for the non-tech-savy users to do this. If the test is passing, but the system "has a bug" it means the client didn't understand what they wanted. In short, they told you to make it do the wrong thing. This cannot be avoided without plenty of discussion between your client and you, and the "damage" will be a darn sight less widespread if you are in a fast-feedback loop with the client.
Anyway, I digress.
I'm going to flat out disagree with your statement about "developers that seek perfection ... etc". It is completely the opposite. A class should only ever have one responsibility and/or role. That is a simple class to maintain, to reuse, and to identify. A large class with multiple responsibilities is very difficult to modify, takes much more brain power to comprehend the consequences, and when looking for the thing that does that particular job, it is harder to locate.
Yes, how a method accomplishes its task should not matter to the outside caller - but if you do tamper with it, it must still perform that function, and perform it with expected results. This is difference between a refactoring and a change. If you change
a method, the caller will need to change - i.e. you've broken it. If you refactor
a method, the outside caller will not know the difference. This thread is firmly discussing change
- not refactoring.
Your comparison of JSON vs SOAP vs XML is .. well .. nonsensical. Any of those can be in "an Array", just as any of those can be in "wsdl crap". If I'm brutally honest, that last paragraph just shows you don't understand the technologies you speak of. WSDL is used to describe
your service to client applications (W
anguage). This means it is used once
to generate a client-proxy so the client app just has to make method calls to what might as well be "just another object". *All* of the plumbing is handled automatically. You don't even serialise the data yourself. That is far superior to your suggested alternative of passing an array around. It also means that my service that offers a WSDL can be used by *any* platform, and I am using universally recognised protocols - not a proprietary one.
Yeah, I might quote my clients $10k for what you can quote $2k, but when they want something changed at a later date, it'll cost them a fraction of what your clients will need every time they want a change. I'll also be able to deliver quicker. My clients just have to agree new acceptance test with me/future developer to introduce new behaviour. You clients will need to let their developers (that's you included - you think you'll be able to remember what's going on in that app in 6 months time?) read through the code, setup test environments, etc. etc.