如何通过删除if检查使代码面向对象?

时间:2019-05-28 12:59:33

标签: c# .net

我有一个windows service,它检查日期并向用户发送通知余款,以供用户订购了服务的款项。

它是每月付款服务系统,用户必须在月末付款,并且该系统向用户发送2笔余款通知:

1)如果未付款,则在截止日期的N天前。

2)如果未收到付款,则在截止日期之前发送剩余款项。

因此,以下是根据发送给用户和管理员的通知的条件: enter image description here

代码:

public enum PaymentStatusEnum
 {
    Ask_For_Payment = 0,
    Payment_Remainder_Sent = 1,
    Full_Payment_Done = 2,
    Payment_Not_Done = 3,
 }

public class ServicePaymentModel
{   
    public int PaymentId { get; set; }
    public string Email { get; set; }
    public int PaymentStatus { get; set; }
    public string AdminId { get; set; }
    public int NoOfDaysPassed { get; set; }
    public decimal DueAmount { get; set; }
    public decimal PaymentMade { get; set; }
}

 public void SendNotification()
 {
    int daysBeforeDeadline = 10;
    int deadlineDays = 20;
    var paymentModel = new PaymentModel
    {
        Today = 10/5/2019,
        DaysBeforeDeadline = daysBeforeDeadline,
        DeadlineDays = deadlineDays
    };
    //Get all the payments whose daysBeforeDeadline or deadlineDays condition is met.
    //For eg: If some users subscription started from 1/5/2019 and Todays date is 10/5/2019 then this users will be will be fetched because of daysBeforeDeadline.
    //For eg: If some users subscription started from 20/4/2019 and Todays date is 10/5/2019 then this users will be will be fetched because deadlineDays condition
    List<ServicePaymentModel> payments = MyRepo.GetPayments(paymentModel);
    if (payments != null && payments.Count == 0)
        return;
    UserPayment userPayment = null;
    foreach (var payment in payments)
    {
      try
      {
        if (payment.DueAmount > 0) //Payment not done
        {
            if (paymentModel.DeadlineDays == payment.NoOfDaysPassed
                            && payment.PaymentStatus == (int)PaymentStatusEnum.Payment_Remainder_Sent) // payment not made on deadline
            {
                userPayment = new UserPayment
                {
                    PaymentId = payment.Id,
                    PaymentStatus = (int)PaymentStatusEnum.Payment_Not_Done
                }
                SendNotificationToUser(payment);//method handles email sending and different email template for user
                SendNotificationToAdmin(payment)//method handles email sending and different email template for Admin telling him about which user payment has not been received
            }
            else if (paymentModel.DaysBeforeDeadline == payment.NoOfDaysPassed
                            && payment.PaymentStatus == (int)PaymentStatusEnum.Ask_For_Payment)//payment not done after N days
            {
                userPayment = new UserPayment
                {
                    PaymentId = payment.Id,
                    PaymentStatus = (int)PaymentStatusEnum.Payment_Remainder_Sent
                }
                SendNotificationToUser(payment);//method handles email sending and different email template for user
                SendNotificationToAdmin(payment)//method handles email sending and different email template for Admin telling him about which user payment has not been received
            }
        }
        else if (payment.DueAmount == 0) // payment done
        {   
            userPayment = new UserPayment
            {
              PaymentId = payment.Id,
              PaymentStatus = (int)PaymentStatusEnum.Full_Payment_Done
            }
            if ((paymentModel.DeadlineDays == payment.NoOfDaysPassed
                           && payment.PaymentStatus == (int)PaymentStatusEnum.Ask_For_Payment)// payment made on deadline
            {

                SendNotificationToUser(payment);//method handles email sending and different email template for user along with message and body
                SendNotificationToAdmin(payment)//method handles email sending and different email template for admin along with message and body
            }
            else if (paymentModel.DaysBeforeDeadline == payment.NoOfDaysPassed
                           && payment.PaymentStatus == (int)PaymentStatusEnum.Ask_For_Payment)//payment done before XX days
            {
               SendNotificationToAdmin(payment)//method handles email sending and different email template for admin along with message and body
            }
        }
        PaymentRepo.UpdateUserPaymentStatus(userPayment);
     }
     catch (Exception ex)
     {
        //do nothing and continue processing other payment             
     }
    }
  }

我已经看到了这个Plural sight video,其中作者-Zoran Horvat 说,我们可以将几乎所有的If检查都转向面向对象的解决方案,并且正如您所见,我的代码包含很多if检查而明天,如果添加的Conditions数量超过此If数量,将会大大增加维护的夜色。

我的所有Conditions and PaymentStatus都基于If检查进行处理,但是在这里,我没有得到如何将条件条件转换为面向对象的解决方案,以及在这种情况下是否真的可能。

那么是否有可能通过删除if检查或其他更好的方法来使此代码面向对象?


public void SendNotificationRefactor2()
{
    int daysBeforeDeadline = 10;
    int deadlineDays = 20;
    var paymentModel = new PaymentModel
    {
        Today = 10 / 5 / 2019,
        DaysBeforeDeadline = daysBeforeDeadline,
        DeadlineDays = deadlineDays
    };
    //Get all the payments whose daysBeforeDeadline or deadlineDays condition is met.
    //For eg: If some users subscription started from 1/5/2019 and Todays date is 10/5/2019 then this users will be will be fetched because of daysBeforeDeadline.
    //For eg: If some users subscription started from 20/4/2019 and Todays date is 10/5/2019 then this users will be will be fetched because deadlineDays condition
    List<ServicePaymentModel> payments = MyRepo.GetPayments(paymentModel);
    if (payments != null && payments.Count == 0)
        return;
    //UserPayment userPayment = null;

    foreach (var payment in payments)
    {
        try
        {
            //  Breaking this out into a method is optional, really, because there's little chance it'll 
            HandlePayment(paymentModel, payment);
        }
        catch (Exception ex)
        {
            //  SWALLOWING EXCEPTIONS IS AN INDESCRIBABLY BAD IDEA. DON'T DO THIS. 
        }
    }
}

protected void HandlePayment(PaymentModel paymentModel, ServicePaymentModel payment)
{
    var userPayment = new UserPayment
    {
        PaymentId = payment.Id
    };

    if (payment.DueAmount > 0) //Payment not done
    {
        if (paymentModel.DeadlineDays == payment.NoOfDaysPassed)
        {
            if (payment.PaymentStatus == (int)PaymentStatusEnum.Payment_Remainder_Sent)
            {
                userPayment.PaymentStatus = (int)PaymentStatusEnum.Payment_Not_Done;
            }
            else if (payment.PaymentStatus == (int)PaymentStatusEnum.Ask_For_Payment)//payment not done after N days
            {
                userPayment.PaymentStatus = (int)PaymentStatusEnum.Payment_Remainder_Sent;
            }

            SendNotificationToUser(payment);//method handles email sending and different email template for user
            SendNotificationToAdmin(payment);//method handles email sending and different email template for Admin telling him about which user payment has not been received
        }
    }
    else if (payment.DueAmount == 0) // payment done
    {
        userPayment.PaymentStatus = (int)PaymentStatusEnum.Full_Payment_Done;

        if (paymentModel.DeadlineDays == payment.NoOfDaysPassed)
        {
            if (payment.PaymentStatus == (int)PaymentStatusEnum.Ask_For_Payment)
            {
                SendNotificationToUser(payment);//method handles email sending and different email template for user along with message and body
                SendNotificationToAdmin(payment);//method handles email sending and different email template for admin along with message and body
            }
            else if (payment.PaymentStatus == (int)PaymentStatusEnum.Ask_For_Payment)
            {
                SendNotificationToAdmin(payment);//method handles email sending and different email template for admin along with message and body
            }
        }
    }

    PaymentRepo.UpdateUserPaymentStatus(userPayment);
}

1 个答案:

答案 0 :(得分:6)

首先,要专注于目标。把更多的东西做成OO会使它更好的信念是一种信念结构,我称之为“对象幸福障碍”。请记住,OO代码的目的是通过非常清楚一段代码提供什么服务,它的公共接口是什么,以及降低大型团队工作的大型程序的成本。它如何与其他组件交互。这不是使小型代码更好的通用技术。

您的目标不应是使程序“更多面向对象”;它应该是为了降低成本,所以问自己“与该计划相关的成本是多少?”您在哪里花钱,还记得您的薪水可能是大部分钱

例如:

  

当业务流程更改时,我们花费太多时间来更新代码。

如果这是问题,那么我将使该程序更具面向对象,而不是通过“用多态替换条件”。仅仅因为它是多态的就不能使其成为OO。使其成为OO的原因是我们已经确定了业务领域中的基本概念,并将这些概念封装到仅在业务流程发生变化时才需要更改的对象中

要看的关键是您的非常有用的图表,显示:

  • 什么外生条件触发状态变化?
  • 状态变化是什么?
  • 什么动作与状态改变有关?

因此,将其编成代码。创建一个基类EventTrigger。您已经有一个表示状态的类型。制作一个名为EventAction的课程。制作课程Condition。现在我们的流程是什么?

for each trigger in eventTriggers
    if any trigger condition is met
        execute all trigger actions

现在,您可以根据需要使用一个if语句。现在,您可以为每个条件编写一个类,为每个动作编写一个类,并将触发器绑定在一起。

如果要更改与特定触发器关联的操作,请在一个位置而不是大量的意大利面条代码中进行更改。

此外,此技术还可以进行许多其他改进。您可以轻松添加日志记录;记录只是另一项操作。您可以组合动作;做出一个同时执行两个动作的动作。依此类推。

您甚至可以制作如下配置文件:

TRIGGER PaymentReceivedTrigger HAS CONDITION AskForPayment WITH ACTIONS  SetFullPayment, EmailAdmin
…

现在,您可以基于配置文件来设置整个系统,而无需编写C#代码。

但是如果不是 问题怎么办?如果问题是什么呢?

  

我们花太多时间来查找错误

  

我们的表现很差,我们也不知道为什么

  

我们完全与一个数据库供应商联系在一起,但是它们太昂贵了;我们如何降低切换后端的成本?

还是其他一百万种东西?在这些情况下,您不想浪费任何时间来构建OO业务流程引擎;您想专注于实际上使您付出金钱的问题,并找出降低成本的方法。