What is an object

TL;DR

Hi all!

Recently worked on a problem. It was necessary to get some objects from the network via REST and process them.

Well, everything seems to be nothing complicated. Uploaded, parsed, returned. OK. Then it was necessary to process the resulting array. In short, according to special logic, sum some fields – it could be a string, a number, or null. I started doing it as usual: I created the sum variable, started the for loop, and began to work on the main logic in it. Finished.

Continued coding. Hoba! The same logic. I didn’t copy-paste, I put it in a separate function. Everything is OK as well.

Started doing task 3. Merge the results of multiple calculations. Again, the cycle began to touch. But then an idea came up:

“А что, если создать для этого отдельный объект?”

No, nonsense! After all, there is no Unifier of Some Result in the real world. But what if you do? Let’s try.

What!!!?? Why did everything suddenly become so simple? I passed the necessary objects to the constructor and made methods that applied their logic to the objects contained in them. Just a few lines! Why didn’t I do this before?

I got angry. I began to see objects everywhere. This is very convenient: you do not need to look at each piece of code with the thought “has it been somewhere before?”. How easy it is to test!

Then it dawned on me what was wrong with me:

I did not distinguish between real world objects and objects in the understanding of OOP.

OOP objects != Real world objects

Probably my main mistake was the lack of practice: I was interested in a lot, read, watched, but my hands did not reach coding. Therefore, by the time of that event, there were only 3 patterns in my head for using objects:

  • DTO

  • Objects from the real world

  • Objects that implement some kind of interface (usually for network requests, for use in container DI)

Looking back, I realized that all roads led to exactly this way of thinking:

  • At the university, we were taught OOP according to some models like: “Here is the object Man. It has the attributes Name and Age”, and when it came to programming, no one looked at how we write code. It turned out to be a mess of imperative programming and object sketches.

  • In all educational resources (videos, books, courses) they give too simple examples. The examples are too straightforward (as in the university listed above). Do not let you feel the power of objects.

  • If there were tasks, they were too simple. Not the level of complexity to really think about something (for example, a boring calculator). They didn’t show that objects could solve many problems.

The program is full of such implicit objects – service objects: count, filter, aggregate. I never thought about the fact that almost any for can be (probably even better) replaced with an object that encapsulates the necessary logic.

Perhaps the only thing that limited me was the idefix that objects should represent concepts of the real world. Who even told me this?

Diagrams get in the way of understanding OOP

But what about popular design tools? notations. Probably everyone has seen various UML diagrams. The class diagram must have been seen by every programmer at least once.

Taken from https://medium.com/@uferesamuel/uml-class-diagrams-the-simple-approach-eee2d1ffc125
Taken from https://medium.com/@uferesamuel/uml-class-diagrams-the-simple-approach-eee2d1ffc125

ER diagrams are also good – they are too much tied to the real world. There, almost everything represents objects of the real world.

Taken from https://online.visual-paradigm.com/diagrams/templates/chen-entity-relationship-diagram/see-doctor-erd-chen-notation/
Taken from https://online.visual-paradigm.com/diagrams/templates/chen-entity-relationship-diagram/see-doctor-erd-chen-notation/

On reflection, I realized 3 things:

  1. ER diagram has nothing to do with OOP – it’s business analysis tool. I am not required to create the same classes as in this diagram. Who told me this?

  2. UML shows high-level program structure: who is in it and what they should do/have. Those. what to do, not how to do it. The implementation falls on the shoulders of the programmer (spoiler, these will be methods for 100+ lines of loops, conditions and other delights)

  3. Many notations are oriented for easy understanding of concepts programs – what components it consists of. Nothing prevents us from passing object arrays instead of classes. No need to focus on them as the truth in the first instance.

As a result, we end up with a lot of objects. Hooray, OOP! What’s inside? Huge cycles for dozens of lines, a lot of flags and if’ov – complete imperative.

What am I talking about?

What did I understand? For example,

public interface IWorkingScheduleService
{
    // Возвращает тип дня: рабочий, предпраздничный, праздничный, выходной
    int GetDayType(DateOnly date);
}
// Количество рабочих часов на каждый день недели
public class UserSchedule
{
    public float Monday { get; set; }
    public float Tuesday { get; set; }
    public float Wednesday { get; set; }
    public float Thursday { get; set; }
    public float Friday { get; set; }
    public float Saturday { get; set; }
    public float Sunday { get; set; }
}

The task is to calculate the total working hours.
Banal, right? Let’s make functions:

public static class ScheduleHelpers
{
    public static float GetTotalWorkingHours(IWorkingScheduleService service,
                                             UserSchedule schedule,
                                             DateOnly from,
                                             DateOnly to)
    
    {
        // Какая-то логика
        return 0;
    }

    public static float GetTotalWorkingHoursWithoutPreholiday(IWorkingScheduleService service,
                                                              UserSchedule schedule,
                                                              DateOnly from,
                                                              DateOnly to)
    
    {
        // Какая-то логика
        return 0;
    }

    public static float GetTotalHolidayWorkingHours(IWorkingScheduleService service,
                                                    UserSchedule schedule,
                                                    DateOnly from,
                                                    DateOnly to)
    
    {
        // Какая-то логика
        return 0;
    }
}

But here we notice a common initial part: IWorkingScheduleService service, UserSchedule schedule. Why don’t we move this logic into a separate object?

public class WorkingScheduleCalculator
{
    private readonly IWorkingScheduleService _service;
    private readonly UserSchedule _schedule;

    public WorkingScheduleCalculator(IWorkingScheduleService service, 
                                     UserSchedule schedule)
    
    {
        _service = service;
        _schedule = schedule;
    }
    
    public float GetTotalWorkingHours(DateOnly from, 
                                      DateOnly to)
    
    {
        // Какая-то логика
        return 0;
    }

    public float GetTotalWorkingHoursWithoutPreholiday(DateOnly from, 
                                                       DateOnly to)
    
    {
        // Какая-то логика
        return 0;
    }

    public float GetTotalHolidayWorkingHours(DateOnly from, 
                                             DateOnly to)
    
    {
        // Какая-то логика
        return 0;
    }
}

How convenient it has become! Everything is nearby, signatures have become shorter and support for auto-completion as a gift is lovely!

conclusions

What did I take away from all this?

  1. An object is not a real world concept. You can make an object that has a name, attributes, behavior, like a real world object, make it as similar as possible, but this is NOT a REAL WORLD OBJECT. We need to stop thinking like this!

Объект - это (всего лишь) данные и функции, ассоциированные с ними

  1. I look at each block with logic (a cycle, a sequence of conditions, etc.) with the thought: “Can’t this be moved into a separate object?”

  1. In the same way, I look at functions that take the same arguments. They can all be combined into objects whose attributes are these common arguments.

PS I’m not a radical, but for the meaningful and pragmatic use of objects: for trivial logic, you can leave cycles, I allow)

Similar Posts

Leave a Reply