首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我应该在哪一层实现文件解析?

我应该在哪一层实现文件解析?
EN

Software Engineering用户
提问于 2015-10-09 03:56:02
回答 6查看 5.4K关注 0票数 6

在一个简单的多层体系结构中,我必须在哪个层实现类似于解析文件的内容。例如:我有一个文件,我必须将特定信息提取到一个对象中。我认为这是数据访问,因为业务层需要数据。这些数据的来源和来源并不是重点。你同意吗?

EN

回答 6

Software Engineering用户

发布于 2015-10-09 07:22:31

我建议停止用“层”来思考,开始用“模块”来思考。现在,您的数据访问层只包含数据库访问模块。所以就在它旁边,你会把文件访问模块。该模块与数据库访问模块在哪些模块可以引用,哪些模块可以引用方面与数据库访问模块处于同一层。但是它可能是单独的库,就像数据库访问模块是它自己的库一样(我强烈希望如此)。

更好的情况是,如果您有一个保存和加载业务数据的抽象。然后,数据访问层(及其包含模块)将包含此抽象的实现。因此,您将拥有SaveDataInDatabaseSaveDataInFileXSaveDataInFileY类。然后,业务代码将在不知道或不关心数据将如何持久化的情况下使用此抽象。

票数 6
EN

Software Engineering用户

发布于 2015-10-09 14:22:02

从你在基本层面上的描述来看,你是:

  1. 从某些持久存储中提取数据
  2. 将其解析为某种通用格式
  3. 将其映射到对象和属性

存储库模式解决了这个问题。它涉及三种主要类型的课程:

  1. "Repository“类,它只处理对象模型,例如应用程序中的”实体类“或”域模型“。它可以提供对此数据的读写访问。
  2. "Gateway“类,它负责将数据读写到持久存储中,无论是数据库、web服务还是文件。正如我们稍后将看到的,这是文件解析应该存在的地方。
  3. "Factory“类,它创建域模型的实例,并从Gateway返回到域模型的泛型数据映射数据。

这种模式的关键是为每种类型的类实现接口。让我们来看看如何以这种方式对博客进行建模和持久化。

库模式域模型及接口

让我们从域模型开始:

代码语言:javascript
复制
public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

别太花哨了。接下来,我们需要定义我们的接口,首先通过定义博客存储库的接口。

代码语言:javascript
复制
public interface IBlogRepository
{
    Blog Find(int id);
    void Create(Blog blog);
    void Update(Blog blog);
    void Delete(Blog blog);
}

IBlogRepository接口主要集中在域模型上,而没有提到持久性是如何实际实现的。这为应用程序的其余部分提供了一层解耦。

接下来,blog网关接口允许存储库在数据存储上预先形成特定的CRUD操作(创建、读取、更新、删除)。

代码语言:javascript
复制
public interface IBlogGateway
{
    IList<IDictionary<string, object>> Load();
    void Create(int id, string name, string description);
    void Update(int id, string name, string description);
    void Delete(int id);
}

最好让“网关”不了解您的领域模型,因为这将提供最大的灵活性,以有限的影响“网关”改变您的领域模型,并以有限的影响您的领域模型。

Load方法只返回字典对象的列表,这是一种很好的通用数据格式。

最后一个接口是博客工厂对象,它负责创建域模型的实例并将通用数据映射到Blog对象。

代码语言:javascript
复制
public interface IBlogFactory
{
    Blog CreateInstance(IDictionary<string, object> data);
    void UpdateInstance(IDictionary<string, object> data, Blog instance);
}

存储库模式具体类

现在我们已经定义了接口,让我们从创建一个实现BlogRepository接口的IBlogRepository类开始:

代码语言:javascript
复制
public class BlogRepository : IBlogRepository
{
    private IBlogGateway gateway;
    private IBlogFactory factory;
    private IList<Blog> cache;

    private IList<Blog> Cache
    {
        get
        {
            if (cache == null)
            {
                var data = gateway.Load();

                cache = new List<Blog>();

                foreach (var record in data)
                {
                    cache.Add(factory.CreateInstance(record));
                }
            }

            return cache;
        }
    }

    public BlogRepository(IBlogGateway gateway = null, IBlogFactory factory = null)
    {
        this.gateway = gateway ?? new BlogFileGateway(@"C:\Path\To\Blogs.txt");
        this.factory = factory ?? new BlogFactory();
    }

    public Blog Find(int id)
    {
        Cache.SingleOrDefault(b => b.Id == id);
    }

