What does "dependency inversion principle" mean in OOP? - oop

What is meant by the "dependency inversion principle" in object-oriented programming? What does it do?

In object-oriented programming,
the dependency inversion principle refers to a specific form of decoupling where conventional dependency relationships established from high-level, policy-setting modules to low-level, dependency modules are inverted (e.g. reversed) for the purpose of rendering high-level modules independent of the low-level module implementation details.
The principle states:
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.
Source

The main reason for using dependency inversion is to allow for different implementations of those lower-level modules to be selected either at compile-time in the application or at runtime by configuration. This is a big win for testing because it allows you to completely isolate the code being tested and use mock objects.
Another way this is a huge help is for client deployments. Let's say you have different customers with different auth systems, or different databases, or reporting systems, or whatever. You can configure their system at deployment time by changing an XML file to choose the right implementations of those components to load, with no code changes at all.

Related

Should the class diagram represent a specific framework?

I know that frameworks provide useful interfaces and classes that save a lot of time in implementation phase, so my question is:
Should the framework interfaces and classes be included in my project's class
diagram design or not?
and if it is,
Does this affect the reusability of the design if I decided to change
the framework in the future?
UML diagrams are intended to be read by different interest groups. Business likes to see requirements, use cases and activities. Architects/testers need that as a basis to develop/test the system. And the results produced by the architects (static and behavioral class diagrams) are meant to be read by programmers. Each reader group has a focus on certain parts but will eventually peek more or less into border areas (from their perspective).
So to answer your question: yes, frameworks shall be part of the model. Architects should pay attention as to how to cut the system. Frameworks should be designed with a different (broader) scope. So eventually you have frameworks that will be used only partially in a system. Or a system has a potential for a framework and it will be designed to be easily decoupled. Of course, this is a tricky task and architects need lots of experience to fulfill all the needs that come from business and eventually other stakeholders.
No, theoretically it shouldn't, but you're also free to do so.
As stated by the authors of UML: Rumbaugh, Jacobson, Booch
on The Unified Modeling Language Reference Manual at page 25
Across implementation languages and platforms. The UML is intended to be usable for systems implemented in various implementation languages and platforms, including programming languages, databases, 4GLs, organization documents, firmware, and so on. The front-end work should be identical or similar in all cases, while the back-end work will differ somewhat for each medium.

