Skip to main content
added 917 characters in body
Source Link
Flater
  • 56.5k
  • 8
  • 107
  • 158

It's small method and it's easy to read but what about methods with hundred of lines which reads and assigns to self in many places?

Anything can be overkilled. Just because OOP has its uses does not mean that it cannot be abused or badly written.

  • OOP or not, methods with hundreds of lines are inherently a code quality issue.
  • While object state can be manipulated and that's not inherently a bad idea, having an object whose state is constantly being altered to the point of no longer being able to keep track of what its state is, is also a code quality issue.

But these are not arguments against using OOP as a blanket rule. That's like saying that no one should ever use a hammer because you've seen your dad hit his thumb with a hammer.
Mistakes happen but the existence of mistakes does not disprove the concept as a whole.

It's small method and it's easy to read but what about methods with hundred of lines which reads and assigns to self in many places?

Anything can be overkilled. Just because OOP has its uses does not mean that it cannot be abused or badly written.

  • OOP or not, methods with hundreds of lines are inherently a code quality issue.
  • While object state can be manipulated and that's not inherently a bad idea, having an object whose state is constantly being altered to the point of no longer being able to keep track of what its state is, is also a code quality issue.

But these are not arguments against using OOP as a blanket rule. That's like saying that no one should ever use a hammer because you've seen your dad hit his thumb with a hammer.
Mistakes happen but the existence of mistakes does not disprove the concept as a whole.

added 917 characters in body
Source Link
Flater
  • 56.5k
  • 8
  • 107
  • 158

The easiest way to think of it is that the state stored in an object (i.e. via self, usually done via the constructor) has been configured once and is reusably accessible by all of that class' methods. For example:

public class Clock
{
    public DateTime GetDateTime()
    {
        return DateTime.Now;
    }
}

public class SundayChecker
{
    private Clock clock;

    public SundayChecker(Clock clock)
    {
        this.clock = clock;
    }

    public bool IsItSunday()
    {
        var now = this.clock.GetDateTime();
        return now.DayOfWeek == DayOfWeek.Sunday;
    }
}

Notice how I only had to tell the SundayChecker which clock it should use once, but I am then able to repeatedly check the current time and confirm whether it's Sunday or not.

This is just a simple example, but it showcases the basic nature of OOP, this.

Note: there are many more arguments in favor of using object state, but this is the easiest one to grasp in order to shift your mind into an OOP-compatible frame.

This is much too broad for an in-depth explanation on OOP and how it should be used. I suggest you research OOP tutorials and exercises that teach you to use (and in turn know how to leverage) object-oriented code.

The easiest way to think of it is that the state stored in an object (i.e. via self, usually done via the constructor) has been configured once and is reusably accessible by all of that class' methods.

This is the basic nature of OOP, this is much too broad for an in-depth explanation. I suggest you research OOP tutorials and exercises that teach you to use (and in turn know how to leverage) object-oriented code.

The easiest way to think of it is that the state stored in an object (i.e. via self, usually done via the constructor) has been configured once and is reusably accessible by all of that class' methods. For example:

public class Clock
{
    public DateTime GetDateTime()
    {
        return DateTime.Now;
    }
}

public class SundayChecker
{
    private Clock clock;

    public SundayChecker(Clock clock)
    {
        this.clock = clock;
    }

    public bool IsItSunday()
    {
        var now = this.clock.GetDateTime();
        return now.DayOfWeek == DayOfWeek.Sunday;
    }
}

Notice how I only had to tell the SundayChecker which clock it should use once, but I am then able to repeatedly check the current time and confirm whether it's Sunday or not.

This is just a simple example, but it showcases the basic nature of OOP.

Note: there are many more arguments in favor of using object state, but this is the easiest one to grasp in order to shift your mind into an OOP-compatible frame.

This is much too broad for an in-depth explanation on OOP and how it should be used. I suggest you research OOP tutorials and exercises that teach you to use (and in turn know how to leverage) object-oriented code.

Source Link
Flater
  • 56.5k
  • 8
  • 107
  • 158

There's validity to some (if not most) of your observations, but the conclusions you draw from them are too extreme.

  1. It is tightly coupled to the concrete data source. (it reads current datetime from machine it runs on)

Correct. The date value should either be passed as a parameter or a clock-like dependency should be injected.

Note that dependency injection requires a non-static class and method. More on that later.

Take note of the latter suggestion (injecting a dependency). Your question argues against this idea, and that's where your observation goes off the rails. More on that later.

  1. It violates the Single Responsibility Principle (SRP).

I don't see how it does, and you didn't justify why you think it does either. This method does one thing. SRP does not focus on whether dependencies are injected, SRP focuses on the logic contained within the class. This class has one strictly defined purpose: generate a human-friendly label for the current time of day.

Just to be clear: the code can be improved, but SRP isn't what comes to mind as a violation here.

The argument that fetching the datetime value is a discrete responsibility is a strenuous argument. Any responsibility can be subdivided into smaller responsibilities - but there is a line drawn between what's reasonable and what's overkill. Assuming the method conveys that the current time of day is being evaluated, this is not an SRP violation.

  1. It lies about the information required to get its job done. Developers must read every line of the actual source code to understand what hidden inputs are used...

That's arguable. When I see GetTimeOfDay and it does not clearly take in a datetime value (either as method parameter or dependency), then the logical inference is that the current time is being used.
Even semantically, "getting the time of day" suggests that you are getting the current time, so I don't quite see an issue here with the naming.

...and where they come from. ...

This, I do agree on. You have no idea whether it's relying on the system clock, or a cloud-based API or ... This is solved when you inject it as a dependency or add it as a method parameter.

The method signature alone is not enough to understand the method’s behaviour.

Most OOP principles (SOLID among others) focus on classes, not methods. You should not observe methods by themselves, you should view them as operations on a class, and more specifically on a known instance of that class.

As far as code readability is concerned, you may assume that whoever calls a class method on an instance (object) of that class is also aware of how that object was constructed in the first place. That is not always the case, but when it is not the case this means that the caller has consented to delegate the object construction.

That is not your responsibility (you = the designer of the consumed class). You cannot and should not try to manage how your consumers delegate their own work internally.

When the source of the datetime value has been refactored to be an injected dependency or a method parameter, then the issue pointed out in your third bullet point is null and void.

So how to deal with self...?

"deal with" implies that it's a problem or unwanted item. Your discourse on self and the alleged issues with it carries an undertone of dislike for the concept of object-oriented state.

If that is how you feel, and you don't want to shift your way of thinking, that's okay too. Programming is an abstract concept of the mind, and different approaches exist to solve the same problem. In that case, you should consider moving to functional programming instead of object-oriented programming, for one major reason:

self is at the heart of OOP.

Objects track state. That is what they do. If they didn't, then your codebase exists only of methods, and then all of those methods could be made static.

self is the keyword that allows you to access the current object's state. Without self, you're effectively unable to actually store and retrieve object state, and thus we would revert back to a system where everything is just a collection of static methods.

Note: in your question, you've conveyed that you judge each method individually. That is actually in line with how you work with static methods, but it's not compatible with how you should think about object-oriented code.

... and how to use it properly?

This goes back to the part where I said that you need to observe things at a class level, not at a method level.

The easiest way to think of it is that the state stored in an object (i.e. via self, usually done via the constructor) has been configured once and is reusably accessible by all of that class' methods.

This is the basic nature of OOP, this is much too broad for an in-depth explanation. I suggest you research OOP tutorials and exercises that teach you to use (and in turn know how to leverage) object-oriented code.