Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Design patterns 具有多个存储选项的持久数据树的模式?_Design Patterns_Data Structures_Tree_Repository_Persistence - Fatal编程技术网

Design patterns 具有多个存储选项的持久数据树的模式?

Design patterns 具有多个存储选项的持久数据树的模式?,design-patterns,data-structures,tree,repository,persistence,Design Patterns,Data Structures,Tree,Repository,Persistence,我有一个现实世界的问题,我将试图把它抽象成一个说明性的例子 假设我在树中有数据对象,父对象可以访问子对象,子对象可以访问父对象: // Interfaces interface IParent<TChild> { List<TChild> Children; } interface IChild<TParent> { TParent Parent; } // Classes class Top : IParent<Middle> {} class

我有一个现实世界的问题,我将试图把它抽象成一个说明性的例子

假设我在树中有数据对象,父对象可以访问子对象,子对象可以访问父对象:

// Interfaces
interface IParent<TChild> { List<TChild> Children; }
interface IChild<TParent> { TParent Parent; }

// Classes
class Top : IParent<Middle> {}
class Middle : IParent<Bottom>, IChild<Top> {}
class Bottom : IChild<Middle> {}

// Usage
var top = new Top();
var middles = top.Children; // List<Middle>
foreach (var middle in middles) {
    var bottoms = middle.Children; // List<Bottom>
    foreach (var bottom in bottoms) {
        var middle = bottom.Parent; // Access the parent
        var top = middle.Parent; // Access the grandparent
    }
}
通过这种方式,我可以为每个数据存储创建一个映射器,并从我想要的映射器构建对象,然后使用我想要的映射器将其保存回

这里有一个循环引用,但我想这不是问题,因为大多数语言只存储对象的内存引用,所以实际上不会有无限的数据

这方面的问题是,每当我想要构造一个新的
Top
Middle
Bottom
,它都需要在该对象的
父属性或
子属性中构建整个对象树,并包含所有数据存储请求和所需的内存使用。在现实生活中,我的树比这里的要大得多,所以这是个问题

对象中的请求

在这种情况下,对象请求其
父对象和
子对象自己:

class Middle {
    private List<Bottom> _children = null; // cache
    public List<Bottom> Children {
        get {
            _children = _children ?? BottomMapper.FetchForMiddle(this);
            return _children;
        }
        set {
            BottomMapper.UpdateForMiddle(this, value);
            _children = value;
        }
    }
}
中产阶级{
私有列表_children=null;//缓存
公开儿童名单{
得到{
_children=_children??BottomMapper.fetchFormidle(此);
返回儿童;
}
设置{
UpdateForMiddle(这个值);
_儿童=价值;
}
}
}
我认为这是一个例子。对吗

这个解决方案看起来很简洁——数据只在您需要时从数据存储中被请求,然后如果您想再次请求它,它就会存储在对象中,从而避免进一步的请求

但是,我有两个不同的数据源。有一个数据库,但也有一个web服务,我需要能够从web服务创建一个对象并将其保存回数据库,然后再次从数据库请求它并更新web服务

这也让我感到不安,因为数据对象本身不再对数据源一无所知。我们引入了一个新的依赖项,更不用说循环依赖项了,这使得它更难测试。对象现在屏蔽了它们与数据库的通信

其他解决方案


是否有其他解决方案可以解决多个存储区的问题,但也意味着我不需要每次都构建/请求所有数据?

您实际上是在问两个不相关的问题。是的,存储库模式应该导致从项目本身(实体)对存储一无所知的存储中提取项目。因此,您通常拥有实现CRUD的存储库,每当您需要一个实体时,您都可以从那里获得它们。我不明白地图绘制的意义,也不明白你为什么需要中产阶级

第二个问题是如何处理可能不是一次建成的大树。因为您用设计模式标记了它,所以建议树类应该实现,而不是“映射器”,您应该有一个。然后,建设者必须能够分阶段构建东西。这是一个有趣的问题。在依赖注入的世界中,这一点更加复杂


试着考虑一个版本,它不会有仅仅用于位置区分的类,我认为Composite将使之成为可能。

我认为Composite只适用于某些类型的树-“当客户端应该忽略对象和单个对象的组成之间的差异时”(来自Wikipedia)。我的树不是这样的。收藏绝对不同于个人。至于“构建器”模式,它说“相同的构建过程可以创建不同的表示”。我想要的是相反的——不同的构造过程(来自不同的数据格式)产生相同的表示。Re:为什么我需要中产阶级——这是说明的一部分。我只是说,我的树中有同时具有父项和子项的项。我建议复合的原因是,您显示的是基于位置逻辑的不同类,因此复合可以消除这些差异。在构建器模式中,您是在反向阅读。请看四人帮中的示例:您正在从两种不同的表示构建一些东西。他们的例子正好说明了这一点。
class Middle {
    private List<Bottom> _children = null; // cache
    public List<Bottom> Children {
        get {
            _children = _children ?? BottomMapper.FetchForMiddle(this);
            return _children;
        }
        set {
            BottomMapper.UpdateForMiddle(this, value);
            _children = value;
        }
    }
}