496
top 42 comments
sorted by: hot top controversial new old
[-] h_a_r_u_k_i@programming.dev 21 points 1 year ago
[-] fuck_u_spez@lemmy.fmhy.ml 4 points 1 year ago

That's actually where the title is coming from...

[-] idunnololz@lemmy.world 14 points 1 year ago

Just make a SolutionStrategyManagerManagerFactory. Easy.

[-] em7@programming.dev 3 points 1 year ago

Can you make it an Enterprise Bean?

[-] bjornsno@lemm.ee 14 points 1 year ago

Why is the joke with Java always factories? Factories are really super useful in a dependency injection context.

[-] firelizzard@programming.dev 7 points 1 year ago

I've been working primarily in Go for the past five years, including some extremely complex projects, and I have never once wished I had dependency injection. It has been wonderful. I have used dependency injection - previously I worked on a C# project for years, and that used DI - but I adore Go's simplicity and I never want to use anything else (except for JS for UI, via Electron or Wails for desktop).

[-] CodeBlooded@programming.dev 9 points 1 year ago

Go programmer here: What does Go’s simplicity have to do with dependency injection? What does a language itself have to do with dependency injection?

Reading your post and not being personally familiar with your work, I do wonder, perhaps your “extremely complex projects” wouldn’t be so extremely complex if you practiced dependency injection?

How do you unit test your extremely complex projects if your business logic carries the additional responsibility of creating objects?

[-] fuck_u_spez@lemmy.fmhy.ml 2 points 1 year ago

I say it's all about data flow and composability, if it's pretty much always in one direction (modular tree structure/architecture) then you just don't need all these "patterns"...

[-] firelizzard@programming.dev 1 points 1 year ago

What does Go’s simplicity have to do with dependency injection?

In my experience, following Go's philosophy of simple solutions eliminates the need for complex solutions such as dependency injection.

How do you unit test your extremely complex projects if your business logic carries the additional responsibility of creating objects?

I write modular code that accepts interfaces so I can test the components I want to test. The vast majority of object creation happens at initialization time, not in the business logic. For the projects I've worked on, that would be true with or without DI - I don't see how that's relevant.

perhaps your “extremely complex projects” wouldn’t be so extremely complex if you practiced dependency injection?

When the CTO says, "Make it distributed and sharded," I do what I'm told, but that is an intrinsically complex problem. The complexity is in the overall behavior of the system. If you zoom in to the individual execution units, the business logic is relatively simple. But the behavior of the system as a whole is rather complex, and DI isn't going to change that.

[-] occams_chainsaw@programming.dev 1 points 1 year ago

I write modular code that accepts interfaces so I can test the components I want to test.

basically dependency injection

[-] firelizzard@programming.dev 2 points 1 year ago

How does dependency injection have anything to do with writing tests? I write tests by writing a test function that executes the code I want to test...

I mean unit tests. I work on Spring Boot apps where there are distinct layers (controller -> service -> persistence), and you generally inject mocks into your object to isolate tests to the specific code you want under test. One benefit of this approach is that it's pretty easy to get 90% coverage.

[-] fuck_u_spez@lemmy.fmhy.ml 4 points 1 year ago

... until you've heard of Rust :)

(I think Go takes all mediocre language features together and makes an even more mediocre language TBH, take error handling for example, or generic programming (which I agree should be used sparingly, but is super useful if you need it))

[-] interolivary@beehaw.org 5 points 1 year ago* (last edited 1 year ago)

Go does have generics nowadays, although they have some limitations (like pointer types being inefficient because reasons).

But yeah I'd tend to agree. Go's strength and explicit design goal is that it's relatively easy to learn and get started with, meaning it's fast to onboard new devs. It's very much a "get shit done" language.

Its weakness is – to paraphrase someone's criticism of Go – the core dev team's extreme unwillingness to adopt any programming language and tool chain designs and patterns invented after the 70's. Pike's rationale against generics was that in cases where you'd normally reach for generics you can either use interfaces (and especially interface{} which is like Go's void* and throws all type information out the window and is slower than proper types because reasons), or just copy and paste code. Because what you as a developer want to do is reimplement something like Find(needle SomeType, haystack []SomeType) for the Nth time in every project, or take a performance hit and lose all type information by using interface{}.

And don't even get me started on how long it took for Go to get proper dependency management and what the arguments against it were, Jesus fuck.

