简体   繁体   中英

OOP: Calling a public method within the same class

I was reading some article about collision avoidance systems in cars when my programmer mind led me to think of that concept in the object-oriented way, and it made me wonder if those systems respect the object-oriented programming model.

Being mainly a Java developer, I transposed this problem in a Java environment and it raised a particular question: does calling a public method within the same class (in a non-static context) respect and follow the object-oriented way?

I mean, take this brief hypothetical Car class:

public class Car {
    // Class attributes.

    // Constructors.

    public void accelerate(final double amplitude) {
        // Accelerate according to the amplitude.
    }

    public void brake(final double amplitude) {
        // Brake according to the amplitude.
    }

    // Other useful methods.

    private void collisionPreventionActions() {
        // Some actions.

        brake(100.0);

        // Some other actions.
    }
}

Suppose some Thread is responsible of detecting a collision and take actions when it does detect a collision, and one of those actions would be braking. Obviously the brake(...) method becomes an interesting choice, but doesn't that break the object-oriented way of doing things? It's not just the brakes though. What if the collision avoidance system in this class used the steering wheel instead to avoid the accident? I find it weird that the car would be using its own input from an internal point of view...

On a more general scope, suppose you have a generic object, which I like to see as a black box. The public methods would be the equivalent of levers on that black box that would control its behaviour. Calling a public method within this object would mean that the black box would activate its own levers from its internal mechanism.

I ask because I know it's legal in Java to do so, and that I've seen public methods being called within the same class numerous times in my life, but it being legal doesn't necessarily mean that it's the proper OO way of doing it.

Does using public methods within the same class in a non-static context follow the rules of object-oriented programming and encapsulation? If not, what would be the proper way of doing it or what could be the workaround?

There is nothing wrong with this choice from the OOP perspective: it is perfectly fine for a method to perform things that require combinations of other methods.

In practice, though, a common approach would be to separate the functionality into a public and a private portions, like this:

public void brake(final double amplitude) {
    // check preconditions
    if (speed == 0) throw new IllegalStateException("cannot brake when standing");
    if (amplitude <= 0) throw new IllegalArgumentException("amplitude must be positive");
    // ... do other important checks
    doBrake(amplitude);
}
private void doBrake(final double amplitude) {
    // The real code goes here
}

Now your collisionPreventionActions could call doBrake instead of brake , assuming that you have checked all the necessary preconditions before making the call.

Note: doBrake should check its preconditions as well. However, rather than throwing exceptions when preconditions are not met, it can use assertions. The difference is that exceptions indicate a misuse of your public methods by others, while assertions indicate misuse of your encapsulated methods by you or someone else maintaining your code.

No rules are violated when an object uses its own API. On the contrary, problems are likely to occur if a class has an API that can be overridden, but it fails to use that API internally.

As a trivial example, consider a non-final property accessor. An object could skip the accessor and read (or worse, write) fields directly. Suppose the accessor is overridden in a subclass to compute the property value using the field together with some other information from the subclass. Now the class is broken because it failed to honor its own contract.

Consider the (somewhat contrived) Point and OffsetPoint classes below. The derived class, OffsetPoint is written correctly, but it's inherited toString() method will not work as expected because the parent class, Point , wrongly fails to use its own accessors.

public class Point {

  private final int x, y;

  public Point(int x, int y) { this.x = x; this.y = y; }

  public int getX() { return x; }

  public int getY() { return y; }

  @Override
  public final String toString() { 
    /* Here's the bug; should be getX() and getY() instead of x and y */
    return String.format("(%d,%d)", x, y); 
  }

}

class OffsetPoint extends Point {

  private int dx, dy;

  OffsetPoint(Point point, int dx, int dy) { 
    super(point.getX(), point.getY());
    this.dx = dx; 
    this.dy = dy; 
  }

  @Override
  public int getX() { return super.getX() + dx; }

  @Override
  public int getY() { return super.getY() + dy; }

}

Does using public methods within the same class in a non-static context follow the rules of object-oriented programming and encapsulation?

No, there is no problem with encapsulation becuase the method is public so anyone (even this ) can call it.

However, for something like a collision avoidance system, relying on public methods could be bad security wise.

Let's use your example of this intenal Collision detector calling the public method brake() . What if someone subclassed car and overrode the method?

public class BrokenCar extends Car{

   @Override
   public void brake(final double amplitude) {
      //BREAKS CUT!!!
   }

}

So there are some security rules of not relying on overridable methods. Making brake and accelerate final methods resolves this problem.

Yes, I think it is proper in an OO context to call your own public method. It's quite common when there are overloads for a method that all but one call the most specific one, either filling in defaults for missing parameters or transforming the type of the argument(s). I also see the pattern where all the overloads call into a private or protected method of the same name with _internal or _impl added to the end. For example, several overloads of ComputeSpeed might all call ComputerSpeed_internal. This pattern would be appropriate if there is parameter validation in the public methods that you don't want to do twice, or would be inappropriate for internal calls.

You can certainly introduce problems by not having a clear separation of concerns. For example, if the caller of collisionPreventionActions also decided it was a good idea to set the brake, you could have a conflict in how much brake is applied.

KC

In general, it is fine to call the public methods. The thing to consider is what the interface of the Car should be. In this case, does preventCollision() belong in the Car class or in some other CollisionPrevention class.

Breaking your code multiple classes each with a single responsibility , and then using them a bigger class like Car , is generally a good idea.

I totally agree with you that it's common that a class itself addresses its private members and methods. But I don't understand why it shouldn't be legal in terms of the Object Oriented Paradigm. Consider the following example:

public class Human {

    public Human() {
        liveYourLife();
    }

    private void liveYourLife() {
        while(alive){
            createYourDay();
        }
    }

    private void createYourDay() {
        drink();
        eat();
        sleep();
        awake();
        drink();
    }

    private void eat() {}

    private void drink() {}

    private void sleep() {}

    private void awake() {}
}

Probably someone will criticise the simple rule of life, shown in the example above. But what I want to demonstrate with the few lines above is, that "normally" a human is allowed to decide on his daily routine.

The basic principle of the OO-Paradigm is to describe the actions and properties of real world entities. Hence, as long you are allowed to yourself decide on when you want to eat, drink, sleep, etc. your above described model is absolutely correct. But if you discover some exceptional cases in your problem domain which you want to address in your software (eg you got arrested, etc. ) you should update your OO-design.

In case that there is an something, which heavily influences the state of another instance, you should treat that "instance of disturbance" as a different object which has a reference to the actual instance.

public class Prisoner extends Human {

    @Override
    private void liveYourLife() {
        while(jailed){
            createYourDay();
        }
    }

    @Override
    private void createYourDay() {
        // A bit different :)
    }
}

public class Prison {

    private List<Prisoner> prisoners;

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM