重构code,以避免反模式重构、模式、以避免、code

2023-09-02 01:26:49 作者:看不惯丨你偏执希拉的傲慢

我有一个BusinessLayer项目,该项目具有以下code。域对象是FixedBankAccount(实现IBankAccount)。

这个仓库是由作为域对象的公共财产,是由作为接口成员。 如何重构它,以便存储库将不会是一个接口成员

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

注:库模式使用LINQ to SQL执行

修改

在下面的一个更好的方法给出的code? http://$c$creview.stackexchange.com/questions/13148/is-it-good-$c$c-to-satisfy-single-responsibility-principle

code

 公共接口IBankAccount
{
    RepositoryLayer.IRepository< RepositoryLayer.BankAccount> AccountRepository {获得;组; }
    INT BankAccountID {获得;组; }
    无效FreezeAccount();
}
 

 公共类FixedBankAccount:IBankAccount
{
    私人RepositoryLayer.IRepository< RepositoryLayer.BankAccount> accountRepository;
    公共RepositoryLayer.IRepository< RepositoryLayer.BankAccount> AccountRepository
    {
        得到
        {
            返回accountRepository;
        }
        组
        {
            accountRepository =价值;
        }
    }

    公众诠释BankAccountID {获得;组; }

    公共无效FreezeAccount()
    {
        ChangeAccountStatus();
    }

    私人无效SendEmail()
    {

    }

    私人无效ChangeAccountStatus()
    {
        RepositoryLayer.BankAccount bankAccEntity =新RepositoryLayer.BankAccount();
        bankAccEntity.BankAccountID = this.BankAccountID;

        accountRepository.UpdateChangesByAttach(bankAccEntity);
        bankAccEntity.Status =冻结;
        accountRepository.SubmitChanges();
    }
}
 
代码重构的艺术

 公共类BankAccountService
{
    RepositoryLayer.IRepository< RepositoryLayer.BankAccount> accountRepository;
    ApplicationServiceForBank.IBankAccountFactory bankFactory;

    公共BankAccountService(RepositoryLayer.IRepository< RepositoryLayer.BankAccount>回购,IBankAccountFactory bankFact)
    {
        accountRepository =回购;
        bankFactory = bankFact;
    }

    公共无效FreezeAllAccountsForUser(INT用户id)
    {
        IEnumerable的< RepositoryLayer.BankAccount> accountsForUser = accountRepository.FindAll(P => p.BankUser.UserID ==用户id);
        的foreach(RepositoryLayer.BankAccount repositroyAccount在accountsForUser)
        {
            DomainObjectsForBank.IBankAccount ACC = NULL;
            ACC = bankFactory.CreateAccount(repositroyAccount);
            如果(ACC!= NULL)
            {
                acc.BankAccountID = repositroyAccount.BankAccountID;
                acc.accountRepository = this.accountRepository;
                acc.FreezeAccount();
            }
        }
    }
}
 

 公共接口IBankAccountFactory
{
     DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositroyAccount);
}
 

 公共类MySimpleBankAccountFactory:IBankAccountFactory
{
    公共DomainObjectsForBank.IBankAccount CreateAccount(RepositoryLayer.BankAccount repositroyAccount)
    {
        DomainObjectsForBank.IBankAccount ACC = NULL;

        如果(String.Equals(repositroyAccount.AccountType,固定))
        {
            ACC =新DomainObjectsForBank.FixedBankAccount();
        }

        如果(String.Equals(repositroyAccount.AccountType,储蓄))
        {
            ACC =新DomainObjectsForBank.SavingsBankAccount();
        }

        返回ACC;
    }
}
 

阅读:

DDD - 实体状态转变

http://$c$creview.stackexchange.com/questions/13148/is-it-good-$c$c-to-satisfy-single-responsibility-principle

Using在"单一职责原则"迫使我的容器有公众制定者

http://programmers.stackexchange.com/questions/150760/single-responsibility-principle-how-can-i-avoid-$c$c-fragmentation

解决方案

重构这个code,使仓库不是一个接口成员是很容易做到。该库是一个依赖的执行,而不是接口 - 注入到你的具体类,并从IBankAccount删除它

 公共类FixedBankAccount:IBankAccount
{
    公共FixedBankAccount(RepositoryLayer.IRepository< RepositoryLayer.BankAccount> accountRepository)
    {
        this.accountRepository = accountRepository;
    }

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

    公众诠释BankAccountID {获得;组; }
    公共无效FreezeAccount()
    {
         ChangeAccountStatus();
    }

    私人无效SendEmail()
    {
    }

    私人无效ChangeAccountStatus()
    {
        RepositoryLayer.BankAccount bankAccEntity =新RepositoryLayer.BankAccount();
        bankAccEntity.BankAccountID = this.BankAccountID;

        accountRepository.UpdateChangesByAttach(bankAccEntity);
        bankAccEntity.Status =冻结;
        accountRepository.SubmitChanges();
    }

}
 

在关于第二个问题...

是,域对象是通过了解你的持久code违反SRP。这可能是也可能不是一个问题,但是,很多框架混合使用这些责任很大的影响 - 例如,活动记录模式。这确实让单元测试更有趣,因为它需要你嘲笑你的IRepository。

如果您选择了一个更持久无知的领域,你可能最好通过执行的工作模式的单位这样做。加载/编辑/删除实例获得注册工作的单位,负责在事务结束持续变化。工作单位是负责你的更改跟踪。

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

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

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

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?

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? http://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:

DDD - Entity state transition

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

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

http://programmers.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.