如何通过删除if检查使代码面向对象? [英] How to make code object oriented by removing if checks?

查看:41
本文介绍了如何通过删除if检查使代码面向对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a windows service which checks for date and send notification remainder to user for a payment for which user has subcribed for a service.

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

Its a monthly payment service system for which user has to make a payment at the end of the month and for that system sends 2 remainder notification to user :

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

1) Before N days from deadline if payment not made.

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

2) Send remainder on deadline if payment not received.

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

So below are the conditions based on which notifications are send to User as well as Administrator :

代码:

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             
     }
    }
  }

我已经看过了多视点视频作者-Zoran Horvat 说的是,我们几乎可以将所有If检查变成面向对象的解决方案,并且您可以看到我的代码包含很多if检查,明天是否还有更多的 Conditions 会比这增加更多,如果它将大大增加维护性的夜马.

I have seen this Plural sight video where Author - Zoran Horvat is saying that we can turn almost any If checks to object oriented solution and as you can see my code contains alot of if checks and tomorrow if more Conditions are added than this If will grow tremendously creating maintainance night mare.

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

All my Conditions and PaymentStatus are being handled based on If checks but here I am not getting how to turn this if conditions in to object oriented solution and whether it would be really possible or not in this case.

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

So is it possible to make this code object oriented by removing if checks or any better way to handle this checks?

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);
}

推荐答案

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

First off: focus on the goal. The belief that making something more OO makes it better is a belief structure I call "object happiness disorder". Remember, the purpose of OO code is to lower the costs of large programs worked on by large teams, by making it very clear what service is provided by a piece of code, what its public interface is, and how it interacts with other components. It's not a general-purpose technique for making small code better.

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

Your goal should not be to make the program "more OO"; it should be to make it lower cost, so ask yourself "what are the costs associated with this program?" Where are you spending money, remembering that your salary is probably most of that money?

例如:

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

We're spending too much time updating the code when business processes change.

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

If that's the problem then I would make the program more OO, but not by "replace condition with polymorphism". Just because it is polymorphic does not make it OO. What makes it OO is we have identified fundamental concepts in the business domain and encapsulated those concepts into objects that only have to change when the business process changes.

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

The key thing to look at is your extremely helpful diagram that shows:

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

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

So, codify that. Make a base class EventTrigger. You've already got a type representing states. Make a class called EventAction. Make a class Condition. And now what is our process?

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

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

Now we're down to a single if statement, like you wanted. Now you can write one class for each condition, and one class for each action, and triggers tie them together.

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

If you want to change the action associated with a particular trigger, you change it in one place, not in a mass of spaghetti code.

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

Also, this technique is amenable to many other improvements. You can easily add logging; logging is just another action. You can make compositions of actions; make an action that takes two actions and runs them both. And so on.

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

You could even make a configuration document that looks like:

TRIGGER PaymentReceivedTrigger HAS CONDITION AskForPayment WITH ACTIONS  SetFullPayment, EmailAdmin
…

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

And now you can set up your whole system based on a config file instead of writing C# code.

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

But what if that is not the problem? What if the problem is:

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

We're spending too much time tracking down bugs

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

Our performance is bad and we do not know why

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

We're completely tied to one database vendor but they are too expensive; how can we lower the cost of switching back ends?

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

Or any of a million other things? In those cases, you don't want to waste any time building an OO business process engine; you want to concentrate on the problem that is actually costing you money, and figure out what lowers that cost.

这篇关于如何通过删除if检查使代码面向对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