关于改进活动记录模式的提案

时间:2017-01-30 08:28:52

标签: oop design-patterns

我想提出一个我想出的设计模式。我没有看到它之前使用它是令人惊讶的,因为它修复了一个常见的问题,所以我想知道我是否还没有找到它,或者我的解决方案是不受欢迎的还是打破了任何主要的设计原则。如果有人可以向我提供任何描述,或提供意见或批评的链接。如果你没有,那么随意使用=)

我经常使用Active Record Pattern,因为它很简单,完全了解它在数据库上创建的不良耦合,以及使用丑陋的SQL代码来填充域对象。所以我的主张如下:

interface IUser{
    getFirstName();
    getLastName():
    setFirstName();
    setLastName();
}

class User implements IUser{

   id:string;
   firstName:string;
   lastName:string;

   constructor(id:string, firstName:string, lastName:string){
      this.id=id;
      this.setFirstName(firstName);
      this.setLastName(lastName);
   }

   function getFirstName(){
      return this.firstName;
   }

   function getLastName(){
      return this.lastName;
   }

   function setFirstName(firstName:string){
      this.firstName=firstName;
   }

   function setLastName(lastName:string){
      this.lastName=lastName;
   }

}


class UserDB implements IUser{
   user:IUser;

   constructror(user:IUser){
      this.user=user;
   }

   function getFirstName(){
      return this.user.getFirstName();
   }

   function getLastName(){
      return this.user.getLastName();
   }

   function setFirstName(firstName:string){
      this.this.setFirstName(firstName);

      this.update();
   }

   function setLastName(lastName:string){
      this.setLastName(lastName);

      this.update();
   }

   function update(){
      // sql execution script
      // update user set first_name=getFirstName(), last_name=getLastName() where id=this.user
   }
}

然后在代码中创建实例,无论是工厂还是用户的回购都会这​​样做:

function getUser(id:string):User{
   // select first_name, last_name from user where id=id;

   var user=new User(id, firstName, lastName);
   var userdb=new UserDB(user);

   return user
}

这可能是一个不好的例子,因为通常用户不会更改他们的名字或姓氏,但这是我能提出的最简单的例子!

我基本上使用GoF Decorator模式,但从未见过用这种方式进行数据访问。

对于每个属性更新执行db访问仍然会产生不良影响,就像活动记录模式一样,但它至少不会删除从User类到数据库的耦合并删除丑陋的SQL代码。 UserDB实现可以与其他数据访问类位于同一个包中。解决方案 unbreak 来自Active Record Pattern的SRP原则。我知道Active Record Pattern被一些人认为是一种反模式,但它很容易在简单的应用程序上使用,所以重点如下,如果你打算使用它,这不是一个更好的实现对吗?

编辑以回应Matia的评论:

Matias,非常感谢你的评论,批评正是我所寻求的。但是,我不同意你的结论,让我解释一下原因:

首先看一下你的示例User类,它是一个域实体,但却有数据存储的含义。域对象不应该有save()方法。我的示例(User类)确实是一个域对象。域对象不应该具有存储库的依赖性,尤其是在构造函数中(!)存储库关注数据存储问题,因此您立即向全世界声明用户域对象关注持久性。此外,User实例不应该知道如何保持自身或如何检索其他User实例 - 所以这是另一个重大违规。

虽然我知道人们确实从域对象引用存储库,但这被认为是非常糟糕的做法,并且在互联网上有很多引用这一点,为方便起见,这里有几个链接:

Is it ok for entities to access repositories?

http://thinkbeforecoding.com/post/2013/03/03/Entities-and-Repository-injection-ndash%3B-follow-up

如果您仍然认为用户类可以引用用户存储库,请查看这些链接,或者请与我分享任何得出相反结论的链接。

  

“单一责任原则。您的活跃记录拥有   持久性逻辑。“

你是绝对正确的,UserDB拥有持久性逻辑,但这正是Active Record Pattern应该做的,并且记住我正在将我的解决方案与那个解决方案进行比较。

但我所做的和我的提议的重点是将持久性逻辑依赖性与域层分离,从而从中删除SQL,因为UserDB将驻留在数据访问层中,允许我的User类真正属于在域层中,而您的用户类却不能。

  

“依赖倒置原则。你创建依赖关系   实现而不是抽象。“

关于依赖于具体类,我假设你引用的是我正在从具体的User类扩展UserDB类这一事实?我知道具体的依赖性,但是通过引入IUser接口并让User和UserDB都实现它可以很容易地消除它,但是我试图保持代码最小化以达到预防和方便的目的。所以我确实认识并同意这一点,但它很容易纠正。

  

“正如你可以检查我的样本,我不是坚持活跃的记录   设置属性时更改,但是当User.Save()为时,它会完成   叫,这似乎是一种更好的方法。“

如果性能是一个问题,那么我会考虑不在每个属性更新上调用它,但它会再次存在于UserDB而不是像您的样本那样的User类,原因我认为我已经解释过了。

1 个答案:

答案 0 :(得分:0)

  

完全了解它在数据库上创建的不良耦合,   并且使用丑陋的SQL代码来破坏域对象

实际上这个陈述可能是错误的,因为整个活动记录可以注入一个从头开始解决问题的存储库。

在C#中:

public class User 
{
    public User(IUserRepository repository)
    {
        Repository = repository;
    }

    private IUserRepository Repository { get; }

    public string Name { get; set; }
    public string LastName { get; set; }

    public void Save() 
    {
         Repository.Update(this);
    }
}

正如您可以查看我的示例,我不会在设置属性时保持活动记录的更改,但是在{{1被调用,这似乎是一种更好的方法。

您是否想指出哪些原则会违反您的方法?至少我发现了两个:

  • 单一责任原则。您的活动记录拥有持久性逻辑。
  • 依赖倒置原则。您可以在实现上创建依赖项,而不是在抽象上创建依赖项。