Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/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
C# TDD:静态方法、依赖项注入、缓存,还有你!_C#_Oop_Tdd - Fatal编程技术网

C# TDD:静态方法、依赖项注入、缓存,还有你!

C# TDD:静态方法、依赖项注入、缓存,还有你!,c#,oop,tdd,C#,Oop,Tdd,希望我能有点得体地解释这一点,因为今天我脑子里的保险丝烧断了。我正在学习C#中的TDD,所以我仍在尝试重新连接我的大脑以适应它 假设我有一个User类,它以前有一个静态方法来检索用户对象(简化如下) 因此,我试图重写它以使用依赖注入,这样如果需要的话,我可以伪造“GetUserFromDatabase”方法。这意味着我必须使函数不是静态的。数据访问层将从数据库构造用户对象,将返回的列映射到对象属性,而从缓存中检索将返回一个真正的蓝色用户对象。然而,在非静态方法中,我不能只说 this = Get

希望我能有点得体地解释这一点,因为今天我脑子里的保险丝烧断了。我正在学习C#中的TDD,所以我仍在尝试重新连接我的大脑以适应它

假设我有一个User类,它以前有一个静态方法来检索用户对象(简化如下)

因此,我试图重写它以使用依赖注入,这样如果需要的话,我可以伪造“GetUserFromDatabase”方法。这意味着我必须使函数不是静态的。数据访问层将从数据库构造用户对象,将返回的列映射到对象属性,而从缓存中检索将返回一个真正的蓝色用户对象。然而,在非静态方法中,我不能只说

this = GetUserFromCache(username);
因为它不是那样工作的。虽然我在如何使用OO解决这一问题方面并不是世界级的专家,但看起来我几乎必须从缓存中获取用户对象,并编写另一个映射函数,将返回的用户对象属性存储到新的用户实例中


这里的解决方案是什么?我错过了什么魔法?重构一切的唯一解决方案是使用工厂,而不是在对象本身中使用实例化逻辑吗?还是说我已经盯着这个问题看得太久了,而忽略了一些非常明显的东西?

我不认为你错过了任何魔法,我认为从单元测试和设计的角度来看,重构将持久性代码从业务对象中移除并放到持久性层是正确的方法。您可能需要考虑让缓存位于业务层和持久性层之间,协调业务对象的检索/更新以简化工作。如果以这种方式将缓存和持久性层分开,您应该能够模拟/伪造它们。

以前

  • 有一些代码使用数据库获取用户并将其放入缓存
之后

  • 有一些代码使用数据库获取用户
  • 有一些代码将用户放在缓存中
这两组代码不应该相互依赖

public static Func<string, UserName> Loader {get;set;}

public static Constructor()
{
  Loader = GetFromDataBase;
}

public static User GetUser(string userName)
{
  User user = GetUserFromCache()
  if (user == null)
  {
    user = Loader(userName);
    StoreUserInCache(user);
  }
  return user;
}    

public void Test1()
{
  UserGetter.Loader = Mock.GetUser;
  UserGetter.GetUser("Bob");
}
公共静态函数加载器{get;set;}
公共静态构造函数()
{
Loader=GetFromDataBase;
}
公共静态用户GetUser(字符串用户名)
{
User=GetUserFromCache()
if(user==null)
{
用户=加载器(用户名);
StoreUserInCache(用户);
}
返回用户;
}    
公共void Test1()
{
UserGetter.Loader=Mock.GetUser;
GetUser(“Bob”);
}

传统上,将使用接口而不是Func。如果涉及一个以上的方法,则显然可以选择接口而不是Func。如果方法实现本身是静态的,Func是对它们进行抽象的一种方法。

在您的示例中,我缺少的是调用“GetUser”的上下文。这可能是因为对于静态方法,您不需要考虑,因为您可以从任何地方调用它。在DI中,这意味着发送方需要以某种方式引用存储库,很可能是一个字段

当您的缓存是某个对象的字段(可能是一个外观)时,您可以使用它使您的缓存成为数据库的代理

所以你会:

class ApplicationFacade{
  private IUserRepository users = null;

  public doStuff(){
    this.users.GetUser("my-name");
  }
}
其中,IUserRepository是缓存、伪数据库和数据库的通用接口。简单地说:

interface IUserRepository{
  User GetUser(string username);
}
您的缓存现在可以是一个实现此接口的简单对象,并且由于缓存被注入,DI容器也可以注入其中

class Cache : IUserRepository {
  private IUserRepository users = null;
  public User GetUser(string username){
    if (this.NotCached(username)){
      this.ToCache(this.users.GetUser(username));
    }
    return this.FromCache(username);
  }
}
现在,根据您的需要,您可以将伪er数据库、缓存或数据库注入facade对象,如果您使用缓存对象,您可以根据需要将伪er数据库注入facade对象(如果您确实想要的话,甚至可以注入其他缓存)

当然,实际的注入机制取决于DI容器,可能需要一些额外的代码作为公共属性或构造函数字段

看一看


如果出于某种原因,您无法按照另一个答案中的建议重构所有内容以分离关注点,那么建议的方法可能对您有用。

感谢您的反馈!至于将缓存逻辑移动到持久性,我很好奇如果我实例化的对象有一堆对缓存对象的引用,然后缓存对象从缓存中退出,会发生什么。我的头很痛。+1:肯定需要更多地分离这些顾虑。将缓存作为应用程序和持久性层之间的一个独立层非常有意义。
class Cache : IUserRepository {
  private IUserRepository users = null;
  public User GetUser(string username){
    if (this.NotCached(username)){
      this.ToCache(this.users.GetUser(username));
    }
    return this.FromCache(username);
  }
}