重构代码,以避免反模式重构代码,以避免反模式(Refactoring code to avoid a

2019-06-14 06:36发布

我有具有以下代码BusinessLayer项目。 域对象是FixedBankAccount(实现IBankAccount)。

  1. 存储库被制成域对象的一个​​公共属性,并且由作为接口构件。 如何重构它使库不会是一个接口成员

  2. 域对象(FixedBankAccount)利用存储库的直接来存储数据。 这是违反单一职责原则的? 如何纠正呢?

注:库模式使用LINQ to SQL实现。

编辑

在下面的一个更好的方法给出的代码? 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 -实体状态转变

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

  3. 使用“单一职责原则”势力我容器具有公共setter方法

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

Answer 1:

我不会说这是一个反模式,因为反模式应该是摆在首位的模式(做事的可识别的,普遍的方式),我不知道有任何“存储库中,该-Domain对象”图案。

但是,它肯定是不好的做法IMO因为你的BankAccount域对象混合3个职责:

  • 它的自然和合法的责任Domain对象冻结本身并改变其状态。

  • 该负责更新和(使用accountRepository)提交更改持久性存储。

  • 责任决定如何消息应该(在这种情况下,电子邮件)发送和发送。

其结果是,你的域对象是紧耦合的东西太多了,使得它硬脆。 它可能会改变,可能对太多的理由打破。

因此,没有反模式,但中违反单一职责原则是肯定的。

最后2个职责应转移到单独的对象。 提交的变化,而属于在管理商业交易(工作单位),并意识到正确的时间,以结束交易,并刷新事情的对象。 第二个可以被放置在基础设施层的EmailService。 理想情况下,做全球冻结操作不应该知道的消息传递机制(通过邮件或别的东西),但应与它,而不是被注入的对象,这样就可以让更多的灵活性。



Answer 2:

重构这个代码,以便存储库是不是接口成员是很容易的。 该库是实现的依赖,而不是接口 - 注入到你的具体类,并从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。 这可能是也可能不是一个问题,但是, 许多框架很大的影响混合这些责任 - 例如,活动记录模式。 它使得单元测试更有趣,因为它需要你嘲笑你的IRepository。

如果您选择有更持久无知的领域,你可能最好通过实现工作模式的单位这样做。 加载/编辑/删除的情况下获得注册工作的单位,负责在交易结束持续变化。 工作的单位是负责为您更改跟踪。

这是如何设置取决于你正在创建的应用程序类型和您所使用的工具。 我相信,如果与实体框架的工作,例如,你可以使用的DataContext你的工作单位。 (是否LINQ到SQL有一个DataContext的概念呢?)

这里的工作模式与实体框架4单元的例子。



Answer 3:

雷米的解决方案要好得多,但更好的解决办法国际海事组织将是这样:

1不要注入什么域对象: 你不需要任何东西注入到你的域entities.Not服务。 没有仓库。 没有。 只是纯粹的领域模型善良

2 -让服务层直接存储库做的SubmitChanges,...但要注意,业务层应该是与域对象不应该是贫血



Answer 4:

接口什么都没有直接与单一职责原则做的。 你不能从业务完全独立的数据访问代码的逻辑,他们在某些时候沟通! 你想要做的是尽量减少 (但不回避 ),其中发生这种情况。 确保您的数据库架构的逻辑而不是物理 (即基于谓词而不是表和列),而基于实现的代码(例如,数据库管理系统连接的驱动程序)是只在一个地方-类负责谈话的数据库。 每个实体都应当由一个类来表示。 而已。



文章来源: Refactoring code to avoid anti-pattern