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

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

问题描述

我有一个包含以下代码的 BusinessLayer 项目.域对象是 FixedBankAccount(它实现了 IBankAccount).

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

  1. 存储库是作为域对象的公共属性制作的,并作为接口成员制作.如何重构它以使存储库不会成为接口成员?

域对象 (FixedBankAccount) 直接使用存储库来存储数据.这是否违反了单一职责原则?如何纠正?

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?

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

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

编辑

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

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

代码

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


阅读:

  1. DDD - 实体状态转换

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

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

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

推荐答案

重构此代码以使存储库不是接口成员就足够容易了.存储库是实现的依赖项,而不是接口 - 将其注入到您的具体类中,然后将其从 IBankAccount 中删除.

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

}

关于第二个问题...

是的,域对象通过了解您的持久性代码而违反了 SRP.然而,这可能是也可能不是问题;许多框架将这些职责混合在一起以取得很好的效果——例如,Active Record 模式.它确实让单元测试更有趣一些,因为它需要你模拟你的 IRepository.

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.

如何设置取决于您正在创建的应用程序类型和您使用的工具.我相信,如果使用实体框架,例如,您可以使用 DataContext 作为您的工作单元.(Linq-to-SQL 是否也有 DataContext 的概念?)

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?)

这里是实体框架 4 的工作单元模式示例.

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

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

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