私は最近、Kazi Manzur Kigg MVC の実装 (Kazi rocks) を調べていて、DRY/SOC の原則を破っているように見えるいくつかのコードに気付きました。懸念事項を分離するためのリファクタリングの可能性について、皆さんの考えをぜひお聞かせください。
Kigg は、各リポジトリ クラスにAdd
とメソッドの両方を実装します (注:各具体的な実装によってオーバーロードできる仮想メソッドがあります。)Remove
BaseRepository
Kigg.Repository.LinqToSql.CategoryRepository
および両方の実装は、子エンティティを削除するために、メソッドをKigg.Repository.LinqToSql.StoryRepository
介して削除をカスケードします。Remove
(注: カテゴリにはストーリーとの親関係 (1 対多) があるため、ストーリーからオブジェクト グラフまで同じ子関係を共有します)ダイアグラムを参照してください。問題のコードは、両方のリポジトリが互いの子エンティティを削除する方法です。
カテゴリリポジトリ
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
StoryRepository
DeleteAll
CategoryRepository
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);
}
}
}
この 2 番目のリファクタリングにより、ユニット テスト中に Unity Ioc を介してのインスタンスをIStoryRepository
注入できるようになりました。CategoryRepository
もちろん、このリファクタリングを各リポジトリ クラスに拡張して、それらが自分の子供の責任を果たすことができるようにする必要があります。
みんなの感想は?