0:00 / 0:00 The dependency inversion principle is very simple, and it states that high level parts of the system
should not depend on low level parts of the system directly, that instead they should depend on some
kind of abstraction.
So this might once again seem very cryptic.
I mean, what does it mean to have high level and low level parts of the system?
Well, we’re going to take a look at a scenario right now.
So what I’m going to do is I’m going to model a genealogy relationship between different people.
And we’re kind of imagining a system where you perform queries on a genealogy database.
So first of all, we may as well define certain relationships, a relationship between two people.
Like, for example, you can be a parent, you can be a child.
That’s going to be an enum, by the way, not a class.
So enum, you can be a parent, you can be a child, you can be something else like a sibling, for
example.
So these are relationships that you can have between people and then we can define person.
And here I’ll do a very simple definition.
I’m just going to have the name of the person.
Although strictly speaking, in a genealogy application, you would have other information as well.
Like for example, you might have the date of birth, for example.
So these sorts of things.
Now I’m going to just ignore everything except for the name, just for the sake of simplicity.
So now that we have this kind of setup, we may as well define the low level and I’m going to write
it here low level parts of the system by defining relationships between different people.
And I’m going to put them in a class called Relationships like so.
And here we are just going to have a flat list of relations between different people.
So I’ll have a private list and I’ll use the C sharp seven tuples here.
So I’ll have the person from which the relationship stems, the relationship itself and the person to
which the relationship applies.
So person, relationship, person as a value tuple as relations.
And I’m just going to initialize it with a new list.
Let’s try and do it this way.
So person, relationship and person again, there we go.
So this is a set of relationships that we have at a low level.
And what we can do then on the low level is we can define some sort of API for actually adding a parent
and child relationship.
So I’m going to do it very crudely.
I’ll have add parent and child and here I will have the person parent and the person child and I’ll
just duplicate the data.
Not something that you do in a typical database, but something that is very popular in today’s design
when you just duplicate data for the purposes of speed of access.
So I’m going to say relations dot Add and then I’ll add parent with the relationship of parent that
should be relationship dot parent to the child.
And then of course I’ll add the reverse relationship just for the fun of it.
So here you would have child relationship, dot child, child and then the parent.
This is completely spurious.
I’m just adding it to show that you can store things in this kind of denormalized way.
Okay, so this is our low level API and the question is, well, how do we actually perform research
on it?
If we want to find out all the children of a particular person, well, let’s actually do this.
Let’s make a class called research.
Now, in the main method I’m going to set up a scenario, so I’m going to have a bunch of people.
I’ll have the parent as a new person with the name, with the name John, for example, and then I’ll
have children.
So we’ll have child one, child one like so and Child two here as well.
Child two And I’ll call them Chris and Mary.
There we go.
So John has two children and we can define the low level relationships module to kind of encompass this
information.
So we present this information.
We say var relationships, relationships equals new relationships like so.
And then I can say relationships, dot add parent and child.
So parent child one And then duplicate and Child two There we go.
So we’ve set up the scenario and now we actually want to perform the research.
So we want to take the low level module and we want to somehow access it in a high level research module
that we have here.
So one of the ways, not the best way is to simply allow the high level module, the research module,
access to some of the internals of the low level module by giving it the list of relations.
So you can do something like the following.
If we make a constructor for research, we can have a constructor which actually takes relationships
as the argument.
We take the relationships, as the argument we pass in relationships in here.
So we say new research with relationships.
And then of course what you want to do is you.
One to access all of their relationship information.
So how do you do it?
Well, one of the ways is you simply expose this private field as a public property, and that’s pretty
much it.
So in this case, what we can do is we can write something like public list of person relationship person.
We expose this list called relations with a capital R as relations like so.
Okay, so we just made a property getter for this field.
And what we can now do is we can perform some sort of research by accessing this low level interface.
So I can say var relations equals relationships dot relations like, so I’m accessing that property
that we just made and then I can go ahead and look for all the children of John.
So I can say var in relations dot where and I can start filtering.
So I can say, Well, I need the name to be John.
So if we take X as the argument I x dot item one dot name to be equal to John and I want the relationship
type.
So x dot item to I want the relationship type to be relationship dot parent.
So I want somebody for whom John is a parent.
So then I can do a loop and I can actually write line.
I can write line here that John has a child called R dot item three dot name.
Okay, So this is a workable scenario.
It will actually work if I sort of compile and execute this.
But there is a bit of a problem here that we need those references to value tuple.
Of course, since I’m using the C-sharp seven variety and we’re still in Visual Studio 2017, I use
value tuple to get my package in here.
There we go.
All right, let’s get those value tuples in.
And now hopefully if I execute this, I get my result.
So John has a child called Chris and another child called Mary.
There we go.
Now, the problem with this scenario and the reason why the dependency inversion principle exists is
that we’re accessing a very low level part of the relationships class.
We’re accessing its data store and we’re accessing it through a specifically designed property which
exposes the private thing as public.
And what this means in practice is that relationships cannot change its mind about how to store the
relationships.
So the relationships class cannot say, Oh, this is a bad way.
I’m using tuples, I’m depending on this value type.
I’m going to start using a dictionary and I’m going to remove this duplication as well or something
to that effect.
So it is very bad idea because then the relationships class can no longer really change in terms of
the way it stores data.
So a better way which would allow relationships to change is to provide a form of abstraction by simply
defining an interface for how the relationships class actually allows you to access certain high level
data.
So, for example, I can define an interface called I relationship browser.
So this concept will actually give us particular information like, suppose I want to be able to find
the children of a particular person.
So what I can do is I can put here a member which returns an enumerable of person, and it’s going to
be called Find all Children of.
And you specify the name of the person whose children you actually want to find.
And then, of course, what I can do instead of this thing, instead of doing this, which is horrible,
I’m going to comment out.
This whole thing is well, actually, not just the not just the constructor, but let’s comment out
the entire body as well is I’m going to actually implement the interface that we’ve just made, which
finds all the children of a particular person.
I’m going to implement this in our low level construct.
So here I’m going to say I relationship browser.
Like.
So I’m going to implement the missing members.
And here all I have to do is I have to basically replicate the kind of call that I’m doing here, except
it’s obviously going to be slightly different.
So if I just copy this over, let me just paste this in here and I’ll uncomment this as well.
So what we’re trying to do is we’re trying to search everyone with the particular name.
So instead of John, we put the argument which is name like so and then we simply you’d return the actual
value, which in this case is R, and we can actually simplify this because none of this is required.
Well, it’s not R, it’s R dot item two.
That’s the child item three, rather.
That’s the child that we want to return.
So I’m going to simplify this, convert this into a basic link.
And there we go.
So we now have a low level module, but instead of depending on this low level module, we depend on
an abstraction, which is the AI relationship browser.
So what we can do in our research module, which is the high level module, is we can build another
constructor, but this time around we don’t depend on relationships.
Instead we depend on this AI relationship browser interface called browser.
And here, if I want to find all the children of John, all I have to do is say for each var p in browser
dot find all children of John and I can once again write line.
John has a child called and then just say p dot name also looks better in terms of the API.
And of course if I execute this I get exactly the same result, but I get it without the dependency
on low level implementation detail.
So now what happens is relationships can actually go ahead and change the way that it stores the relationships.
It can change the underlying data structure because it’s never exposed to the high level modules which
are actually consuming it.
Play Play Play Play Play Stop Play