1

As per wiki,

A dependency is an object that can be used (as a service).


here is the OOP paradigm using C syntax that address 4 roles, shown below.

1) interface (handlers.h)

typedef struct {
    int (*canHandle) (char *);
    int (*drawImage)(char *);
    int (*savefile)(char *);
}imageHandler;

2) Take one Dependency (gifhandler.c)

imageHandler gifhandler = {
    gif_canHandle,
    gif_drawImage,
    gif_savefile
};

3) Dependency container (dealt by config.c)

//gifhandler.c - dependency 
int _init(){
    printf(" registering gifhandler \n");
    reg_handler(&gifhandler);
    return 0;
}

//config.c
imageHandler *imagehandlers[10];
int reg_handler(imageHandler *ih){
// we need to perform checks here.
    imagehandlers[libs] = ih;
    libs++;
    return TRUE;
}
// config.c
int init_handlers(){
    .....
    soptr = dlopen(so_name,RTLD_NOW);
    ....
}

4) Client - Service locator (UI.C)

// UI.C
switch(choice){
        case 1:
            vdrawImage(filename);   
            break;
        case 2:
            vsavefile(filename);
            break;
}
// viml.c
int vdrawImage(char *filename){
    ...
    handleno = find_handler(filename);
    ...
    ih=imagehandlers[handleno];
    ih->drawImage(filename);    
    return FALSE;       
}
// viml.c
int vsavefile(char *newfilename ){
    ...
    handleno = find_handler(newfilename);
    ...
    ih=imagehandlers[handleno];
    ih->savefile(newfilename); 
}

1) To add new dependency(libxyzhandl.so.1) in Dependency container, it just requires adding a new entry in config.txt configurable, as shown below,

config.txt

./libgifhandl.so.1

./libtiffhandl.so.1

2) New service provided by ./libxyzhandl.so.1, will be contained by Dependency container without re-compilation of application.

3) Testing of complete application is not required, except source code of libxyzhandl.so.

So, if config.txt goes empty, then, application does nothing, except saying, We cannot handle this kind of files, shown here, for any input(image file).

Below is the visualisation of call flow,

enter image description here

Question:

Can this dependency container be called an IOC container?

  • Sorry, I still can't say I see how imageHandler is constructed. Can you show me the line of code where it's happening? – candied_orange Jul 23 '17 at 15:08
  • @CandiedOrange Now, you should see that in query. Query edited. Hope it clarifies – overexchange Jul 23 '17 at 15:09
  • if imagehandlers[10] is your construction code I need to know where that is and what else is going on there. I need context. – candied_orange Jul 23 '17 at 15:14
  • @CandiedOrange extern imageHandler *imagehandlers[]; in handlers.h, imagehandlers is array of structure of pointers(vtable). Each element of array, houses behavior code – overexchange Jul 23 '17 at 15:19
  • 1
    Don't pay too much attention to any definition of dependency injection. Long before this term became popular, image processing frameworks and applications used the exact same thing, called plug-ins. It is not always necessary to have a configuration file. Just having a file present in a directory (a library) with the correct types inside is usually enough for it to be dynamically loaded, and an instance installed in the application or framework. – Frank Hileman Jul 26 '17 at 16:37
1

From the perspective of the client code you've posted, this is not dependency injection. Your client references a statically-defined collection of objects and requests one that matches certain criteria, which is much more similar to the Service Locator pattern than Dependency Injection. That said, they are two similar patterns that achieve a number of the same goals. The only real difference is that wouldn't be possible for your application to have, for example, two different invocations of vdrawImage or vsavefile that use a different handler object for the same file type (which could be beneficial for unit testing, for example), while traditional dependency injection has a little more overhead but can achieve this level of configuration.

|improve this answer|||||
  • 1) Statically defined collection? Dependencies are available at runtime. 2) That matching criteria is vtable logic, as C does not support polymorphism, as a language feature. – overexchange Jul 27 '17 at 23:05
  • By statically defined I mean the same instance is shared between all invocations. You can't have one set of objects in one part of your program and a different set in another part, because there's only a single pointer to the dependencies shared by all clients. In a dependency injection system, each client gets their own copy of the pointer so that they can have a different dependency if necessary. – Jules Jul 28 '17 at 8:20
  • For every vdrawImage(filename) we get corresponding dependency(handleno = find_handler(filename);), based on the filename passed to vdrawImage(). Isn't it? – overexchange Jul 28 '17 at 8:26
  • What if you want two different calls to vdrawImage("test.png") to use a different implementation of your handler object? They both call the same file_handler function that pulls the same object out of global data. This is the main distinction between dependency injection and the service locator pattern. Service locators are great for many things, but they're hard to write unit tests against, because very often different tests want to use different mock implementations in otherwise indistinguishable calls to the locator. – Jules Jul 28 '17 at 16:37
  • Can this dependency container be called as IOC container? – overexchange Dec 14 '17 at 16:59
4

Dependency injection can be used to externalize a system's configuration details into configuration files

If you're doing simple reference passing you can do your configuration in main (or whatever your composition root is). That's certainly Dependency Injection. Using a configuration file that doesn't need to be compiled to create the object graph is optional.