Go is currently the language I'm most fluent in after having written mostly Go for something like 8 years or even more (I remember when error was Error, pre 1.0 I think) and the one I'd be the most productive in, which is sort of unfortunate since I really don't like it as a language 😁 Learning Julia at the moment though, since I'm going back to the university to study some more computer science and maybe get into evolutionary algorithm research, and Julia is a neat language for lots of different number crunch-y tasks (no I won't touch Python, significant whitespace is a crime against common sense and fully dynamic typing gives me the heebie jeebies)

[-] firelizzard@programming.dev 2 points 1 year ago

I've heard of Rust. It sounds noisy and even more verbose than Go, which is already a fairly verbose language. I haven't had any reason to learn Rust, so I haven't done so. The error handling is annoying but at this point I don't really notice it any more. And as interolivary said, Go has generics now.

[-] tatterdemalion@programming.dev 2 points 1 year ago

Do you write unit tests with objects mocked via interfaces? Or polymorphism via interfaces? Those are the main reasons to use DI.

[-] fuck_u_spez@lemmy.fmhy.ml 1 points 1 year ago

I guess I have to start calling function invocation with generic parameters, fancy names (like "dependency injection" ^^)

[-] tatterdemalion@programming.dev 1 points 1 year ago

Yea that works too.

[-] firelizzard@programming.dev 1 points 1 year ago

I use various strategies depending on what seems appropriate, including the two you mention. I've never felt the lack of DI.

[-] fuck_u_spez@lemmy.fmhy.ml 3 points 1 year ago

Btw. why don't interfaces work + "as argument" (whether it's a constructor in an OOP context or a function parameter)? I think interfaces are exactly built for such a use-case (without all the boilerplate that's necessary with dependency injection as well as factories).

[-] bjornsno@lemm.ee 3 points 1 year ago

What are you talking about? At least in Java and PHP you can absolutely declare constructor and function parameters as interfaces. As you say that's exactly what they're for.

[-] fuck_u_spez@lemmy.fmhy.ml 1 points 1 year ago

Yeah that's my point, I don't see a use for factories here, because it's possible to just use generic parameters (whether it's constructors or function arguments).

[-] thtroyer@programming.dev 1 points 1 year ago

I'm somewhat confused by your statements, so perhaps I don't understand.

Function/objects that allow changing their behavior by passing different objects into them, based on some interface, is called dependency injection. Some subset of behavior is determined by this passed behavior. E.g. To keep a logger class from having to understand how to write logs, you could create a WriteTo interface and various implementations like WriteToDatabase, WriteToFile, WriteToStdout, WriteToNull.

When you create this example logger, you'll need to make a choice of what object to pass when you write the code. e.g. new Logger(new WriteToDatabase(config)) But maybe you don't want to make that decision yet -- you want to let a config file decide which writer(s) to create. The pattern to pick between dependencies at runtime is called a factory. In this case, you might make a WriterFactory to pick the right writer, or perhaps a LoggerFactory to hide the creation of both Writer and Factory objects.

So, a factory is only really a facade to hide the runtime switching of how an object is created.

Also, the term dependency injection often gets confused with what you see in various Java / C# / and various frameworks in other languages -- those usually use what's called a "DI Container" or "IoC Container". These manage and facilitate how dependency injection happens within the project, often with various annotations (e.g.@Autowired). These containers are powerful, but sometimes complicated.

However, you can absolutely still do DI without DI containers, and I think advocating for not using DI generally (and related patterns like factories) is rather misguided.

[-] fuck_u_spez@lemmy.fmhy.ml 1 points 1 year ago* (last edited 1 year ago)

I still haven't really understood the use (and use case) of "dependency injection" (and it feels to me I read now everything about dependency injection I could find), to me it seems to be yet another ProblemFactory.

[-] CodeBlooded@programming.dev 7 points 1 year ago* (last edited 1 year ago)

Single responsibility principle: is your GetData() function responsible for getting data? Or is it responsible for creating a new database connection and also using that to go get the data?

Start naming your functions by what they really do. When you see the word “and” in your function name, you know your function is responsible for too much.

Dependency injection is the difference between CreateDatabaseConnectionAndGetData() and GetData(connection ConnectionType).

In the first example, that function will always connect to the specific db that you hard coded in it. It probably has to also read in a config file to get the connection details. Maybe you should name it ReadConfigAndCreateDatabaseConnectionAndGetData()?

In the second example, I can pass in a MySQL connection or PostgreSQL connection, or some dummy connection for testing.

Keep all that nasty dirty untestable code in one place and spare your business logic from owning all of that.

[-] fuck_u_spez@lemmy.fmhy.ml -1 points 1 year ago

Thanks for the write up, but as I said, I know and I've read all about that already. I still cannot see, why a simple function argument and an interface isn't enough (you can probably already call that "dependency injection" if you want to get fancy)

I guess I have just divorced with OOP and the "necessary" "design patterns"...

Things are more simple and less boilerplaty now for me :).

[-] bjornsno@lemm.ee 11 points 1 year ago

My brother in Christ, that is dependency injection. Just because you don't want to call the spade a spade anymore doesn't make it not so.

[-] philm@programming.dev -2 points 1 year ago

Yeah I guess you can call it like that, I'll just call it function invocation...

[-] bjornsno@lemm.ee 4 points 1 year ago

