我有一个现实世界的问题,我将尝试将其抽象为一个说明性示例。
所以想象一下我在树中有数据对象,父对象可以访问子对象,子对象可以访问父对象:
// 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
}
}
所有三个数据对象都具有保存在两个数据存储(例如数据库和 Web 服务)中的属性,它们需要反映并与存储同步。有些对象只从 Web 服务请求,有些只向它写入。
数据映射器
我最喜欢的数据访问模式是Data Mapper,因为它将数据对象本身与与数据存储的通信完全分开:
class TopMapper {
public Top FetchById(int id) {
var top = new Top(DataStore.TopDataById(id));
top.Children = MiddleMapper.FetchForTop(Top);
return Top;
}
}
class MiddleMapper {
public Middle FetchById(int id) {
var middle = new Middle(DataStore.MiddleDataById(id));
middle.Parent = TopMapper.FetchForMiddle(middle);
middle.Children = BottomMapper.FetchForMiddle(bottom);
return middle;
}
}
这样我可以每个数据存储有一个映射器,并从我想要的映射器构建对象,然后使用我想要的映射器将其保存回来。
这里有一个循环引用,但我想这不是问题,因为大多数语言只能存储对对象的内存引用,因此实际上不会有无限数据。
这样做的问题是,每次我想构造一个新的Top
, Middle
or时,它都需要在该对象的or属性Bottom
中构建整个对象树,其中包含所有数据存储请求和所需的内存使用情况。在现实生活中,我的树比这里展示的要大得多,所以这是个问题。Parent
Children
对象中的请求
在此对象请求它们Parent
的 s 和Children
它们自己:
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;
}
}
}
我认为这是存储库模式的一个例子。那是对的吗?
这个解决方案看起来很简洁——数据只在您需要时从数据存储中请求,然后如果您想再次请求它,它会存储在对象中,避免进一步的请求。
但是,我有两个不同的数据源。有一个数据库,但也有一个 Web 服务,我需要能够从 Web 服务创建一个对象并将其保存回数据库,然后从数据库再次请求它并更新 Web 服务。
这也让我感到不安,因为数据对象本身不再对数据源一无所知。我们引入了一个新的依赖项,更不用说循环依赖项了,这使得测试变得更加困难。这些对象现在掩盖了它们与数据库的通信。
其他解决方案
是否有任何其他解决方案可以解决多个商店问题,但也意味着我不需要每次都构建/请求所有数据?