db4objects 域和 DAL
如何使领域独立于 db4objects。
引言
我花了一段时间阅读关于 NoSQL 数据库的文章,最终得到一个计划使用 db4objects 的项目。这个 OODBMS 具有一个非常有用的功能,称为 透明激活,即延迟加载。它需要存储对象具备某些功能,但这与一个众所周知的理念相矛盾,即领域不应依赖于对象如何持久化。
使用代码
代码非常简单,主要思想不在于编码,而在于设计。
我首先想到的解决方案是基于我使用 NHibernate 的经验,每个领域类都像这样
public class Book
{
public virtual string Name { get; set; }
}
NHibernate 会负责创建带有所有必需修改的 Book
类派生类。db4o 不会为我们做这件事,所以我们需要自己实现 IActivatable
接口,并且有以下选项
- 编写代码生成器
- 为 DAL 中的每个类创建派生类,添加所需的更改
- 在领域中创建一个公共基类,并在那里实现所需的更改
选项 1 非常复杂,通常可以被认为是一种开销,除非我们是 db4o 开发者 :)
选项 2 看起来是合理的,但我们将不得不一遍又一遍地复制粘贴大量内容,因为 C# 中没有多重继承。
派生类看起来会是这样
public class DALBook : Domain.Book, IActivatable
{
public virtual string Name
{
get
{
Activate(ActivationPurpose.Read);
return base.Name;
}
set
{
Activate(ActivationPurpose.Write);
base.Name = value;
}
}
/*
Here is the part that is required for the transparent activation
which is identical for each DAL class.
*/
[Transient]
private IActivator _activator;
public void Activate(ActivationPurpose purpose)
{
if(_activator != null)
{
_activator.Activate(purpose);
}
}
public void Bind(IActivator activator)
{
if (_activator == activator)
{
return;
}
if (activator != null && null != _activator)
{
throw new System.InvalidOperationException();
}
_activator = activator;
}
}
似乎希望创建一个公共超类,它将实现 Activate
和 Bind
方法。
选项 3 意味着我们将领域和 DAL 绑定在一起——这是一个非常不灵活的解决方案,而且只是一个坏主意
- 领域将“知道”它的持久化
- 不相关的类将获得一个公共超类
- 等等。
让我们将类更改为接口
public interface Book
{
string Name { get; set; }
}
现在在 DAL 中,我们可以创建一个适当的层次结构,其中包含一个基类,该基类实现所有可持久化对象真正通用的逻辑。
internal abstract class Db4oBase: IActivatable
{
[Transient]
private IActivator _activator;
public void Activate(ActivationPurpose purpose)
{
if(_activator != null)
{
_activator.Activate(purpose);
}
}
public void Bind(IActivator activator)
{
if (_activator == activator)
{
return;
}
if (activator != null && null != _activator)
{
throw new System.InvalidOperationException();
}
_activator = activator;
}
}
internal class DALBook : Db4oBase, Domain.IBook
{
private string name_;
public virtual string Name
{
get
{
Activate(ActivationPurpose.Read);
return name_;
}
set
{
Activate(ActivationPurpose.Write);
name_ = value;
}
}
}
所有 DAL 类都标记为 internal
,因为 DAL 客户端应该只了解领域接口,因此应该实现一种基于所需接口实例化具体类别的解决方案。例如
public static class DAOFactory
{
public static T CreateDomainObject<T>()
{
string dalClass = string.Format("DAL.{0}", typeof(T).Name.Substring(1));
return (T)Type.GetType(dalClass).GetConstructor(Type.EmptyTypes).Invoke(null);
}
}
使用 DAOFactory
,我们可以创建对象并根据它们的接口来处理它们,例如
Domain.IBook book = DAL.DAOFactory.CreateDomainObject<Domain.IBook>();
关注点
领域可以是一组接口,DAL 是一组具体类,具有完全封装的实现,该实现本身可以具有任何类层次结构,以消除复制粘贴并避免不必要的内聚。
历史
- 2011 年 8 月 14 日 - 初始版本。
- 2011 年 8 月 16 日 - 修复了左尖括号。