7

I've been reading Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin. One point he makes:

G34 Functions should descend only one level of abstraction

However, I'm wondering about the functions that actually create the objects for an application. Say we have dependencies

App -> Book -> Database

Then using dependency injection, App would need to create both a Book and Database instance, and inject the database instance into Book.

  • Is this an acceptable breaking of the mixing level of abstraction rule?
  • If yes / no, why?
5

Let's start by defining what an abstraction is. In Agile Principles, Patterns, and Practices, Robert C. Martin defines an abstraction like this:

Abstraction is the elimination of the irrelevant and the amplification of the essential

What is essential, and what is irrelevant, changes with the client's perspective. From the perspective of the Composition Root, the relevant abstractions are types (classes, primitives, etc.) and the graph(s) composed from objects.

The purpose of the Composition Root is to construct a valid object graph. In that perspective, all types have the same level of abstraction. They're just types being composed together; the Composition Root doesn't invoke any of the object members, so it's irrelevant whether the object originates from a UI package, a Data Access package, a Domain Model package, etc. The abstraction is Composition.

  • Thanks! (All good answers, but I've chosen this one due to the external reference). – Michal Charemza Apr 14 '14 at 5:21
4

My interpretation is that the levels of abstraction don't refer to what objects a function can see; it refers to what it's actually doing. It's not inconceivable that a function may have to receive and pass along an object from a much lower level of abstraction in the process of delegating tasks to the level immediately below it. But it shouldn't be operating on such an object directly.

If you're gluing stuff together in main, then that's one level of abstraction. I don't feel there's any violation of abstraction here, because main knows nothing about these objects except that one needs the other. If you need to do anything more complicated to glue things together, like look up something in a config file to know what to glue together, that's a different level that should be pushed somewhere else.

3

If we were to generalize the rule a bit and make it less legalistic, it might look something like this:

Limit what each module/class/function/etc knows about the rest of the system.

So you have two types of code:

  1. Code that glues our layers of abstraction together (App)
  2. The meat of the software that actually solves a problem (Book, Database)

Glue code exists solely so the meat code can do its work. Regardless what kind of code you're writing, you don't want your functions or modules to know too much about other areas of your software. This is for the sake of maintainability; the more pieces of code a module touches, the more likely you will have to change code in all those areas. Yuck.

Now when you're writing the "glue" code, you will obviously have to deal with modules that are spread widely throughout your software. But you're not actually using those modules. The glue code is actually quite dumb. It's calling some constructors and mostly just shuffling around references to objects. If your application gets large enough, your glue code will indeed get quite large and complex, but that's what the factory pattern is for.

So in my eyes, it's ok to traverse through multiple layers of abstraction in the glue code, so long as your glue code stays uninformed about how those layers are actually working together.

Side note:

You should look into inversion of control containers. They are great little tools that allow you to really minimize how much glue code you have to write, thereby potentially increasing maintainability.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.