You're gonna have a tough time talking to others about your code if you don't agree on common terminology. Function invocation is just function invocation, it doesn't say anything about the form of the parameters or composition. Dependency injection is a well known and commonly understood method of facilitating decoupling and composition by passing a function's dependencies as parameters. From your comments you're talking about the second, but refusing the name, for... reasons?

[-] philm@programming.dev 0 points 1 year ago

I guess I'm a little bit too long already in the functional/data-driven world (after being a decade in OO languages (IMO too long...)). In OOP you may need a separate term for that.

But I think it' just not really necessary in functional programming, it's just another parameter when calling a function, that may be a somewhat type-constrained generic (for testing e.g. with a mock implementation).

I mean function parameters are somewhat dependencies anyway, so should I call all my parameters now dependencies and invocation "injection"?

[-] bjornsno@lemm.ee 1 points 1 year ago

Thought you were OP for a second there, as they were talking about composability. Whether it's dependency injection or not depends on what shape your parameters take. If you're doing functional programming and you're passing handlers and connections etc. as params, that's dependency injection. If you're only passing strings and objects and such and the function has to do a bunch of logic to decide how to handle its params, that's not dependency injection.

[-] jvisick@programming.dev 2 points 1 year ago

“Dependency injection” is just a term for providing a function or method with its dependencies rather than making the function go and gather them itself.

It’s (typically) done through parameters, but it’s still more specific than just invoking a function. It describes how that function was written and the reasoning for certain parameters. To the other commenter’s point, you’ll have a hard time communicating about your code with other developers if you refuse to use the term dependency injection just because you don’t like OOP.

[-] dudinax@programming.dev 1 points 1 year ago* (last edited 1 year ago)

Di is just good functional practice. I'm not sure it's a super importent idea to someone who knows how to write a good function.

Edit: "a function do one thing and its operands should be passed as arguments" for the OO world.

[-] MrShankles@reddthat.com 1 points 1 year ago

I'm just lurking around, so don't mind me. But I gotta say, it really does sound like y'all are just making shit up sometimes lol. Like a mechanic trying to charge you an extra 50 bucks because your jindle shaft needed an alignment

[-] Smallletter@lemmy.world 1 points 1 year ago

I've tried studying CSa d programming, both formally and informally, for long enough to know most of what y'all are talking about, but failed terribly enough that I still have no idea how people can actually build code. Syntax is easy enough, I always started a class like oh cool I can do this. And then they drop me off the deep end and I fail, every time. I work in IT infrastructure now and I'm happy to say I never have to worry about coding, though my foundational knowledge is useful for troubleshooting.

[-] fuck_u_spez@lemmy.fmhy.ml -1 points 1 year ago

It's all about experience IMHO, just start small projects (and in my experience ignore all these "design patterns", I learned quite a lot of patterns, but I'm not really using any of them nowadays, maybe sometimes intuitively, (as this thread shows, "dependency injection" which can be a fancy term for generic function parameters).

Well maybe learn them, but take everything with a grain of salt, I think. Intuitively thinking is often better (with the drive to do it better). Try to write code such that it's easily readable exactly focused on the problem that it's trying to solve, not anything fancier (this is actually a very simple but effective Mantra). Otherwise it often leads to overengineering (all these "design patterns" for example...), or premature optimization. (E.g. something like the popular book "Clean code" is IMHO full of antipatterns, like the examples are definitely not something I would do, they are inefficient, boilerplate, and often make the code unreadable).

[-] offbyone@reddthat.com 2 points 1 year ago

Generally speaking the use case is writing tests. If your tests just call all the dependencies and new directly then it's harder to write tests for your specific component while avoiding setting up a whole bunch of stuff (to make all those other classes work). By requiring all the dependencies to be provided to the class, you can swap them out at test time for something else that's easier to work with.

That said, IMO it's a symptom of problems in language design. Using DI is only necessary because languages like C# don't make it easy to mock out new or classes used directly, so we resort to wrapping everything in interfaces and factories to avoid those features and replace them with ones that are easier to mock. If the language was designed such that those features were easy to replace during testing then DI probably wouldn't be a thing.

[-] fuck_u_spez@lemmy.fmhy.ml -1 points 1 year ago

It's probably a general symptom of what people call OOP nowadays, in a more functional composeable world (where I'm living in currently). You just use function parameters and interfaces (or as Rust calls it "Traits"). But I still think in OOP, this is enough as well and the dataflow is more clear.

[-] Fordiman@programming.dev 9 points 1 year ago

I'm sorry, but why is this not an AbstractToolBuildingFactoryFactoryFactory? How are you going to derive alternatives?

[-] fuck_u_spez@lemmy.fmhy.ml 4 points 1 year ago
load more comments
view more: next ›
this post was submitted on 08 Jul 2023
496 points (97.3% liked)

Programmer Humor

19623 readers
1 users here now

Welcome to Programmer Humor!

This is a place where you can post jokes, memes, humor, etc. related to programming!

For sharing awful code theres also Programming Horror.

Rules

founded 1 year ago
MODERATORS