0

This question is based on two premises:

There are some consequences to this such as: don't use public or protected properties. It is simple to understand why they should be avoided. There exist posts about this specific topic, too. Why is Clean Code suggesting avoiding protected variables?

Another consequence is that the this reference should not be given to methods from other classes: http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html#2 At least, it is often advised not to do so in constructors.

However, strictly speaking, wouldn't this prohibit almost every instance of passing this to any dependency's methods? After all, unless a class is not part of a class hierarchy, how can it avoid that an object holds invalid state when passing this?

An example in PHP:

class ClientCodeClass {
    public function carryOutTask(ParentClass $object) {
        // Client code now has access to the object in illegal state and things may mishappen because of that.
    }
}

class ParentClass {
    private $client_code_class;

    public function __construct(ClientCodeClass $client_code_class) {
        $this->client_code_class = $client_code_class;
    }

    protected function exposeParentClass() {
        // At this time, $this is in invalid state, and we're passing it to client code!
        $this->client_code_class->carryOutTask($this);
    }
}

class ChildClass extends ParentClass {
    public function doSomething() {
        $this->setIllegalState();
        $this->exposeParentClass();
        $this->setLegalState();
    }
}

$child_class = new ChildClass();
$child_class->doSomething();

So my question is: is it true that my two premises are correct and that they would be violated if a class (unless it does not extend another class and is marked final) passes this to a method of another object?

Follow up question: doesn't this also drastically restrict the allowed usage of callbacks? After all, when an object invokes the callback it can not guarantee that it is valid - while that callback could be working on that very object.

user2180613
  • 1,752
  • 3
  • 14
  • 17

1 Answers1

1

Your premises are correct. However, what can be considered as a 'valid' state is very subjective. It usually means that the assumptions that the writer of the class has about the structure of the contained data inside instances of that class are correct.

The only way you know that these premises are not invalidated by code at another location, is to ensure that all changes to your class' instances happen through a public API that your class provides. If you have such a public API, it means that any and all changes made to the data of your instances are validated by the class you've written. This means that (if the code in your class definition is bug-free) there is no way to invalidate the state of your instances.

This is exactly the reason why accessor methods (getters/setters) exist, and also why the separation of public/private methods exists.

TL;DR:

There is nothing wrong with passing this to other code, as long as the classes/methods you pass it to manipulate your class instance using its public API, as this means the class/instance itself has the final say about the changes that might happen to it.

Also, when you pass this to other code before your class internally has finished making its state valid, it indeed is very possible that other methods of the class will be called before building the valid state was finished. This is why using this inside the constructor is a bad idea.


When dealing with inheritance, it is up to the implementer of the child classes to not violate the assumptions made for instances of the parent classes. This indeed is something that can happen, but it is not the parent class' concern to ensure that its child classes are correct.


As for the follow-up question about callbacks: You should only register a callback to something after its state is valid. Otherwise you indeed introduce a race-condition where the callback might be triggered before the object is fully valid. This indeed restricts the locations where callbacks are allowed. Or, alternatively, what information a certain callback is allowed to assume to be valid on your instance.

Qqwy
  • 4,709
  • 4
  • 31
  • 45
  • "This indeed is something that can happen, but it is not the parent class' concern to ensure that its child classes are correct." Why not? Then who is responsible? The child class can't know of the internal workings of its parent, can it? So I suspect the child class can't know that `this` is passed to other code. Isn't such a situation a variation on the fragile base class problem? – user2180613 Jul 17 '16 at 10:20
  • It is the parent class' concern to provide a stable(non-fragile) public API/base, and _document_ properly what the requirements of inheriting classes are. It is the child class' concern to follow these requirements. Unfortunately there is no way in most languages to strictly enforce these requirements, as beside input/output typing, they often require (or forbid) certain things to be done inside the specification. The only languages that are able to enforce some stronger claims are dependently typed pure functional programming languages, which are, as of this time, still mostly academic. – Qqwy Jul 17 '16 at 10:59