C
C#ā€¢17mo ago
stigzler

ā” Help with Dependency Inversion Principle

Hi - could anyone help me with understanding the dependency inversion principle, please? Done lots of reading, but I just cant get it. Some quickly constructed code below as an example. How would I use DIP in this scenario?
public interface ICacheManager
{
Directory CacheRootDirectory { get; set; }
Image NoImageFileImage { get; set; }
Image CorruptImageFileImage { get; set; }

void CacheImage(Image image, string path, string filename, Size? resizeTo = null);

void RetrieveImage(string path, string filename);

void ClearCacheFolder(string path);
}

public class CacheManager
{
public Image RetrieveImage(string path, string filename)
{
Image cachedImage
// Stuff here

return cachedImage
}
}

public class GamesListViewer
{

CacheManager cacheManager;

public void PopulateCacheImages()
{
for each (ListItem item in GamesList.Items)
{
item.Image = cacheManager.RetrieveImage("\Boxart\Front", item.filename);
}
}


}

public interface ICacheManager
{
Directory CacheRootDirectory { get; set; }
Image NoImageFileImage { get; set; }
Image CorruptImageFileImage { get; set; }

void CacheImage(Image image, string path, string filename, Size? resizeTo = null);

void RetrieveImage(string path, string filename);

void ClearCacheFolder(string path);
}

public class CacheManager
{
public Image RetrieveImage(string path, string filename)
{
Image cachedImage
// Stuff here

return cachedImage
}
}

public class GamesListViewer
{

CacheManager cacheManager;

public void PopulateCacheImages()
{
for each (ListItem item in GamesList.Items)
{
item.Image = cacheManager.RetrieveImage("\Boxart\Front", item.filename);
}
}


}

20 Replies
Omnissiah
Omnissiahā€¢17mo ago
i guess that - CacheManager should extend ICacheManager - ICacheManager should be registered in the di with CacheManager as concrete class - GamesListViewer should have a constructor that takes ICacheManager and set cacheManager this from looking at the code for 2 seconds and not one more
mtreit
mtreitā€¢17mo ago
Yes, you should be using the ICacheManager interface and not depending on the concrete CacheManager class The fact that ICacheManager is defined but not used seems like a mistake.
Omnissiah
Omnissiahā€¢17mo ago
it's like it's a sort of draft
stigzler
stigzlerOPā€¢17mo ago
thanks both - yes - it's just draft code. I understand you use INterfaces with DIP? Hence the interface. Just don't know how to apply it. Sorry - I'm an amatuer coder
Omnissiah
Omnissiahā€¢17mo ago
how you use this stuff depends on what are you using to realize di maybe it's not the answer you expected
stigzler
stigzlerOPā€¢17mo ago
food for thought. Let me draft some code to see if it matches what yuou're suggesting
phaseshift
phaseshiftā€¢17mo ago
I think what dont is alluding to is whether youre using a DI container or not
mtreit
mtreitā€¢17mo ago
Basically your GameListViewer should have a constructor that takes an ICacheManager as a parameter and uses that for any cache-related operations. That's dependency injection in it's most basic form. The code that constructs the GameListViewer can inject different implementations of the cache manager depending on the need (this is often useful for unit testing, among other things.) Whether you then use a DI framework on top of that to automatically handle injecting some registered types at object construction is up to you. You can just do it manually rather than using a DI framework, or you can choose to use a DI framework.
stigzler
stigzlerOPā€¢17mo ago
Really helpful, thanks. I'm trying to stick with doing it without a framework first, so I get to undertsand the basic principles OK, some code (just sketched - not tested/done in an IDE):
public interface ICacheManager
{
Directory CacheRootDirectory { get; set; }
Image NoImageFileImage { get; set; }
Image CorruptImageFileImage { get; set; }

void CacheImage(Image image, string path, string filename, Size? resizeTo = null);

void RetrieveImage(string path, string filename);

void ClearCacheFolder(string path);
}

