如何使用依赖项注入实现可变对象重构

本文关键字:对象 重构 实现 注入 何使用 依赖 | 更新日期: 2023-09-27 18:27:45

存在一个"审计;对象,该对象在我试图重构的整个代码库中使用,以允许依赖项注入,并最终进行更好的单元测试。到目前为止,我为类创建接口并通过构造函数注入这些接口没有遇到任何问题。然而,这一类是不同的。我知道它为什么/如何不同,但我不确定如何将其修复为工作";适当地";。

下面是一个例子(简化版本,但问题仍然存在,即使在这个例子中也是如此):

namespace ConsoleApplication1.test.DI.Original
{
    public class MultiUseDependencies
    {
        public MultiUseDependencies()
        {
        }
        public void Update()
        {
            Audit a = new Audit();
            a.preAuditValues = "Update";
            // if data already exists, delete it
            this.Delete();
            // Update values, implementation not important
            // Audit changes to the data
            a.AuditInformation();
        }
        public void Delete()
        {
            Audit a = new Audit();
            a.preAuditValues = "Delete";
            // Delete data, implementation omitted.
            a.AuditInformation();
        }
    }
    public class Audit
    {
        public string preAuditValues { get; set; }
        public void AuditInformation()
        {
            Console.WriteLine("Audited {0}", preAuditValues);
        }
    }
}

在上文中,Update函数(实现方式未示出)获得"0";改变前";版本的数据,删除数据(并审核它),插入/更新对数据的更改,然后审核插入/更新。

如果我要从控制台应用程序运行:

Console.WriteLine("'n");
test.DI.Original.MultiUseDependencies mud = new test.DI.Original.MultiUseDependencies();
mud.Update();

我会得到:

已审核删除

经审核的更新

这是预期的行为。现在,在类的实现方式中,我已经可以看到会有问题,但我不确定如何纠正它

namespace ConsoleApplication1.test.DI.Refactored
{
    public class MultiUseDependencies
    {
        private readonly IAudit _audit;
        public MultiUseDependencies(IAudit audit)
        {
            _audit = audit;
        }
        public void Update()
        {
            _audit.preAuditValues = "Update";
            // if data already exists, delete it
            this.Delete();
            // Update values, implementation not important
            // Audit changes to the data
            _audit.AuditInformation();
        }
        public void Delete()
        {
            _audit.preAuditValues = "Delete";
            // Delete data, implementation omitted.
            _audit.AuditInformation();
        }
    }
    public interface IAudit
    {
        string preAuditValues { get; set; }
        void AuditInformation();
    }
    public class Audit : IAudit
    {
        public string preAuditValues { get; set; }
        public void AuditInformation()
        {
            Console.WriteLine("Audited {0}", preAuditValues);
        }
    }
}

运行:

Console.WriteLine("'n");
test.DI.Refactored.MultiUseDependencies mudRefactored = new test.DI.Refactored.MultiUseDependencies(new test.DI.Refactored.Audit());
mudRefactored.Update();

我得到(如预期,但不正确):

已审核删除

已审核删除

以上内容是基于实现的预期内容,但根据原始行为是不正确的。我不知道该怎么办。最初的实现依赖于不同的Audit来正确地跟踪变化。当我在重构中传递IAudit的实现时,我只得到Audit的一个实例,其中两个实例相互碰撞。

基本上在重构之前,Audit的作用域在函数级别。重构之后,Audit的作用域在类上。

有没有一种简单的方法来纠正这个问题?

这是一把小提琴:https://dotnetfiddle.net/YbpTm4

如何使用依赖项注入实现可变对象重构

问题出在您的设计中。Audit是一个可更改的对象,它使它成为运行时数据。将运行时数据注入组件的构造函数是一种反模式。

解决方案是更改设计,例如,通过定义如下IAudit抽象:

public interface IAuditHandler {
    void AuditInformation(string preAuditValues);
}

对于这个抽象,您可以创建以下实现:

public class AuditHandler : IAuditHandler {
    public void AuditInformation(string preAuditValues) {
        var audit = new Audit();
        audit.preAuditValues = preAuditValues;
        audit.AuditInformation();
    }
}

消费者现在可以依赖IAuditHandler:

public class MultiUseDependencies
{
    private readonly IAuditHandler _auditHandler;
    public MultiUseDependencies(IAuditHandler auditHandler) {
        _auditHandler = auditHandler;
    }
    public void Update() {
        this.Delete();
        _auditHandler.AuditInformation("Update");
    }
    public void Delete() {
        // Delete data, implementation omitted.
        _auditHandler.AuditInformation("Delete");
    }
}

但我甚至应该更进一步,因为用您当前的方法,您正在用交叉的问题污染业务代码。审计跟踪的代码分布在整个代码库中并重复。

然而,这将是应用程序设计中的一个相当大的变化,但可能非常有益。你绝对应该阅读这篇文章,了解如何通过这种方式改进你的设计。

试试这个:

public void Update()
{
    // if data already exists, delete it
    this.Delete();
    //preAuditValues should be changed after the delete or it will keep 
    //the old value
    _audit.preAuditValues = "Update";
    // Update values, implementation not important
    // Audit changes to the data
    _audit.AuditInformation();
}

或者这也应该起作用:

public void Delete()
{
    string oldValue = _audit.preAuditValues;
    _audit.preAuditValues = "Delete";
    // Delete data, implementation omitted.
    _audit.AuditInformation();
    //Restoring oldValue after finished with Delete
    _audit.preAuditValues = oldValue ;
}