c# - C# 重构代码以避免 Pattern

  显示原文与译文双语对照的内容
102 1

我有一个BusinessLayer项目,它有以下代码。域对象为 FixedBankAccount ( 实现 IBankAccount ) 。

存储库作为域对象的public 属性进行,并作为接口成员进行。如何重构它以便存储库不会成为接口成员 ?

域对象( FixedBankAccount ) 直接使用存储库来存储数据。这与 单一职责原则(Single Responsibility Principle) 有冲突?如何更正它?

注意:存储库 Pattern 是使用linqtosql实现的。

编辑

下面的代码提供了更好的方法?https://codereview.stackexchange.com/questions/13148/is-it-good-code-to-satisfy-single-responsibility-principle

代码

public interface IBankAccount
{
 RepositoryLayer.IRepository<RepositoryLayer.BankAccount> AccountRepository { get; set; }
 int BankAccountID { get; set; }
 void FreezeAccount();
}


public class FixedBankAccount : IBankAccount
{
 private RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository;
 public RepositoryLayer.IRepository<RepositoryLayer.BankAccount> AccountRepository
 {
 get
 {
 return accountRepository;
 }
 set
 {
 accountRepository = value;
 }
 }
 public int BankAccountID { get; set; }
 public void FreezeAccount()
 {
 ChangeAccountStatus();
 }
 private void SendEmail()
 {
 }
 private void ChangeAccountStatus()
 {
 RepositoryLayer.BankAccount bankAccEntity = new RepositoryLayer.BankAccount();
 bankAccEntity.BankAccountID = this.BankAccountID;
 accountRepository.UpdateChangesByAttach(bankAccEntity);
 bankAccEntity.Status ="Frozen";
 accountRepository.SubmitChanges();
 }
}


public class BankAccountService
{
 RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository;
 ApplicationServiceForBank.IBankAccountFactory bankFactory;
 public BankAccountService(RepositoryLayer.IRepository<RepositoryLayer.BankAccount> repo, IBankAccountFactory bankFact)
 {
 accountRepository = repo;
 bankFactory = bankFact;
 }
 public void FreezeAllAccountsForUser(int userId)
 {
 IEnumerable<RepositoryLayer.BankAccount> accountsForUser = accountRepository.FindAll(p => p.BankUser.UserID == userId);
 foreach (RepositoryLayer.BankAccount repositroyAccount in accountsForUser)
 {
 DomainObjectsForBank.IBankAccount acc = null;
 acc = bankFactory.CreateAccount(repositroyAccount);
 if (acc!= null)
 {
 acc.BankAccountID = repositroyAccount.BankAccountID;
 acc.accountRepository = this.accountRepository;
 acc.FreezeAccount();
 }
 }
 }
}


public interface IBankAccountFactory
{
 DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositroyAccount);
}


public class MySimpleBankAccountFactory : IBankAccountFactory
{
 public DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositroyAccount)
 {
 DomainObjectsForBank.IBankAccount acc = null;
 if (String.Equals(repositroyAccount.AccountType,"Fixed"))
 {
 acc = new DomainObjectsForBank.FixedBankAccount();
 }
 if (String.Equals(repositroyAccount.AccountType,"Savings"))
 {
 acc = new DomainObjectsForBank.SavingsBankAccount();
 }
 return acc;
 }
}


阅读:

DDD实体状态转换

https://codereview.stackexchange.com/questions/13148/is-it-good-code-to-satisfy-single-responsibility-principle

使用"单一职责原则(Single Responsibility Principle)"强制我的容器拥有 public 设置器。

https://softwareengineering.stackexchange.com/questions/150760/single-responsibility-principle-how-can-i-avoid-code-fragmentation

时间:原作者:0个回答

140 4

这是一种反模式,因为反模式在第一个位置是 Pattern,而我不知道任何一个的值。

但是,由于你的BankAccount域对象混合 3个责任,所以使用IMO肯定是不好的做法:

  • 它作为域对象的自然和合法责任冻结自身并改变它的状态。

  • 更新和提交对持久性存储( 使用 accountRepository )的更改的责任。

  • 决定如何发送邮件并发送邮件的责任。

因这里,你的域对象与太多的东西紧密耦合,使它硬和易碎。它可能会改变,可能会因太多原因而中断。

所以没有反向模式,但是与 单一职责原则(Single Responsibility Principle) for的冲突确定。

最后 2个职责应该移动到单独的对象。提交更改代替了管理业务事务( 工作单位)的对象,并且知道结束事务和刷新事务的正确时间。第二个可以放在基础设施层的EmailService中。理想情况下,执行全局冻结操作的对象不应该注意消息传递机制( 邮寄或者其他东西),而应该使用它。

原作者:
121 4

重构这里代码以便存储库不是接口成员很容易。存储库是实现的依赖项而不是接口- 将它的注入到具体类中,并将它的从IBankAccount中删除。

public class FixedBankAccount : IBankAccount
{
 public FixedBankAccount(RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository)
 {
 this.accountRepository = accountRepository;
 }
 private readonly RepositoryLayer.IRepository<RepositoryLayer.BankAccount> accountRepository;
 public int BankAccountID { get; set; }
 public void FreezeAccount()
 {
 ChangeAccountStatus();
 }
 private void SendEmail()
 {
 }
 private void ChangeAccountStatus()
 {
 RepositoryLayer.BankAccount bankAccEntity = new RepositoryLayer.BankAccount();
 bankAccEntity.BankAccountID = this.BankAccountID;
 accountRepository.UpdateChangesByAttach(bankAccEntity);
 bankAccEntity.Status ="Frozen";
 accountRepository.SubmitChanges();
 }
}

关于第二个问题。

是,域对象通过注意持久性代码来违反 SRP 。但是,这可以能不是问题,许多框架将这些责任混合在一起,例如 Active Record Pattern 。它使得单元测试更加有趣,因为它需要你模仿你的IRepository 。

如果你选择拥有更持久的无理领域,你可以能最好通过实现工作 Pattern 单元来实现。Loaded/edited/deleted 实例在工作单元中注册,它负责在事务结束时持久化更改。工作单位负责你的变更跟踪。

如何设置取决于正在创建的应用程序类型以及所使用的工具。例如,如果使用 Entity Framework,你可以能能够将DataContext作为工作单元使用。( Linq-To-SQL 是否也有DataContext的概念)?

这里是一个工作单元的例子,其中包含 Entity Framework 4.

原作者:
137 0

Remi的解决方案更好,但更好的解决方案是:

1-不向域对象注入任何内容:你不需要把任何东西注入你的域服务服务。不是仓库。什么都不是。只是纯领域模型的优缺点。

2-让服务层直接存储库进行 SubmitChanges 。但是请注意,服务层应该是 &域对象,不应该是贫血。

原作者:
...