Interface object instantiation and n-layer encapsulation

196 views Asked by At

We are bulding a system that we want the final implementation do "hide" the data access layer (so that we can change it in the future).

So the idea is that the application code will call an interface that will be implemented by Database access objects. So we have the interface class:

public interface DatabaseInterface

    {
        void Create(DataObject obj);
    }

Then we have the class that implements the interface:

public class DatabaseAccess : DatabaseInterface

    { 
        void Create(DataObject obj)
        { 
            Entity dbContext = new Entity();

            dbContext.Data.Add (obj);
            dbContext.SaveChanged();
       }
    }

Together will all this, we have the DataObject class:

public class DataObject
{
     integer Data1;
     string  Data2;
}

OUr problems sits on the main application, as I don´t know what is the DatabaseInterface implementation:

public class CreateElementOnDatabase

    {
         DataObject myObj = new DataObject();
         myObj.Data1 = 10;
         myObj.Data2 = "Test String";

         var dbAccess = new DatabaseInterface() <=== I know this is not possible, but don´t know what to do!!!!

         dbAccess.Create (myObj);
    }

How do I call the implemented method without knowing which class did it ? Surely I´m missing something on my design.

We REALLY wanna make the application fully independent from the code below.

[EDIT 1]

These implementations reside in different class libraries. So I have the following: App project DatabaseInterface project DatabaseAccess project DataObject project

2

There are 2 answers

11
Jerez On BEST ANSWER

what you are missing is a "factory" class that builds instances for outside world based on parameters or configuration settings it receives (if they want to have the outside world influencing its behavior) or make their own decisions, i.e. they will create the instances of either type A or B based on their internal logic that cannot be changed from the outside. In the code below you will see the DatabaseFactory which is what you were missing. Also, note the private/public/internal modifiers which are important to make sure you expose what is needed and not more than you need (making all classes public creates confusions to those who want to use your library)

Here is the code

// interfaces must be public - this is the "contract" with the outside world / assemblies
public interface IDatabase
{
    void Create(DataObject obj);
}

// classes that implement interfaces should be internal - outside world don't know about them
internal class SQLDatabase : IDatabase
{
    // internal on constructor helps you to make sure you are the only one 
    // that can create such an instance
    internal SQLDatabase()
    {
    }
    void Create(DataObject obj)
    {
        Entity dbContext = new Entity();
        dbContext.Data.Add(obj);
        dbContext.SaveChanged();
    }
}
internal class OracleDatabase : IDatabase
{
    internal OracleDatabase()
    {
    }
    void Create(DataObject obj)
    {
        //oracle creation method
    }
}

public class DataObject
{
    int Data1;
    string Data2;
}

// this is the factory class that creates the instances for you (ourside world)
public class DatabaseFactory
{
    // you can use either params or ideally app.config keys
    public IDatabase CreateInstace()
    {
        if (ConfigSetting == "SQL")
        {
            return new SQLDatabase();
        }
        else if (ConfigSetting == "Oracle")
        {
            return new OracleDatabase();
        }
        else throw new System.Exception ("invalid configuration setting key");
    }
}

// this is in external assembly:
public class CreateElementOnDatabase
{
     DataObject myObj = new DataObject();
     myObj.Data1 = 10;
     myObj.Data2 = "Test String";

    // you only work with public interfaces
    // and the factory creates the instances for you ...
     IDatabase db = DatabaseFactory.CreateInstace();
     db.Create(myObj);
}
3
Daniel Gabriel On

You could make a class that would create the correct DAL object. Something like this:

public class RepositoryFactory {
    public static DatabaseInterface MakeDAL() {
        return new DatabaseAccess();
    }
}

Then, to make a DAL object:

var dbAccess = RepositoryFactory.MakeDAL();

Depending on how much separation you want between the app and the database access, you would put the interfaces, the interface implementations, and the factory class in separate assemblies, or put some in one assembly and some in another. It really depends on what you mean when you say "fully independent from the source code".