【问题标题】:Interface object instantiation and n-layer encapsulation接口对象实例化和n层封装
【发布时间】:2013-08-26 20:27:50
【问题描述】:

我们正在构建一个系统,我们希望最终实现“隐藏”数据访问层(以便我们将来可以更改它)。

所以想法是应用程序代码将调用一个接口,该接口将由数据库访问对象实现。所以我们有了接口类:

public interface DatabaseInterface

    {
        void Create(DataObject obj);
    }

那么我们就有了实现接口的类:

public class DatabaseAccess : DatabaseInterface

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

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

将这一切加在一起,我们就有了 DataObject 类:

public class DataObject
{
     integer Data1;
     string  Data2;
}

我们的问题出在主应用程序上,因为我不知道 DatabaseInterface 实现是什么:

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);
    }

如何在不知道是哪个类的情况下调用已实现的方法?当然,我的设计遗漏了一些东西。

我们真的想让应用程序完全独立于下面的代码。

[编辑 1]

这些实现驻留在不同的类库中。所以我有以下内容: 应用项目 数据库接口项目 数据库访问项目 数据对象项目

【问题讨论】:

    标签: c# reflection interface factories


    【解决方案1】:

    您缺少的是一个“工厂”类,它根据它接收到的参数或配置设置为外部世界构建实例(如果他们想让外部世界影响其行为)或做出自己的决定,即他们将创建类型 A 或 B 的实例基于其无法从外部更改的内部逻辑。 在下面的代码中,您将看到您缺少的 DatabaseFactory。另外,请注意 private/public/internal 修饰符,这些修饰符对于确保您公开所需内容而不是超出您的需要很重要(将所有类公开会给想要使用您的库的人造成混淆)

    这里是代码

    // 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);
    }
    

    【讨论】:

    • 很棒的帖子。这是一个想做的事情......但是......所有课程都在独立的ClassLibraries上,我正在建造那个工厂。所以,想象一下我有 4 个库(App、DatabaseInterface、DatabaseAccess 和 DataObject),我应该把工厂放在哪里以及项目引用的行为方式。 (见最后一个主题 cmets。我将编辑原帖)
    • 您可以做的是:Lib A - IDatabase、IDBObject 和 DatabaseFactory。 Lib B_SQL - 实现 SQLDatabase - 它将引用 LibA 并使用 CreateInstance 方法公开静态类 DatabaseCreator,Lib B_Oracle - 将与 Lib B_SQL 执行相同的操作。我希望 Lib A 能够动态加载 B_Oracle 或 B_SQL,并调用 DatabaseCreator.CreateInstance 方法。不确定这是否是您要找的……是吗?
    • 哦-所有上层库都应该引用IDatabase
    • 没有。我不想创建不同的数据库实例。我想要的是让 App 只使用接口类和库,并让接口决定它可用的实现类型(取决于应用程序参数)。我不想引用应用程序中的任何数据库内容。我可以让它全部工作,但是当我把工厂放在接口中时,我必须引用数据库库,这给了我循环引用,因为我需要数据库库来引用接口才能实现它。经过几个小时的努力,发现了一个黑洞。
    • 澄清一下,我的目标是让 App 完全不引用任何与数据库相关的类,只引用数据库接口类。但是如果我有一个工厂,这需要引用数据库实现,这会打破我的腿,因为我不能把它放在接口类中。
    【解决方案2】:

    您可以创建一个类来创建正确的 DAL 对象。 像这样的:

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

    然后,制作一个 DAL 对象:

    var dbAccess = RepositoryFactory.MakeDAL();
    

    根据您希望应用程序和数据库访问之间的分离程度,您可以将接口、接口实现和工厂类放在单独的程序集中,或者将一些放在一个程序集中,而将一些放在另一个程序集中。当你说“完全独立于源代码”时,这真的取决于你的意思。

    【讨论】:

    • 我忘了说这些实现在不同的库中。我应该把这个类放在哪里 - 在与 DatabaseInterface 相同的库中还是在 DatabaseAccess 库中?
    • 那么您有 3 个项目:您的应用程序、接口和数据库访问?您的应用程序引用了数据库访问和接口?并且数据库访问引用了接口?
    • 我有 4 个项目:App、DatabaseAccess、DatabaseInterface 和 DataObject。我已经尝试建立一个工厂,但是如果我将工厂放在 DatabaseAccess 上,我需要从应用程序中引用它,这会破坏架构。我试图构建一个工厂接口,但我无法从 DatabaseInterface 添加对 DatabaseAccess 的引用,因为它被以另一种方式引用......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多