public class CacheManager: ICacheManager
{
public Image RetrieveImage(string path, string filename)
{
Image cachedImage
// Stuff here

return cachedImage
}
}

public class GamesListViewer
{

ICacheManager cacheManager;

Public void GamesListViewer(ICacheManager cahceManager)
{
this.cacheManager = cahceManager
}

public void PopulateCacheImages()
{
for each (ListItem item in GamesList.Items)
{
item.Image = cacheManager.RetrieveImage("\Boxart\Front", item.filename);
}
}

}

public class Main
{
GamesListViewer gamesListViewer;

public void Main()
{
gamesListViewer = new GamesListViewer(new CacheManager);
}
}
public interface ICacheManager
{
Directory CacheRootDirectory { get; set; }
Image NoImageFileImage { get; set; }
Image CorruptImageFileImage { get; set; }

void CacheImage(Image image, string path, string filename, Size? resizeTo = null);

void RetrieveImage(string path, string filename);

void ClearCacheFolder(string path);
}

public class CacheManager: ICacheManager
{
public Image RetrieveImage(string path, string filename)
{
Image cachedImage
// Stuff here

return cachedImage
}
}

public class GamesListViewer
{

ICacheManager cacheManager;

Public void GamesListViewer(ICacheManager cahceManager)
{
this.cacheManager = cahceManager
}

public void PopulateCacheImages()
{
for each (ListItem item in GamesList.Items)
{
item.Image = cacheManager.RetrieveImage("\Boxart\Front", item.filename);
}
}

}

public class Main
{
GamesListViewer gamesListViewer;

public void Main()
{
gamesListViewer = new GamesListViewer(new CacheManager);
}
}
obs not complete, but hjopefully should relate to the principles suggested
mtreit
mtreitā€¢17mo ago
Yes that's the basic concept. People throw around lots of software engineering principles, a lot of which can be questionable, but in my opinion the two most important concepts that absolutely should be followed are: Loose coupling. High cohesion. Dependency Injection is a good way to help promote loose coupling.
stigzler
stigzlerOPā€¢17mo ago
Ah, great. I think the problem I'm having is that I'm so used to the habitual process of making a tightly coupled/non interfaced class when the needs arises in any class I'm working on. For example, in this scenario, I'll be working on GamesListViewer and identify need for separate Caching Class. I need a rule of thumb on how to think differently about it. From this - you kind of have to go either side of the class you're working on? That is, provide the correct Caching Class from the instantiating class of the class that has generated the need (i.e. GamesListViewer created the need for CacheManager, but I have to provide that CacheManager class from the class that creates GamesListViewer) Inversion and upside-down to my presnt simple approach šŸ¤Æ So working practice could be: 1. Create an interface class 2. Create a class that implements it with the relevant code. 3. Pass an instance of a Class implementing the interface to the constructor of the class being worked on (in this case GamesListViewer) Think I get it now
mtreit
mtreitā€¢17mo ago
"interface class" isn't a thing, but yes I think you have the right idea.
stigzler
stigzlerOPā€¢17mo ago
sorry - yes of course
phaseshift
phaseshiftā€¢17mo ago
technically this is (just) dependency injection. Inversion is something connected to, but not exactly the same
stigzler
stigzlerOPā€¢17mo ago
The way my simpleton brain is understanding it is that you have to work both backwards and forwards to make it happen
Korbah
Korbahā€¢17mo ago
for what it's worth, VS has quick actions that allow you to create interfaces from classes in quick actions
stigzler
stigzlerOPā€¢17mo ago
ah yes - I've seen this on various vids
Korbah
Korbahā€¢17mo ago
yeah, I use that quite often with my approach. The option appears if you right click the class declaration You can also easily create methods on a class, and then pull it up to an existing interface
stigzler
stigzlerOPā€¢17mo ago
Thanks all - that was really helpfull. Now just got to try an implement it in my dev code
Accord
Accordā€¢17mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server