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