我最近查看了 Kazi Manzur Kigg MVC 实现(Kazi rock),并注意到一些似乎违反 DRY/SOC 原则的代码。我很想听听每个人对可能的重构以分离关注点的想法。
Kigg在每个存储库类上都实现了一个Add
和方法(注意:具有虚拟方法,每个具体实现都可以重载。)Remove
BaseRepository
theKigg.Repository.LinqToSql.CategoryRepository
和Kigg.Repository.LinqToSql.StoryRepository
both 级联的实现通过它们的Remove
方法删除子实体。(注意:Category 与 Story 有父关系(一对多),因此它们从 Story 到对象图共享相同的子关系)见图表。有问题的代码是两个存储库相互删除子实体的方式:
类别存储库
namespace Kigg.Repository.LinqToSql.CategoryRepository{
//using statements omitted
public class CategoryRepository : BaseRepository<ICategory, Category>, ICategoryRepository
{
//code omitted
public override void Remove(ICategory entity)
{
Check.Argument.IsNotNull(entity, "entity");
Category category = (Category) entity;
Database.DeleteAll(Database.StoryViewDataSource.Where(v => v.Story.CategoryId == category.Id));
Database.DeleteAll(Database.CommentSubscribtionDataSource.Where(cs => cs.Story.CategoryId == category.Id));
Database.DeleteAll(Database.CommentDataSource.Where(c => c.Story.CategoryId == category.Id));
Database.DeleteAll(Database.VoteDataSource.Where(v => v.Story.CategoryId == category.Id));
Database.DeleteAll(Database.MarkAsSpamDataSource.Where(sp => sp.Story.CategoryId == category.Id));
Database.DeleteAll(Database.StoryTagDataSource.Where(st => st.Story.CategoryId == category.Id));
Database.DeleteAll(Database.StoryDataSource.Where(s => s.CategoryId == category.Id));
base.Remove(category);
}
}
}
故事库
namespace Kigg.Repository.LinqToSql
{
//using statements omitted
public class StoryRepository : BaseRepository<IStory, Story>, IStoryRepository
{
//code omitted
public override void Remove(IStory entity)
{
Check.Argument.IsNotNull(entity, "entity");
Story story = (Story) entity;
Database.DeleteAll(Database.StoryViewDataSource.Where(sv => sv.StoryId == story.Id));
Database.DeleteAll(Database.CommentSubscribtionDataSource.Where(cs => cs.StoryId == story.Id));
Database.DeleteAll(Database.CommentDataSource.Where(c => c.StoryId == story.Id));
Database.DeleteAll(Database.VoteDataSource.Where(v => v.StoryId == story.Id));
Database.DeleteAll(Database.MarkAsSpamDataSource.Where(sp => sp.StoryId == story.Id));
Database.DeleteAll(Database.StoryTagDataSource.Where(st => st.StoryId == story.Id));
base.Remove(story);
}
}
}
假设更好的设计会在 上CategoryRepository
调用Remove
方法StoryRepository
,从而将 Story 的子对象删除的关注委托给StoryRepository
它所属的位置,我是否正确?从维护的角度来看,对 Story 的孩子的任何添加都需要将DeleteAll
调用添加到 theCategoryRepository
和StoryRepository
.
什么是更好的实施方式?
是否应该CategoryRepository
重构以StoryRepository
直接使用?:
CategoryRepository(重构)
namespace Kigg.Repository.LinqToSql.CategoryRepository{
//using statements omitted
public class CategoryRepository : BaseRepository<ICategory, Category>, ICategoryRepository
{
//code omitted
public override void Remove(ICategory entity)
{
Check.Argument.IsNotNull(entity, "entity");
Category category = (Category) entity;
// refactor - start
StoryRepository _storyRepository = new StoryRepository( Database );
category.Stories.ForEach( story => _storyRepository.Remove( story ) );
// refactor - end
base.Remove(category);
}
}
}
此重构将允许CategoryRepository
重用 中的删除逻辑,并且还应该重用由给定构造函数的参数引用StoryRepository
的相同 LinqToSql 。但是当涉及到单元测试时,它开始有异味。DataContext
Database
StoryRepository
更好的重构是否包括使用 IoC(Kigg 使用 Unity 作为其 Ioc 容器)将PerWebRequest
作用域实例注入IStoryRepository
到CategoryRepository
的构造函数中?
CategoryRepository(重构采取 2)
namespace Kigg.Repository.LinqToSql.CategoryRepository{
//using statements omitted
public class CategoryRepository : BaseRepository<ICategory, Category>, ICategoryRepository
{
private readonly IStoryRepository _storyRepository;
public CategoryRepository(IDatabase database, IStoryRepository storyRepository) : base(database)
{
Check.Argument.IsNotNull(storyRepository, "storyRepository");
_storyRepository = storyRepository;
}
public CategoryRepository(IDatabaseFactory factory, IStoryRepository storyRepository) : base(factory)
{
Check.Argument.IsNotNull(storyRepository, "storyRepository");
_storyRepository = storyRepository;
}
//code omitted
public override void Remove(ICategory entity)
{
{
Check.Argument.IsNotNull(entity, "entity");
Category category = (Category) entity;
// refactor - start
category.Stories.ForEach( story => _storyRepository.Remove( story ) );
// refactor - end
base.Remove(category);
}
}
}
通过第二次重构,我们现在可以在单元测试期间和通过 Unity IocIStoryRepository
注入的实例。CategoryRepository
当然,我们必须将此重构扩展到每个存储库类,以便他们可以倾向于自己孩子的责任。
大家的想法是什么?