|
TNCaver wrote: code that uses an external object still has to know about it's properties and methods in order to use them, so how does passing the object or interface to the constructor or a setter make the code any less dependent than using the concrete object with the 'new' keyword?
Imagine a door. On that door is a knocker. Imagine another door. On the side of that door is a doorbell. Imagine an interface. The interface has one method: "Announce" -- Announce someone is waiting at the door."
Now go knock on the door or ring the doorbell. They both implement "Announce". I think I'll stop now about injecting knockers.
TNCaver wrote: I'm learning about DI, and it kind of makes sense, but I'm not convinced it solves the loose coupling issue.
It does, if you don't abuse it. And boy, have I seen abuse. However, I avoid DI like the plague because it flips the problem on its head. Most DI's that I've seen basically express in metadata (XML, JSON, whatever):
Property-of-instance-Foo-gets-initialized-with-interface-Eye.
And worse, some of those DI's will instantiate Foo for you if it doesn't exist at the point in time of the injection. Massive entanglement of dependencies ensues -- what if the instance of Foo is itself a dependency?
Now, you also have another choice. Foo itself can implement an interface that defines the property (of some interface type) that the DI is told about and can set. That's pretty quick. Otherwise, the DI has to rely on reflection to set the property of Foo, because it doesn't know what type it is and has no interface to map Foo's property to a "known" interface. Reflection is slow.
Now magnify that problem with hundreds, if not thousands, of entangled dependencies, all specified in some XML file (yes, I've actually seen this) and you get a nightmare that is impossible to debug, slow to instantiate, and brittle to changes.
I'm old fashioned. Give me a factory pattern. DI is a bad solution looking for an already solved problem.
Latest Article - A Concise Overview of Threads
Learning to code with python is like learning to swim with those little arm floaties. It gives you undeserved confidence and will eventually drown you. - DangerBunny
Artificial intelligence is the only remedy for natural stupidity. - CDP1802
|
|
|
|
|
I think you may have scared him off. I know I would be if I didn't know any better. Just saying...
|
|
|
|
|
Slacker007 wrote: I think you may have scared him off. I know I would be if I didn't know any better. Just saying...
That was the idea.
Latest Article - A Concise Overview of Threads
Learning to code with python is like learning to swim with those little arm floaties. It gives you undeserved confidence and will eventually drown you. - DangerBunny
Artificial intelligence is the only remedy for natural stupidity. - CDP1802
|
|
|
|
|
If you think 'goto' is evil, try writing an Assembly program without JMP.
|
|
|
|
|
LOL Nah, but Marc's comment does give me an alternate perspective, which gives me choices.
If you think 'goto' is evil, try writing an Assembly program without JMP.
|
|
|
|
|
Yeah, lot's of feedback on this, which is good.
|
|
|
|
|
DI is a very useful tool as many others have already pointed out. But it's just that, a tool. Don't fall into the "everything looks like a nail because I just learned how to use a hammer" trap. Also don't confuse DI the concept with DI frameworks. DI frameworks are not required to implement the concept of DI. The frameworks are for a very specific situation (very complex DI "webs") and are grossly overused. All you're doing is offloading coupling to external code you have no control over and incorporating extra dependencies into your software. Make sure that trade-off is worth it.
So consider the details of the situation. In the case of a database class, even if your software only uses a single database now, there are multiple kinds that may be supported in the future. So an interface would be the way to go. Since we're using an interface anyways, why couple a class that uses the database class with a concrete derivative of that interface? DI is the way to go here... maybe.
You also need to consider how your software is going to be used. Is it an application, framework, library, service, etc? If the end-user isn't using the class directly and you want to keep things as loosely coupled as possible internally, you may not have an issue with needing to type something like new Something(new MssqlDb(new AccessInfo()), new RegionFormatter(someRegion), ...more stuff) . If the end-user is going to be using this class directly, they'll probably appreciate some default new Something() which is going to require coupling to concrete classes in that constructor. Consider how ugly and bloated code would look if, say, .NET removed all but the most complex constructors for their entire framework just to be "less coupled."
I guess what it boils down to is that whether it's useful depends on the specifics. Use common sense, use experience, and use others' advice. There is no perfect solution to every problem; it's often a cost-benefit analysis.
EDIT: Also as Marc above pointed out, sometimes the factory pattern is the best solution. I tend to think of this as a form of DI except instead of needing a concrete class passed in, you need information that can be used to generate that concrete class. In both cases, the class using the interface isn't coupled to the concrete class.
|
|
|
|
|
Where the coupling is reduced is the dependencies of the interfaces/classes that are being injected.
If your class needs a IWidgetService to run, you don't have to know that the implementation IWidgetService requires an instance of the WonderfulApiClient class, a WhatchamacallitRepository, and a AppConfiguration object, or any or their dependencies. You don't have to worry about the construction.
Furthermore, if the implementation of IWidgetService changes to require different dependencies, your code that uses the IWidgetService doesn't have to change, unless you've changed the API for the IWidgetService.
This keeps minor changes in your code from requiring a cascading set of changes to the users of your classes.
"Time flies like an arrow. Fruit flies like a banana."
|
|
|
|
|
I think that's a good question and you do have something of a good point regarding loose coupling.
However no matter how loosely coupled you want the code to be, at some point there needs to be some agreement between the calling code and the called code as to which methods/functions are called and are available.
That's where interfaces come in - all they do is define the available operations and not the actual implementation.
That then means that as long as your external library implements what is specified by the interface you can then use that library.
There is always going to be some form of coupling in code - it's just that it's generally better to aim for less dependency between different areas of code.
“That which can be asserted without evidence, can be dismissed without evidence.”
― Christopher Hitchens
|
|
|
|
|
Think of the different pieces of your code as "services" for the other parts.
If you can imagine module X as a service for module Y, then that is a good place to introduce an X interface that is injected into module Y.
Even if you do not use IOC, it is still a cleaner design.
I like lazy initialization with IOC concepts. If nothing has been injected when you need it, go ahead and new() the Production service by default.
|
|
|
|
|
Dependency Injection or DI is a highly complex framework that is perfect for those situations where one needs to rely on the application processes to be able to determine the type of object it requires. Such a requirement is most often found in highly complex applications such as financial trading applications or military war gaming simulations (military, non-commercial) as both require to be able to determine hundreds if not thousands of object types.
That being said, the interest in DI in business development has become over infatuated with the powerful capabilities of DI whereby development teams believing they need such capabilities implement it into their applications as a course of development policies in their shops.
The problem is that Dependency Injection has similar issues as those of complex inheritance schemes, many of which have been found to have failed, are performance hogs, or have become so complex as to make maintenance very difficult. Such issues with complex inheritance were outlined during the later 1990s and the early 2000s.
The result is that today, one rarely hears anything regarding the successes of complex inheritance as much of such success is limited to military and scientific applications as well as highly complex financial applications and other such development endeavors in the business development arena. As a result, such applications are few and far in-between and not the norm for application development.
DI does in fact have issues with efficiency simply due to its own interpretation of how to select the object types it requires. And if one is not careful, using DI can turn an application's performance into a sluggish nightmare.
Admittedly, even with my very long career in software development and engineering which entailed the development of quite a number of complex systems, no one ever suggested the use of Dependency Injection since there simply wasn't a need for it.
Most applications in business development do not require the interpretation of many different kinds of objects and so are quite capable of being developed with semi-loose coupling or tight coupling of objects. Besides, the idea of loose-coupling which has achieved an almost paradoxical paradigm of priority in many application development endeavors has only added to any application's complexity unnecessarily.
Loose-coupling and other such paradigms such as DI have come about as a result of technical managers and leads "imagining" what-if scenarios for their application requirements. Such ruminations by many such technical personnel have been the bane of software development in recent years. This is because, the majority of applications are designed for specific requirements which are needed within a specific time period of immediacy.
Those that have gone over the deep end into predicting what is needed within the future of an application are often wrong and subsequently have had their teams create applications that are grossly over complex for the actual needs of the system.
Though DI is of course a perfect attribute to those applications that actually require it, such applications to be beneficial to the organizations they will serve must be well designed to handle the capabilities that DI will provide. With today's ever increasing speeds of development cycles that push applications and application modifications into production at ever increasing rates, such quality designs simply cannot be created to take advantage of such paradigms as DI efficiently.
This result is a direct consequence of such development concepts as Agile and DevOps, which are both touted as silver-bullets for the ability to implement final products at high rates of speed. This is corroborated by the increasing reports for example of Agile not being able to scale appropriately for much larger systems since you can only push complexity in development so fast...
Steve Naidamast
Sr. Software Engineer
Black Falcon Software, Inc.
blackfalconsoftware@outlook.com
|
|
|
|
|
Maybe this will help. IoC is a design/architecture concept, DI is an implementation approach. With IoC your are doing structural inversion on object lifecycles. Commonly an operating object will "new" any objects it needs and then invoke operations. With IoC the objects are supplied and the operating object invokes operations on this supplied object. So, behavior lives in the operating object, but the structure has been factored out to a higher level - inverted. Since Interfaces define behavior, they are a common construct in IoC/DI. The simplest implementation of IoC doesn't need DI. This is IoC:
Class Foo {
public string Bar(Decoder decoder, string inString){ return decoder.decode(inString); }
}
You have inverted control of lifecycle management. The caller creates and supplies the structure (the object doesn't "new" any ones it wants), and the operating object operates on these objects.
More commonly, you do this in the constructor.
Class Foo {
readonly Decoder _decoder;
public Foo(Decoder decoder){
_decoder = decoder;
}
public string Bar(string inString){ return _decoder.decode(inString); }
}
In some ways, the operating object is less dependent because it doesn't have to manage the selection and lifecycle management of the used object. This is IoC (hard coded) in a nutshell.
DI is one way to address hard coding. You can think of DI as a combined Lookup and Activator. You give DI a "key", it looks up the mapped object, instantiates it using an Activator, and passes out the instance. You can have 1:1 type situations, where the Key and Value are the same. I tell it Foo, and it gives me a Foo object. Or, you can have 1:Many. Interfaces help here. Because with IoC I have decoupled the structure from the behavior, and interfaces are about behavior, I can associate many Values with a Key.
interface IFoo {
string Bar(string inString);
}
class FooOne : IFoo { }
class FooTwo : IFoo { }
lookup.Add(IFoo, FooOne);
lookup.Add(IFoo, FooTwo);
This provides another form of loosening dependencies and coupling.
class Foo{
IFoo _foo;
public Foo(IFoo foo){ _foo = foo }
}
The DI system "intercepts" object creation, inspects the constructor to see if any types are in the lookup, supplies those instances, and then lets the creation process resume.
To your question about "new", with "new" I have to know the class and it's details. Here I just need to know the (usually smaller) set of relevant methods on an interface. In theory, the DI system could randomly pick FooOne or FooTwo, and things should work the same. Of course, there is a lot more to this.
So, "less dependent" begs, "In what way?". From the perspective of having to know about all of the methods, we can use interfaces to minimize the set of those we need to know. A class may have 100 methods, but if IFoo only has one, we can use the class and completely ignore the other 99. Or I can use "best fit" implementations - one implementation (FooOne) may work better with small data sets, and one (FooTwo) with large data sets. I can swap them in and out in real time using DI, never touching a "new FooXxx()". Better though, is (conceptually) we can design code without ever thinking about implementation. We have factored out structure (and it's implementation), and deal only with behavior. You could write an entire application using only interfaces and test stubs. Then you could build/modify/buy classes that implement those interfaces. You could plug them in manually or use a DI system to do it dynamically.
|
|
|
|
|
If you drive a VW full of plantain into a hill of pasta, do you get Spaghetti Carbanana?
Sent from my Amstrad PC 1640
Never throw anything away, Griff
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Can't think about stuff like that now. My girlfriend left me because of my obsession with pasta – I’m doing well, but feeling cannelloni.
Anything that is unrelated to elephants is irrelephant Anonymous
- The problem with quotes on the internet is that you can never tell if they're genuine Winston Churchill, 1944
- Never argue with a fool. Onlookers may not be able to tell the difference. Mark Twain
|
|
|
|
|
This morning pastina hurry, but as for your question, I'll know in a day orzo.
Is a obsessive attraction to small feet a fettuccine?
Ravings en masse^ |
---|
"The difference between genius and stupidity is that genius has its limits." - Albert Einstein | "If you are searching for perfection in others, then you seek disappointment. If you are seek perfection in yourself, then you will find failure." - Balboos HaGadol Mar 2010 |
|
|
|
|
|
OriginalGriff wrote: If you drive a VW
Diesel too pass.
"If we don't change direction, we'll end up where we're going"
|
|
|
|
|
"Berlin to Warsaw on one tank." -- JC
|
|
|
|
|
Griff you are always good for a fusilli puns.
Socialism is the Axe Body Spray of political ideologies: It never does what it claims to do, but people too young to know better keep buying it anyway. (Glenn Reynolds)
|
|
|
|
|
...if you listen varicosely?
Anything that is unrelated to elephants is irrelephant Anonymous
- The problem with quotes on the internet is that you can never tell if they're genuine Winston Churchill, 1944
- Never argue with a fool. Onlookers may not be able to tell the difference. Mark Twain
|
|
|
|
|
|
O really?
Anything that is unrelated to elephants is irrelephant Anonymous
- The problem with quotes on the internet is that you can never tell if they're genuine Winston Churchill, 1944
- Never argue with a fool. Onlookers may not be able to tell the difference. Mark Twain
|
|
|
|
|
A sanguine remark?
Ravings en masse^ |
---|
"The difference between genius and stupidity is that genius has its limits." - Albert Einstein | "If you are searching for perfection in others, then you seek disappointment. If you are seek perfection in yourself, then you will find failure." - Balboos HaGadol Mar 2010 |
|
|
|
|
|
You bloody well know that!
Anything that is unrelated to elephants is irrelephant Anonymous
- The problem with quotes on the internet is that you can never tell if they're genuine Winston Churchill, 1944
- Never argue with a fool. Onlookers may not be able to tell the difference. Mark Twain
|
|
|
|
|
All puns aside, I can. It is encoded in my tinitus...
If you can keep your head while those about you are losing theirs, perhaps you don't understand the situation.
|
|
|
|
|
Make two of them
M.D.V.
If something has a solution... Why do we have to worry about?. If it has no solution... For what reason do we have to worry about?
Help me to understand what I'm saying, and I'll explain it better to you
Rating helpful answers is nice, but saying thanks can be even nicer.
|
|
|
|
|