    public void Create(Blog blog)
    {
        Cache.Add(blog);
        gateway.Create(blog.Id, blog.Name, blog.Description);
    }

    public void Update(Blog blog)
    {
        gateway.Update(blog.Id, blog.Name, blog.Description);
    }

    public void Delete(Blog blog)
    {
        Cache.RemoveAll(b => b.Id == blog.Id);
        gateway.Delete(blog.Id);
    }
}

现在我们可以开始看看每一块是如何结合在一起的。BlogRepository的构造函数允许您传递自己的网关和工厂对象,返回到您选择的默认实现。

要找到单个Blog,存储库首先从网关加载所有数据,并将其映射到使用工厂的对象。然后,Find方法返回与Id匹配的第一个Blog。诚然,您可能希望在“网关”对象上创建一个方法来返回单个记录,但是存储库也是实现缓存的好地方。

现在我们进入您感兴趣的部分:解析文件。

代码语言:javascript
复制
public class BlogFileGateway : IBlogGateway
{
    private string filePath;

    public BlogFileGateway(string filePath)
    {
        this.filePath = filePath;
    }

    public IList<IDictionary<string, object>> Load()
    {
        // Read entire file and return the parsed data
    }

    public void Create(int id, string name, string description)
    {
        // Append the new data to the file
    }


    public void Update(int id, string name, string description)
    {
        // Update the correct row within the file
    }


    public void Delete(int id)
    {
        // Delete the correct row within the file
    }
}

BlogFileGateway实现IBlogGateway接口。这就是打开、读取和解析文件的地方。如果您有一个通用文件格式,您的“网关”类可以创建另一个类的实例,负责解析文件中的每个记录。

为了更好的衡量,部落格工厂类:

代码语言:javascript
复制
public class BlogFactory : IBlogFactory
{
    public Blog CreateInstance(IDictionary<string, object> data)
    {
        if (data == null)
            return null;

        Blog instance = new Blog();

        UpdateInstance(data, instance);

        return blog;
    }

    public void UpdateInstance(IDictionary<string, object> data, Blog instance)
    {
        instance.Id = int.Parse(data["id"].ToString());
        instance.Name = data["name"].ToString();
        instance.Description = data["description"].ToString();
    }
}

--为什么存储库模式工作最好的

由于您将数据访问和映射与域模型分离开来,所以您可以从文件存储开始。转移到数据库存储需要编写一个新的网关类来访问数据库,以及一个新的工厂类将数据从数据库映射到您的Blog域模型。如果稍后您决定转向面向服务的体系结构,则只需编写另一个网关来调用web服务和另一个工厂即可。您可以从根本上改变或切换存储机制,而无需重构应用程序的主要部分。

@BartVanIngenSchenau在他的回答中说:

用于其他类型的数据访问(与第三方进行数据交换、读取/写入文件等)这些体系结构具有服务的概念,用于执行数据的传输,并在外部格式和业务层可以使用的东西之间进行转换。

(强调地雷)

使用Repository模式的优点是,调用web服务的代码不必存在于称为“服务”的应用程序的通用且经常被过度使用的层中。调用web服务发生在网关中。

示例用法

让我们找到一个博客,更改一个值并更新它:

代码语言:javascript
复制
IBlogRepository repository = new BlogRepository();

Blog blog = repository.Find(3);

blog.Description = "Black and white, and read all over";

repository.Update(blog);

稍后,您需要使用MySQL数据库以便编写BlogMySqlGatewayBlogMySqlFactory类,然后更改一行代码:

代码语言:javascript
复制
IBlogRepository repository = new BlogRepository(new BlogMySqlGateway(), new BlogMySqlFactory());

Blog blog = repository.Find(3);

blog.Description = "Black and white, and read all over";

repository.Update(blog);

没有什么需要改变的了。稍后,您决定所有这些都需要生活在“云”中:

代码语言:javascript
复制
IBlogRepository repository = new BlogRepository(new BlogWebServiceGateway(), new BlogWebServiceFactory());

Blog blog = repository.Find(3);

blog.Description = "Black and white, and read all over";

repository.Update(blog);

没有什么需要改变的了。

票数 3
EN

Software Engineering用户

发布于 2015-10-09 04:30:35

在解析文件中的数据时,您可以在UserServicesLayer中这样做,如果要操作那些需要任何逻辑条件的数据,那么是时候调用BusinessLayer然后执行该层中的所有逻辑操作了,您的DataAccessLayer就像从数据库访问数据的桥,因此不建议在DataAccesLayer中提取数据。

希望这能有所帮助。

票数 0
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/299435

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档