The goal in either case is to have a clear separation between behavior code and construction code.

I can't really tell if you're meeting this goal because I don't see where imageHandler is being constructed. All I know is that it doesn't look like reg_handler is constructing it. Which if reg_handler holds behavior code, is a good thing.

|improve this answer|||||
1

I can see why this discussion goes in divergent directions, with different people giving different answers.

Dependency Injection describes a technique in object oriented programming (OOP). Meanwhile, your sample code isn't OOP.

The goal of Dependency Injection is to have flexibility - to allow a piece of client code to work with a choice of different service implementations, without requiring the client code to be recompiled. To this end, your non-OOP sample code seems to have the same kind of flexibility, even though it is non-OOP.

As noted by others, your code contains some characteristics of service locator, plugin system, and IoC container configuration using configuration files.

Though, because your sample code is non-OOP, programmers who are deep into OOP can't use their rules-of-thumb to judge whether your code fits a certain OOP pattern.

With respect to Dependency Injection, OOP programmers will ask these questions:

  1. Are there different service implementations? (e.g. S1, S2, S3, ...)
  2. Do these service implementations implement the same interface (I)? Or do they all derive from the same abstract base class (S)?
  3. Does the client (application code) use the service implementation strictly in accordance with the interface (via I or S)?
  4. Is the client code encapsulated into an object (C)?
  5. When the client object (C) is initializing (e.g. in the constructor, or shortly after having been constructed), does it allow the user of this object (the owner of C, therefore not part of C but at a higher command level) to specify which service implementation to use, by passing in an instance of that implementation?

Condition #5 is the uniquely-identifying characteristic of dependency injection. For OOP programmers, they will jump to condition #5 without verifying conditions #1 - #4. It is because OOP programmers took these for granted. Therefore, OOP programmers will ask to see the constructor or the property-setters of the client object (C). Your code doesn't have these.

When applying these conditions to your sample code, the results are:

  1. Yes
  2. Yes. However, the "interface" is not based on OOP-style interface or abstract base class. Instead, it is based on C-style function pointers.
  3. Yes.
  4. No. There is an "application" that runs a menu-driven application loop.
  5. Not applicable. The application code is already at the highest command level; there is no other "code". In some sense, the only higher command level is the (human) user who gives keyboard input to the program.

Edited: Suppose we stretch the definition of OOP to cover C-style function pointers and explicit vtables. Here is my response:

The essence of Dependency Injection is that, after you have obtained a pointer to an imagehandler, as in:

// ih is a pointer to a struct that contains three function pointers
ih = imagehandlers[handleno]; 

that you have other functions that accepts the ih pointer as a function parameter (input argument). This is the essence.

|improve this answer|||||
  • Code has implemented polymorphism(vtable). Which aspect of OOP is missing? Encapsulation is not required, as there are no invariants to maintain. – overexchange Jul 28 '17 at 10:50
  • @overexchange: Your code is perfectly fine. I just try to explain why other people gives different answers. Also, I am sure that there will be other programmers who will say that DI is not applicable to non-OOP code, therefore your code example isn't a valid showcase of DI. (i.e. it gets down into philosophical arguments.) – rwong Jul 28 '17 at 10:52
  • @overexchange It also depends on what your ultimate goal is. If your ultimate goal is to build an image-handling application that supports different image formats, then I think your code is fine. However, if you are looking for (or constructing) a code example that teaches people what is DI and what is not DI, then it will be a bad example, because a good example for DI should have both the services and the client(s) as objects. – rwong Jul 28 '17 at 10:55
  • @overexchange I edited my answer. – rwong Jul 28 '17 at 11:02
  • 1
    @rwong While global variables are not allowed in many OO languages, static fields for example in .net, accessed publicly or modified via side effects globally, have all the same problems. The problem with this question is the emphasis on dependency injection, like you said. It doesn't matter whether it is dependency injection or not. – Frank Hileman Jul 31 '17 at 17:17
0

It seems to me, this is more in line with the Open/Closed principle. Dependency injection abstracts common dependencies so that the implementation of the abstract dependency doesn't matter and is "injected" into the process, by constructor or property injection.

|improve this answer|||||
  • reg_handler() in config.cis the constructor for extern imageHandler *imagehandlers[]; declared in handlers.h. – overexchange Jul 23 '17 at 9:18
  • I'm not a c programmer, but it seems this code is designed explicitly to import one or more implementations and loading them dynamically. What would happen if the file were empty? Is your program dependent upon them? – Eric Smith Jul 23 '17 at 9:29
  • If config.txt is empty, then, printf(" We cannot handle this kind of files\n"); executes, shown here So, if config.txt is empty, then application cannot support any image file as there is no corresponding service injected, on restart of my application ), for any input(image file) by user. So, if config.txt is empty, then application cannot support any image file as there is no corresponding service injected, on restart of my application – overexchange Jul 23 '17 at 9:41
  • 2
    If that's the purpose of the app, and nothing else gets done without a handler in the config file, then it could be considered dependency injection. After all, this is similar to the methods used by some IoC containers, where dependencies can be registered in config files. – Eric Smith Jul 23 '17 at 9:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.