重构代码避免反模式 [英] Refactoring code to avoid anti-pattern

查看:166
本文介绍了重构代码避免反模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


  1. 存储库是作为域对象的公共属性创建的作为接口成员。


  2. 域对象(FixedBankAccount)使用存储库直接存储数据。这是违反单一责任原则吗?如何纠正?


注意:存储库模式是使用LINQ to SQL实现的。



编辑



下面给出的代码是更好的方法吗?
https://codereview.stackexchange。 com / questions / 13148 / is-it-good-code-to-satisfied-one-responsibility-principle



代码

  public interface IBankAccount 
{
RepositoryLayer.IRepository< RepositoryLayer.BankAccount> AccountRepository {get;组; }
int BankAccountID {get;组; }
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;组; }

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(repositoryroyAccount);
if(acc!= null)
{
acc.BankAccountID = repositroyAccount.BankAccountID;
acc.accountRepository = this.accountRepository;
acc.FreezeAccount();
}
}
}
}

br>

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


  public class MySimpleBankAccountFactory:IBankAccountFactory 
{
public DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositoryroyAccount)
{
DomainObjectsForBank.IBankAccount acc =空值;

if(String.Equals(repositoryroyAccount.AccountType,Fixed))
{
acc = new DomainObjectsForBank.FixedBankAccount();
}

if(String.Equals(repositoryroyAccount.AccountType,Savings))
{
acc = new DomainObjectsForBank.SavingsBankAccount();
}

return acc
}
}




阅读:


  1. DDD - 实体状态转换


  2. https://codereview.stackexchange.com/questions/13148/is-it-good-code-to-satisfy - 单一责任原则


  3. 使用单一责任原则强制我的容器有公共设置


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



解决方案

重新构建这个代码,使得存储库不是一个接口会员很容易存储库是实现的依赖,而不是接口 - 将其注入到具体类中,并将其从IBankAccount中删除。

  public class FixedBankAccount:IBankAccount 
{
public FixedBankAccount(RepositoryLayer.IRepository< RepositoryLayer.BankAccount> accountRepository)
{
this.accountRepository = accountRepository;
}

私有只读RepositoryLayer.IRepository< RepositoryLayer.BankAccount> accountRepository;

public int BankAccountID {get;组; }
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。然而,这可能是也可能不是问题;许多框架将这些责任组合起来会产生很大的影响 - 例如,活动记录模式。它确实使单元测试更有趣,因为它需要您模拟您的存储库。



如果您选择拥有更持久的无知域,那么您可能会最好通过实施工作单位模式。加载/编辑/删除的实例在工作单位中注册,该单位负责在交易结束时持续更改。工作单位负责您的更改跟踪。



这是如何设置取决于您正在创建的应用程序的类型和您正在使用的工具。例如,我相信如果使用Entity Framework,您可以使用DataContext作为您的工作单位。 (Linq-to-SQL是否具有DataContext的概念?)



这里是一个例子的工作单位与实体框架4。


I have a BusinessLayer project which has the following code. The domain object is FixedBankAccount (which implements IBankAccount).

  1. The repository is made as a public property of the domain object and is made as an interface member. How to refactor it so that repository will not be an interface member?

  2. The domain object (FixedBankAccount) makes use of the repository directly to store the data. Is this a violation of Single Responsibility Principle? How to correct it?

Note: The repository pattern is implemented using LINQ to SQL.

EDIT

Is the code given in the following a better approach? https://codereview.stackexchange.com/questions/13148/is-it-good-code-to-satisfy-single-responsibility-principle

CODE

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;
    }
}


READING:

  1. DDD - Entity state transition

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

  3. Using the "Single Responsibility Principle" forces my containers to have public setters

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

解决方案

Refactoring this code so that the repository is not an interface member is easy enough. The repository is a dependency of the implementation, not the interface - inject it into your concrete class, and remove it from the 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();
    }

}

In regards to the second question...

Yes, the domain object is violating SRP by being aware of your persistence code. This may or may not be a problem, however; many frameworks mix these responsibilities for great effect - for example, the Active Record pattern. It does make unit testing a little more interesting, in that it requires you to mock your IRepository.

If you choose to have a more persistent-ignorant domain, you would probably best do so by implementing the Unit of Work pattern. Loaded/edited/deleted instances get registered in the Unit of Work, which is responsible for persisting changes at the end of the transaction. The unit of work is responsible for your change tracking.

How this is setup depends on the type of application you're creating and the tools you're using. I believe if working with Entity Framework, for example, you may be able to use the DataContext as your unit of work. (Does Linq-to-SQL have the notion of a DataContext as well?)

Here's an example of the Unit of Work pattern with Entity Framework 4.

这篇关于重构代码避免反模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