Search code examples
javaoopcompositionsolid-principlesdependency-inversion

Does composition violate the D in SOLID?


I am learning SOLID principle. While learning "Dependency Inversion Principle" found out that class should depend on interfaces rather than concrete classes.

Does this mean composition is not allowed? Also, does aggregation same as DIP?

Composition:

    class HPDesktop {
        private BluetoothMouse bluetoothMouse;
        private BluetoothKeyboard bluetoothKeyboard;
    
        public HPDesktop(){
            bluetoothMouse = new BluetoothMouse();
            bluetoothKeyboard = new BluetoothKeyboard();
        } 
     }

DIP: (This seems to be as an aggregation)

class HPDesktop {
    private Mouse mouse;
    private Keyboard keyboard;

    public HPDesktop(Mouse mouse, Keyboard keyboard){
        this.mouse = mouse;
        this.keyboard = keyboard;
    }
}

Solution

  • No, because you misunderstand what kinds of dependencies are being discussed.

    Dependency Inversion is the concept that high level modules should not depend on low level module details, this "exposure" of the low level module details is called "leaky abstraction"

    For example, let's say I'm wrapping a logging implementation with a new facade, that permits me to "do more" with logging than I was recently doing with Log4j. If I exposed the Log4j log levels as elements in my design, then I have a dependency between my facade to Log4j log levels because my facade requires Log4j.

    Now, if I created my own facade log levels, then everyone who used my facade would not need to import Log4j to use Log4j's log levels. This means that if I wanted to alter what the facade used internally, I could do so without impacting the users of my facade.

    How is this done? Well, interfaces provide the means of inverting dependency direction. I directly depend (in the facade) on an interface that specifies my log levels to the Log4j log levels, and some module provides these (directly depending on the same interface.

    Since Log4j is now depending on the interface but as a provider of services, instead of the facade depending on log4j as a provider of services, the "provision" of "log level services" is now inverted. This means that in the future, if I want to change the implementation, I don't have to rewrite much code, just the code that presents the provision of services with my new log level provider.

    Dependency Inversion

    Just to drive the point home one more time, a client of the Facade using direct dependencies would have to

    import facade;
    import log4j;
    

    to provide the log4j log level that facade uses. With dependency inversion, I might have to create a second set of facade log levels, but my client's import will be

    import facade;
    

    and I can later wire in a different jar to provide the log level interface implementation, meaning my client's code doesn't have to change as I swap out back-end log level providers.