Are there any significant disadvantages to depending upon abstractions? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
After reading this wiki on the Stable Abstractions Principle (SAP) I was wondering if anyone knows any disadvantage to depending on abstractions rather than concretes (i suppose, that outweighs the advantages).
The SAP states that the more stable a package the more abstract it should be. This implies that if a package is less stable (more likely to change) then it should be more concrete. What i don't really understand is why this should be the case. Surely in all cases regardless of stability we should be depending upon abstractions and hiding the concrete implementation?
Robert C. Martin always had a rather obscure way of describing things. His points are always very good but require a bit of deciphering -- "afferent vs. efferent coupling", ugh! Another thing about the way Martin writes is that it's always kind of blurred between descriptive and prescriptive ("would" or "should"?)
"Stability"
First it's important to understand how Martin defines "stability". He defines it in terms of afferent and efferent couplings yielding a stability metric:
instability = efferent / (efferent + afferent)
"Afferent" and "efferent" are such obscure terms. For simplicity, let's use "outgoing dependencies" in place of "efferent couplings" and "incoming dependencies" for "afferent couplings". So we have this:
instability = outgoing / (outgoing + incoming)
It's very much divorced from the likelihood of change, and has everything to do with the difficulty of change. As confusing as it is, by this definition, a "stable" package could still be changing all the time (it would be bad and really difficult to manage though, of course).
If you get a divide by zero error with the above formula, then your package is neither being used nor using anything.
Stable Dependencies Principle
To understand Martin's point about SAP in context, it's easier to start with SDP (Stable Dependencies Principle). It states:
The dependencies between packages should be in the direction of the
stability of the packages. A package should only depend upon packages
that are more stable than it is.
That's pretty easy to understand. The cost of changing a design cascades with the number (and complexity) of incoming dependencies to it. Probably anyone who has worked in a large-scale codebase can appreciate this one pretty quickly where a central design change might end up wanting to break 10,000 really complex parts in the codebase.
So the dependencies should (would?) flow towards the parts that are unchanging, firmly-rooted, unwavering, like a tree flowing down from its leaves towards its roots.
The stability metrics that the roots should have boil down to zero efferent couplings (zero outgoing dependencies). That is, this stable "root" package should not depend on anything else. In other words, it should be totally independent of the outside world. This is the characteristic that defines "maximum stability" according to Martin's metrics: total independence.
Maximum independence = "stable root" (as I'm calling it)
Maximum dependence = "unstable leaf" (as I'm calling it)
Given this kind of totally-independent, ultra stable root design, how can we still gain back a level of flexibility where we can easily extend and change its implementation without affecting the interface/design? And that's where abstractions come in.
Stable Abstractions Principle
Abstractions allow us to decouple implementation from interface/design.
And thus, here comes the stable abstractions principle:
Packages that are maximally stable should be maximally abstract.
Unstable packages should be concrete. The abstractness of a package
should be in proportion to its stability.
The idea is to allow these central root designs to be ultra-stable, as stated by SDP, while still retaining a degree of flexibility for changes which do not impact the core design through abstraction.
As a simple example, consider a software development kit at the heart of some engine and used by plugin developers worldwide. By definition, this SDK would have to have a very stable design given the combination of numerous incoming dependencies (all these plugin developers using it) against minimal or no outgoing dependencies (the SDK depends on little else). This principle would suggest that its interfaces should be abstract to have the maximum degree of flexibility for change without impacting the stable design.
"Moderately abstract" here might be an abstract base class. "Maximally abstract" would be a pure interface.
Concrete
On the flip side, with the abstract is a need for the concrete. Otherwise there would be nothing to provide the implementation for an abstraction. So this principle also suggests that the concrete parts should (would?) be the unstable parts. If you imagine this as a tree (inverted from the usual programming tree) with dependencies flowing downwards from leaf to root, the leaves should be the most concrete, the roots should be the most abstract.
The leaves would typically have the most outgoing dependencies (lots of dependencies to things outside -- to all those branches and roots), while they would have zero incoming dependencies (nothing would depend on them). The roots would be opposite (everything depends on them, they depend on nothing).
This is how I've come to understand Martin's descriptions. They are difficult to understand and I may be off on some parts.
Surely in all cases regardless of stability we should be depending
upon abstractions and hiding the concrete implementation?
Perhaps you're thinking more in terms of entities. An abstract interface for an entity would still require a concrete implementation somewhere. The concrete part may be unstable, and would likewise be easier to change since nothing else depends on it directly (no afferent couplings). The abstract part should be stable as many could potentially depend on it (lots of incoming dependencies, few or no outgoing dependencies), and so it would be difficult to change.
At the same time, if you work your way up to a more dependent package like the application package where you have your main entry point for your application where everything is assembled together, to make all the interfaces abstract here would often increase the difficulty of change, and would still transfer the need to have a concrete (unstable) implementation somewhere else. At some point in a codebase, there has to be dependencies to concrete parts, if only to select the appropriate concrete implementation for an abstract interface.
To Abstract or Not to Abstract
I was wondering if anyone knows any disadvantage to depending on
abstractions rather than concretes (i suppose, that outweighs the
advantages).
Performance comes to mind. Typically abstractions have some kind of runtime cost in the form of dynamic dispatch, e.g., which then become susceptible to branch mispredictions. A lot of Martin's writing revolves around classical object-oriented paradigms. Moreover, OOP in general wants to model things at the singular entity kind of level. At the extreme level, it might want to make a single pixel of an image into an abstract interface with its own operations.
In my field, I tend to use entity-component systems with a data-oriented design mindset. This kind of flips the classical OOP world upside down. Structures are often designed to aggregate data for multiple entities at once with a design mindset looking for optimal memory layout (designing for the machine rather than logically for the human). Entities are designed as collections of components, and components are modeled as raw data using a data-oriented mindset. Interfaces still get abstract for systems that process components, but the abstractions are designed to process things in bulk, and the dependencies flow from systems to central components which are not abstract in the slightest.
This is a very common technique employed in game engines and it offers a lot potential in terms of performance and flexibility. Yet it is in stark contrast to the kind of focus Martin places on object-oriented programming, as it is strong departure from OOP overall.
first of all, from the paper you link to:
stability is not a measure of the likelihood that a module will
change; rather it is a measure of the difficulty in changing a module
so things hard to change (e.g. used in many places) should be abstract to make the extension easy/possible.
and yes, there are disadvantages. it's the easiness of change. it's much easier and faster to change the concrete code rather than abstraction and the code.
Surely in all cases regardless of stability we should be depending
upon abstractions and hiding the concrete implementation?
that is true. but level of abstraction differs. on-the-fly example: if i ask you to compute length of a square diagonal then you will probably just use build-in double sqrt(double) function. is it abstracted? yes. we don't know if there is a newton method used or is it delegated directly to the cpu.
but what if we want to create a sqrt function and rely some kind of physics calculations library on it? is the previous abstraction enough in this case? probably not as we may want to handle (in a uniform way) matrices, relative errors, arbitrary length numbers, parallelization for desired number of cores/threads, maybe delegating to gpu and it should be prepared for other extensions because sooner or later someone may want it to handle NaNs and imaginary numbers.
so it's still sqrt function but level of abstraction is a bit higher. and that's only because lots of code will depend on it. and which function is easier to change?
This implies that if a package is less stable (more likely to change)
then it should be more concrete. What i don't really understand is why
this should be the case.
Abstractions are things that are hard to change in the software because everything depend on them. If your package is going to change often and it provides abstractions, people who depend on it will be forced to rewrite a big bunch of their code when you change something. But if your unstable package provides some concrete implementations, much lesser code will have to be rewritten after changes.
So, if your package is going to change often, it should better provide concretes, not abstractions. Otherwise... who the hell will use it? ;)

How do I facilitate and encourage implementation of my specification?

Suppose I have written a library in a programming language (e.g. Java) to interact with an external component (e.g. a database).
I would now like the community to provide implementations in other languages.
How can I enable and encourage, other developers to provide implementations that are identical in functionality to mine.
Some examples may be:
Provide a written specification of behaviour
Provide a reference implementation
Provide a testing framework so they can validate their implementation (can this be done across multiple languages?)
What other options are available?
Common approach of all that you are after for, can be the abstraction level of Coding conventions. Since they are set of guidelines for a programming languages that recommend programming style, practices and methods for each aspect of a piece program written. These conventions usually cover file organization,indentation, comments, declarations,statements, white space, naming conventions, programming practices,programming principles, programming rules of thumb, architectural best practices, etc.
About
 enable and encourage, other developers to provide implementations that are identical in functionality to mine.
you can use Interfaces (protocols). Even if they are used to define an abstract type that contains no data or code, they also define behaviors as method signatures.
Your examples are good. But in addition to
Provide a testing framework so they can validate their implementation
you can introduce the main ideas of the Test-driven development:
Establishing the goals of different stakeholders required for a vision to be implemented
Drawing out features which will achieve those goals using feature injection
The goal of developer TDD is to specify a detailed, executable design for your solution
read more

Software composition using "components" - Clarification needed

I read a nice definition for software composition here. It says
Software composition is the construction of software applications from components that implement abstractions pertaining to a particular problem domain
Is it the construction of these abstractions where design patterns fit in?
That's a higher level abstraction. Patterns solve common object-oriented problems.
Abstraction, encapsulation, and information hiding are pertinent at the level of individual components. You can do the latter without ever employing patterns.

What is the real difference between "Bastard Injection" and "Poor Man's Injection"

From the Dependency Injection in .NET book I know that the object graph should be created at the Composition Root of the application which makes a lot of sense to me when you are using an IoC Container.
In all the applications I've seen when an attempt to use DI is being made, there are always two constructors:
the one with the dependencies as parameters and
the "default" one with no parameters which in turn calls the other one "newing" up all the dependencies
In the aforementioned book, however, this is called the "Bastard Injection anti-pattern" and that is what I used to know as "Poor Man's Injection".
Now considering all this, I would say then that "Poor Man's Injection" would be just not using an IoC Container and instead coding all the object graph by hand on the said Composition Root.
So my questions are:
Am I understanding these concepts correctly or am I completely off track?
If you still need to register all the dependencies in the IoC container vs. coding them by hand in the exact same Composition Root, what's the real benefit of using an IoC container?
If I have misunderstood what "Poor Man's Injection" really is, could someone please clarify it?
When it comes to DI, there's a lot of conflicting use of terminology out there. The term Poor Man's DI is no exception. To some people, it means one thing and to others it means something different.
One of the things I wanted to do with the book was to supply a consistent pattern language for DI. When it came to all of those terms with conflicting use, I had two options: Come up with a completely new term, or pick the most prevalent use (according to my subjective judgment).
In general, I've preferred to re-use existing terminology instead of making up a completely new (and thus alien) pattern language. That means that in certain cases (such as Poor Man's DI), you may have a different notion of what the name is than the definition given in the book. That often happens with patterns books.
At least I find it reassuring that the book seems to have done its job of explaining exactly both Poor Man's DI and Bastard Injection, because the interpretation given in the O.P. is spot on.
Regarding the real benefit of a DI Container I will refer you to this answer: Arguments against Inversion of Control containers
P.S. 2018-04-13: I'd like to point out that I've years ago come to acknowledge that the term Poor Man's DI does a poor (sic!) job of communicating the essence of the principle, so for years, now, I've instead called it Pure DI.
P.P.S. 2020-07-17: We removed the term Bastard Injection from the second edition. In the second edition we simply use the more general term Control Freak to specify that your code "depend[s] on a Volatile Dependency in any place other than a Composition Root."
Some notes to the part 2) of the question.
If you still need to register all the dependencies in the IoC container vs. coding them by hand in the exact same Composition Root, what's the real benefit of using an IoC container?
If you have a tree of dependencies (clasess which depend on dependencies which depend on other dependencies and so on): you can't do all the "news" in a composition root, because you new up the instances on each "bastard injection" constructor of each class, so there are many "composition roots" spreaded along your code base
Wheter you have a tree of dependencies, or not, using an IoC container will spare typing some code. Imagine you have 20 different classes that depend on the same IDependency. If you use a container you can provide a configuration to let it know which instance to use for IDependency. You'll make this in a single place, and the container will take care to provide the instance in all dependent classes
The container can also control the object lifetime, which offers another advantage.
All of this, apart of the other obvious advantages provided by DI (testability, maintainability, code decouplig, extensibility...)
We've found, when refactoring legacy applications and decoupling dependencies, that things tend to be easier when done with a two step process. The process includes both "poor man" and formal IoC container systems.
First: set up interfaces and establish "poor mans ioc" to implement them.
This decouples dependencies without the added overhead (and learning
curve) of a formal IoC set up.
This also reduces interference with the existing legacy code. Nothing like introducing yet another set of issues to debug.
Development team members are never at the same level of expertise or understanding. So it saves a lot of implementation time.
This also allows a footing for test cases.
This also establishes standards for a formal IoC container system later.
This can be implemented in steps over time by many people.
Secondly: Each IoC system has pros & cons.
Now that an application standard is established, an educated decision
can be made in choosing an IoC container system.
Implementing the IoC system becomes a task of swapping the "poor mans" code with the
new IoC system.
This can be implemented in steps over time and in parallel with "poor man". It is better to head this with one person.