Does this violate the "Dependency Inversion Principle"?

public class Connection {
public Connection(){

}

public String description() {
    return "Generic";
}

}

public class SqlServerConnection extends Connection{


public SqlServerConnection(){

}

public String description(){
    return "SQL Server";
}

}

public class OracleConnection extends Connection{
public OracleConnection(){

}

public String description(){
    return "Oracle";
}

}

public class MySqlConnection extends Connection{
public MySqlConnection(){

}

public String description(){
    return "MySQL";
}

}

public class FirstFactory {
String type;

public FirstFactory(String t){
    type = t;
}

public Connection createConnection(){
    if(type.equals("Oracle")){
        return new OracleConnection();
    }else if(type.equals("SQL Server")){
        return new SqlServerConnection();
    }else{
        return new MySqlConnection();
    }
}

}

public class TestConnection {
public static void main(String[] args) {
        FirstFactory factory;

        factory = new FirstFactory("Oracle");

        Connection connection = factory.createConnection(); //createConnection return concrete implementation not an abstraction

        System.out.println("You're connection with " + connection.description());
}

}

This is from VTC design pattern video tutorials my question is did this example violates the Dependency Inversion Principle?

Because TestConnection class depends on concrete implementation because factory.createConnection() return concrete implementation not an abstraction.

can I fix this by doing this instead?

public Connection createConnection(){

Connection connection = null;

if(type.equals("Oracle")){
    connection = new OracleConnection();
}else if(type.equals("SQL Server")){
    connection = new SqlServerConnection();
}else{
    connection = new MySqlServerConnection();
}
return connection;

}


Ideally you would inject the factory into the TestConnection (ie it would take a ConnectionFactory as a parameter, by an interface) rather than calling "new" to get a factory. That way the TestConnection doesn't depend either on the concrete implementation of the Factory or of the Connection.

It's fine that the factory returns a concrete implementation of Connection -- in fact, it's necessary if you want to actually use the connection. You aren't depending on the particular concrete implementation because the factory returns it as Connection (ie as the interface). Someone, somewhere does have to actually instantiate the Connection implementation -- that's the job of a Factory.

It would be a violation if, say, the OracleConnection had certain methods that other connections don't, and the calling class was depending on those. Not the case here.


While it can be argued that your general architecture is not the best way to go about implementing database connections, I'll skip that as I understand the question to be more about Dependency Inversion rather than database implementation. To achieve full Dependency Inversion:

  • Connection should be an interface, not a concrete class.
  • ConnectionFactory should be an interface, not a concrete class, that defines a createConnection() method returning a Connection . It should not require arguments in its constructor.
  • For each database, you would have a different concrete class that implements ConnectionFactory and returns objects that implement Connection .
  • The above satisfies the Dependency Inversion Principle better because now TestConnection only specifies what it needs from service providers and is not coupled to any concrete implementation. Any concrete implementation can be created and used so long as it meets the requirements specified by the interfaces defined for TestConnection and the interfaces specify no more than what TestConnection truly needs.

    In particular, another vendor in another package can create a new Factory and Connection implementation and you can plug that into the code (if you've followed the above architecture) and your TestConnection class needs no modification of any kind.

    This supports many paradigms of run-time association of concrete services to service clients. For example, in a typical Startup-time Dependency Injection scenario, you would inject a concrete implementation of ConnectionFactory into TestConnection and it would then look like:

    public class TestConnection {
        private ConnectionFactory connectionFactory;
        public setConnectionFactory(ConnectionFactory connectionFactory) {
            this.connectionFactory = connectionFactory;
        }
    
        public static void main(String[] args) {
    
            //
            // Perform dependency injection here
            //
    
            // after dependency injection
    
            Connection connection = connectionFactory.createConnection(); //createConnection return concrete implementation not an abstraction
    
            System.out.println("You're connection with " + connection.description());
        }
    }
    

    This separates TestConnection completely from any dependency on any database and makes it easily configurable by injecting just a ConnectionFactory . Alternately, instead of "Perform dependency injection" you could do a service lookup. J2EE/J3EE use this extensively, for example to get a ConnectionFactory (or more typically a DataSource ) out of the JNDI Context object. These are beyond the scope of the question, and just provided as an example of why you would want to do this and one way to check if you've satisfied the principle. TestConnection should only be referring to Interfaces, not Classes, for anything that is coming from external sources (such as Database drivers).


    the Dependency Inversion Principle principals are

    A - High-level modules should not depend on low-level modules. Both should depend on abstractions.

    In your case you've got the Connection abstracted which is good. The use of the factory pattern is fine in my opinion too.

    B - Abstractions should not depend upon details. Details should depend upon abstractions.

    Your Connection does not depend on lower implementations, and the inherited classes may use methods/variables of Connection (if the example was more complex they probably would)

    As an aside, I would lean towards making the Connection an abstract class though, if you were to add some more functionality to it later, and not bother with the default constructor which is made for you

    public class Connection {
      public abstract String description();
    }
    

    or just make it an interface

    public interface Connection {
      String description();
    }
    
    链接地址: http://www.djcxy.com/p/82240.html

    上一篇: 观察者模式中应用的控制反转

    下一篇: 这是否违反了“依赖倒置原则”?