在此代码中找不到只是重构代码的错误 [已关闭]

Cant find a bug in this code that is just refactored code [closed]

提问人:Yusuf 提问时间:11/17/2023 更新时间:11/17/2023 访问量:55

问:


编辑问题以包括所需的行为、特定问题或错误以及重现问题所需的最短代码。这将帮助其他人回答这个问题。

6天前关闭。

我有一段 C# 代码,我打算重构并使其变得更好 - 遵守 SOLID、DRY、KISS 等标准。原始代码为:

public class CustomerService
    {
        public bool AddCustomer(string firname, string surname, string email, DateTime dateOfBirth, int companyId)
        {
            if (string.IsNullOrEmpty(firname) || string.IsNullOrEmpty(surname))
            {
                return false;
            }

            if (!email.Contains("@") && !email.Contains("."))
            {
                return false;
            }

            var now = DateTime.Now;
            int age = now.Year - dateOfBirth.Year;
            if (now.Month < dateOfBirth.Month || (now.Month == dateOfBirth.Month && now.Day < dateOfBirth.Day)) age--;

            if (age < 21)
            {
                return false;
            }

            var companyRepository = new CompanyRepository();
            var company = companyRepository.GetById(companyId);

            var customer = new Customer
                               {
                                   Company = company,
                                   DateOfBirth = dateOfBirth,
                                   EmailAddress = email,
                                   Firstname = firname,
                                   Surname = surname
                               };

            if (company.Name == "VeryImportantClient")
            {
                // Skip credit check
                customer.HasCreditLimit = false;
            }
            else if (company.Name == "ImportantClient")
            {
                // Do credit check and double credit limit
                customer.HasCreditLimit = true;
                using (var customerCreditService = new CustomerCreditServiceClient())
                {
                    var creditLimit = customerCreditService.GetCreditLimit(customer.Firstname, customer.Surname, customer.DateOfBirth).Result;
                    creditLimit = creditLimit*2;
                    customer.CreditLimit = creditLimit;
                }
            }
            else
            {
                // Do credit check
                customer.HasCreditLimit = true;
                using (var customerCreditService = new CustomerCreditServiceClient())
                {
                    var creditLimit = customerCreditService.GetCreditLimit(customer.Firstname, customer.Surname, customer.DateOfBirth).Result;
                    customer.CreditLimit = creditLimit;
                }
            }

            if (customer.HasCreditLimit && customer.CreditLimit < 500)
            {
                return false;
            }

            CustomerDataAccess.AddCustomer(customer);

            return true;
        }
    }
}

我将其重构如下:

    public bool AddCustomer(string firname, string surname, string email, DateTime dateOfBirth, int companyId)
    {
        if (!_customerValidator.ValidateCustomer(firname, surname, email, dateOfBirth))
        {
            return false;
        }

        var company = _companyRepository.GetById(companyId);
        var customer = _customerFactory.CreateCustomer(firname, surname, email, dateOfBirth, company);

        customer.HasCreditLimit = _creditLimitCalculator.AssessCreditLimit(company.Name);
        customer.CreditLimit = _creditLimitCalculator.RetrieveCreditLimit(customer);

        if (!_creditLimitValidator.HasCreditLimit(customer))
        {
            return false;
        }

        _customerDataAccessFactory.AddCustomer(customer);

        return true;
    }
}

我基本上希望类只调用方法以尽可能多地满足 SOLID 和 DRY。

当我提交此代码时,它是根据一系列未提供给我们的测试来判断的,我被告知的错误是:

There was a mistake that made the credit check work in reverse, which broke business logic. 

处理它的新代码的相关部分只是从原始代码中剥离出来,基本上在这里:

public class CreditLimitCalculator : ICreditLimitCalculator
{
    public bool AssessCreditLimit(string companyName)
    {
        if (companyName == Company.VeryImportantClient)
        {
            return true;
        }
        return false;
    }

    public int RetrieveCreditLimit(Customer customer)
    {
        int creditLimit;
        switch (customer.Company.Name)
        {
            case Company.VeryImportantClient:
                creditLimit = customer.CreditLimit;
                break;

            case Company.ImportantClient:
                using (var customerCreditService = new CustomerCreditServiceClient())
                {
                    var limit = customerCreditService.GetCreditLimit(customer.Firstname, customer.Surname, customer.DateOfBirth).Result;
                    limit *= 2;
                    creditLimit = limit;
                }

                break;

            default:
                using (var customerCreditService = new CustomerCreditServiceClient())
                {
                    var limit = customerCreditService.GetCreditLimit(customer.Firstname, customer.Surname, customer.DateOfBirth).Result;
                    creditLimit = limit;
                }
                break;
        }

        return creditLimit;
    }
}

和:

public bool HasCreditLimit(Customer customer)
{
    if (customer.HasCreditLimit && customer.CreditLimit < 500)
    {
        return false;
    }

    return true;
}

它基本上是完全相同的代码,只是移动到不同的类中,所以我不知道为什么它失败了。它也无法初始化,因此也无法调试和单步执行它。有什么建议吗?

C# 固体

评论

2赞 Andrew S 11/17/2023
恢复到旧代码,并为所有代码路径和边缘条件编写单元测试。更改回新代码,查看哪个单元测试失败。另外,什么......无法初始化,因此无法调试...意味 着?
1赞 Fildor 11/17/2023
呃......是否同步调用异步 API?.Result
2赞 Fildor 11/17/2023
为什么所有 ?只。。。if(condition) {return true;} return false;return condition;
2赞 Fildor 11/17/2023
哦,将包含“红旗”的代码称为“红旗”是轻描淡写的。它实际上是一面红旗,在按货运列车的喇叭和放烟花时燃烧......if (customer.Name == "yadda") ...

答:

0赞 FunkyLobster27 11/17/2023 #1

我认为访问客户的 CreditLimit 会触发测试工具中的行为,这被认为是不正确的,因为在公司非常重要的情况下,原始代码没有

在您的代码中,您可以设置信用额度,而不管公司的评估如何:

customer.CreditLimit = _creditLimitCalculator.RetrieveCreditLimit(customer);

您的 RetrieveCreditLimit(customer) 方法返回客户。非常重要公司的信用额度:

case Company.VeryImportantClient:
            creditLimit = customer.CreditLimit;
            break;

在这种情况下,简化为

customer.CreditLimit = customer.CreditLimit;

现在,我同意这看起来是良性的,但我们无法分辨是什么客户。CreditLimit 初始化为,或者 set/get 访问器是否执行比访问基础属性更复杂的操作。例如,如果他们在某处触发了某些操作 - 例如,监视设置/访问客户信用额度的尝试,那么它将围绕此代码向系统公开一组不同的调用,因为:

在原始代码中,在公司非常重要的情况下,不会尝试设置或获取客户。信用额度:

if (company.Name == "VeryImportantClient")
        {
            // Skip credit check
            customer.HasCreditLimit = false;
        }

即使在原始代码的末尾,也不会计算下面的第二句:

if (customer.HasCreditLimit && customer.CreditLimit < 500)
        {
            return false;
        }

所以我最初的猜测是,访问客户的 CreditLimit 会触发测试工具中的行为,这被认为是不正确的,因为在公司 VeryImportant 的情况下,原始代码没有

您是否有权访问 Customer 类源代码?它是否对访问 CreditLimit 做了什么特别的事情?

如果客户有 CreditLimit,您可以尝试仅设置 CreditLimit 吗?

if (customer.HasCreditLimit)
{
    customer.CreditLimit = _creditLimitCalculator.RetrieveCreditLimit(customer);
}