Dependency Injection for Procedural Programming

泄露秘密 提交于 2019-12-12 07:47:22

问题


Suppose I've decided to write a large application in C, or any other procedural programming language. It has functions with call-dependencies that look like this:

A
|
+-------------+
|             |
B1            B2
|             |
+------+      +------+
|      |      |      |
C11    C12    C21    C22

Obviously, unit-testing the leaves functions, C11, C12, C21, and C22 is very easy: Setup the inputs, invoke the functions, assert the outputs.

But what is the proper strategy to enable good unit-testing for B1, B2 and A?

Would Dependency Injection suggest that B1 (and B2 also) be declared as followed?

// Declare B1 with dependency injection for invoking C11 and C12.
int B1(int input, int (*c11)(int), int(*c12)(int));

But that strategy does not seem scalable if I have many layers of calls. Just imagine what the declaration for A would look like:

int A(int input, int (*b1)(int, int (*)(int), int(*)(int)), 
                 int(*b2)(int, int (*)(int), int(*)(int)),
                 int (*c11)(int),
                 int (*c12)(int),
                 int (*c21)(int),
                 int (*c22)(int));

Yuck! There has to be a better way.

Sometimes, I feel that DI and other similar patterns that purport to promote modularity and ease of maintenance actually hampers code clarity and complicates what should be straightforward coding into nonsense abstractions and convoluted indirections.

How do large software projects in C, like Perl and Ruby, deals with unit-testing?


回答1:


If you only require the DI for unit testing you may use the linker to do it.

What I mean is that the functions B1 & B2 are declared in a header and used by function A, so the implementation of the B functions is provided by the linker. You just need to provide a different C-File for unit tests. This should not be a big problem, as you probably have your own makefile for the unit test anyhow.

If you require dynamic dependency resolution at runtime you should use a factory pattern (a function returning the function-pointer) for function pointers and pull them from the factory when required. The factory may decide depending on global context what function to return.




回答2:


A only needs to call B1 and B2. It does not need to know about anything at the C level.

You could inject a different dummy versions of functions B1 and B2 into A for the purpose of testing A.

This isolates A from needing the whole structure and means you can test each function in isolation.




回答3:


you can put the dependencies to a c-struct that will become one parameter for the function call. in c this would be similar to the file api where the first parameter is always the file-handle




回答4:


I like this question. It gets a bit tricky in procedural languages.... but I think you can borrow an idea from the OO world where folks often use constructor overloading to handle some of the DI work. So for example you would use the default constructor that sets up all the dependencies as usual... but then also have another constructor that allows the dependencies to be injected.

Since you are procedural... I think you could use function overloading to handle this for you. Also when you are testing you only would need to mock out B1 & B2 when calling A... so you could simplify your DI for that purpose. In other words if you really only using DI for unit testing then you don't have to inject the whole tree of dependcies only the first level dependecies...

So from A you might have...

int A(int input){
// create function point to b1 & b2 and call "return A(input, {pointer to b1},{pointer to b2})"
}

forgive my psuedo code it has been a long time since I did C.




回答5:


You can do proper unit testing of B1, B2 and A without DI. Just like the leaf functions, B1 and B2 has valid inputs and outputs, and you test those, same with A. That B1 can use C11 and C12 internally to help you fulfill its unit tests doesn't mean they have to be injected in cases where you don't need that flexibility.



来源:https://stackoverflow.com/questions/5527785/dependency-injection-for-procedural-programming

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!