在软件开发中,对象的持久化是一个常见而重要的议题。透明激活(transparent activation)作为一种延迟加载技术,能够显著提高应用程序的性能。本文将探讨如何在不违反领域模型独立性的前提下,实现透明激活。
透明激活要求存储的对象具备某些功能,但这与领域模型不应依赖于其持久化方式的设计理念相冲突。为了解决这一矛盾,需要在设计上做出一些权衡和创新。
代码实现的关键在于设计而非编码本身。作者首先考虑了NHibernate的经验,其中每个领域类都类似于以下形式:
public class Book {
public virtual string Name { get; set; }
}
NHibernate会自动创建一个Book类的派生类,包含所有必要的修改。然而,db4o不会为做这些,所以需要自己实现IActivatable接口。
有几种实现透明激活的选项:
选项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;
}
}
[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;
}
}
为了实现透明激活,每个DAL类都需要这样的部分。
为了使代码更加灵活,可以考虑创建一个通用的超类来实现Activate和Bind方法。
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() {
string dalClass = string.Format("DAL.{0}", typeof(T).Name.Substring(1));
return (T)Type.GetType(dalClass).GetConstructor(Type.EmptyTypes).Invoke(null);
}
}
Domain.IBook book = DAL.DAOFactory.CreateDomainObject();