Passing object as parameter changes the original object

Asked

Viewed 96 times

2

I’m passing an object as a parameter to another function. In my years of study in college it has always been differentiated the passage by reference and value, in the case of the object it always works as passage by reference?

Follow the situations

This is my model used to perform the tests

class Teste
{
    private $atributo;

    public function getAtributo(){
        return $this->atributo;
    }

    public function  setAtributo($atributo){
        $this->atributo = $atributo;
    }

    public function incrementar($teste){
        $teste->atributo += 1;
    }
}

SITUATION 1

In the first situation I made an instance of the object Teste, initialized the attribute to zero and sent it to increment function

    $teste = new Teste();
    $teste->atributo = 0;
    for($i = 0; $i < 3; $i++){
        $teste->incrementar($teste);
    }

obtaining the following result

$i = 0 => $teste->atributo = 1
$i = 1 => $teste->atributo = 2
$i = 2 => $teste->atributo = 3

SITUATION 2

In the second situation I initialized the attribute with 0 and sent the own model as a parameter

    $this->atributo = 0;
    for($i = 0; $i < 3; $i++){
        $teste->incrementar($this);
    }

and I got the following result

$i = 0 => $teste->atributo = 1
$i = 1 => $teste->atributo = 2
$i = 2 => $teste->atributo = 3

SITUATION 3

In the third situation I initialized the attribute with 0 and sent a Model clone as parameter

    $this->atributo = 0;
    for($i = 0; $i < 3; $i++){
        $teste->incrementar(clone $this);
    }

and I got the following result

$i = 0 => $teste->atributo = 0
$i = 1 => $teste->atributo = 0
$i = 2 => $teste->atributo = 0

Now the question is: Would that be standard object-oriented behavior? There is a correct way to send these objects without changing the attributes of the original object?

The methods in the example above are in the same class to facilitate for me the exemplification. In the actual occurrence the calls are in different classes and the same behavior occurs.

  • Would it be any problem if I change the name of the class and instance in the explanation?

  • 1

    No, it wouldn’t be @Augustovasques

  • Any reason to do this? This class doesn’t make any sense, as it is conceptually wrong, nothing matters. According to your answer I can try to answer the question, including because I have an "opinion" different from the answer already posted. Could put all the code used to test?

  • The reason would be only for understanding the process, only. The class created only to exemplify the situation, for understanding (apologies to everyone for the names used). I should have created 2 classes to better exemplify the real occurred, I will try to expose the doubt better.

  • 1

    @Maniero, The original question did not have the tag php. The original tags were orientação-a-objetos and engenharia de software. The editor saw fit to put php and removed it from the scope. Pseudo-php was the vehicle the user agreed as suitable to convey his doubt as if it were an algorithm.

  • 1

    @Augustovasques I saw, actually just now I saw that they changed :) My complementary response, for the most part, was not even PHP (just an excerpt I mention this)

Show 1 more comment

2 answers

3


Yes this parameter passage you are doing is yes a parameter passage by reference and the obtained results respect the paradigms of the POO.

As for your questions:

This would be a standard behavior of object orientation?

Yes this is the behavior is standard for POO. What hurt the understanding, in my view, was the choice of class names and instances.

There is a correct way to send these objects without changing the attributes of the original object?

Yes there is a correct way to send these objects without changing the attributes of the object. What happened in the case was that the language executed exactly what you ordered it to do.


Reinterpretation of the example:

To facilitate understanding I will make some modifications in the examples you gave.

//   Mudei o nome da classe Teste para classe Exemplo pois a repetição da  
//palavra teste em diferentes contextos estava confusa.
    class Exemplo 
    {
        private $atributo;

        public function getAtributo(){
            return $this->$atributo;
        }

        public function  setAtributo($valor){
            $this->$atributo = $valor;
        }

        //   Aqui fiz outra modificação troquei o nome do parâmetro de $teste
        //para $alvo. Só para simplificar.
        public function incrementar($alvo){
            $alvo->atributo += 1; 
        }
    }

The first example I divided into four situations, which I enumerated from 1.1 to 1.4, and instead of an instance of the example class I used two instances.

SITUATION 1.1

I create two instances of Exemplo, $objeto1 and $objeto2. I begin the attribute of $objeto1 with 0 and I begin the attribute of $objeto2 with 10. I used these values to make the results different.

In this example I do $objeto1 increment the attribute of $objeto2:

$objeto1 = new Exemplo();
$objeto2 = new Exemplo();
    $objeto1 ->atributo = 0;
    $objeto2 ->atributo = 10;
    for($i = 0; $i < 3; $i++){
        $objeto1 ->incrementar($objeto2);
    }

Results:

$i = 0 => $objeto1 ->atributo = 0 ; $objeto2 ->atributo = 11
$i = 1 => $objeto1 ->atributo = 0 ; $objeto2 ->atributo = 12
$i = 2 => $objeto1 ->atributo = 0 ; $objeto2 ->atributo = 13

SITUATION 1.2

In this example I do $objeto1 increment the own attribute $objeto1, such as example 1 in question:

$objeto1 = new Exemplo();
$objeto2 = new Exemplo();
    $objeto1 ->atributo = 0;
    $objeto2 ->atributo = 10;
    for($i = 0; $i < 3; $i++){
        $objeto1 ->incrementar($objeto1);
    }

Results:

$i = 0 => $objeto1 ->atributo = 1 ; $objeto2 ->atributo = 10
$i = 1 => $objeto1 ->atributo = 2 ; $objeto2 ->atributo = 10
$i = 2 => $objeto1 ->atributo = 3 ; $objeto2 ->atributo = 10

SITUATION 1.3

In this example I do $objeto2 increment the attribute of $objeto1:

$objeto1 = new Exemplo();
$objeto2 = new Exemplo();
    $objeto1 ->atributo = 0;
    $objeto2 ->atributo = 10;
    for($i = 0; $i < 3; $i++){
        $objeto2 ->incrementar($objeto1);
    }

Results:

$i = 0 => $objeto1 ->atributo = 1 ; $objeto2 ->atributo = 10
$i = 1 => $objeto1 ->atributo = 2 ; $objeto2 ->atributo = 10
$i = 2 => $objeto1 ->atributo = 3 ; $objeto2 ->atributo = 10

SITUATION 1.4

$objeto1 = new Exemplo();
$objeto2 = new Exemplo();
    $objeto1 ->atributo = 0;
    $objeto2 ->atributo = 10;
    for($i = 0; $i < 3; $i++){
        $objeto2 ->incrementar($objeto2);
    }

Results:

$i = 0 => $objeto1 ->atributo = 0 ; $objeto2 ->atributo = 11
$i = 1 => $objeto1 ->atributo = 0 ; $objeto2 ->atributo = 12
$i = 2 => $objeto1 ->atributo = 0 ; $objeto2 ->atributo = 13

What can be drawn from these examples?

From these examples one can extract that the method is the instrument by which a class allows its instances to perform an action on a parameter.

So if you pass the instance itself as a parameter to be consumed by one of its own methods, the instance becomes the target of its own action. Eg: dog trying to bite its own tail. Situation 1.2 and situation 1.4.


SITUATION 2 and SITUATION 3

For a perfect visualization of POO behavior, I will make a class modification Exemplo.

    class Exemplo 
    {
        private $atributo;

        public function getAtributo(){
            return $this->$atributo;
        }

        public function  setAtributo($valor){
            $this->$atributo = $valor;
        }

        public function incrementar($alvo){
            $alvo->atributo += 1; 
        }

        // Método que engloba o exemplo 2
        public function auto_incrementar(){
            $this->atributo = 0;
            for($i = 0; $i < 3; $i++){
               $teste->incrementar($this); 
            } 
        }

        // Método que engloba ao exemplo 3
        public function incrementar_clone(){
            $this->atributo = 0;
            for($i = 0; $i < 3; $i++){
               $teste->incrementar(clone $this);
            } 
        }

    }

This time I will create only an object(instance) of the 'Example' class and call the methods auto_incrementar() and incrementar_clone()

$objeto1 = new Exemplo();
$objeto1 ->atributo = 10;

$objeto1 ->auto_incrementar(); //Note que ates de chamar o método setei o atributo com 10

Upshot:

$i = 0 => $objeto1 ->atributo = 1 
$i = 1 => $objeto1 ->atributo = 2 
$i = 2 => $objeto1 ->atributo = 3 

Why is that?

That’s because the method:

public function auto_incrementar(){
                $this->atributo = 0;
                for($i = 0; $i < 3; $i++){
                   $teste->incrementar($this); 
                } 
            }

Amounts to:

public function auto_incrementar(){
                $this->atributo = 0;
                $this->incrementar($this); 
                } 
            }

That is to say, it falls into the situation illustrated in 1.2 and 1.4.

The third situation of the example:

    $objeto1 = new Exemplo();
    $objeto1 ->atributo = 10;

    $objeto1 ->incrementar_clone();

Upshot:

$i = 0 => $objeto1 ->atributo = 0 
$i = 1 => $objeto1 ->atributo = 0 
$i = 2 => $objeto1 ->atributo = 0 

Since the method:

public function incrementar_clone(){
                $this->atributo = 0; // O atributo passou de 10 para 0
                for($i = 0; $i < 3; $i++){
                   $teste->incrementar(clone $this);
                } 
            }

Amounts to:

public function auto_incrementar(){
                    $this->atributo = 0
                    $this->incrementar(clone $this); 
                    } 
                }

But if we inspect the values of clone $this. For this we will assume grossly that $objeto2 = clone $this. Falling under situation 1.1 and 1.3

$i = 0 => $objeto2 ->atributo = 1 
$i = 1 => $objeto2 ->atributo = 2 
$i = 2 => $objeto2 ->atributo = 3 
  • I hope I helped and sorry for the syntax errors. I don’t use PHP.

  • 1

    It helped a lot! I could understand that the error is totally mine in relation to the concept and application of the uses of POO. Thank you!

3

I will answer only as a complement in the part I think the other answer is mistaken and because the question directly asks about it. I will not repeat what I agree with Augusto’s answer, which is the bulk of it and which correctly explains what is more central in the question.

All of this has nothing to do with OOP. So it’s not normal OOP behavior. Object orientation is code organization, so what these mechanisms do matters little to OOP. One of the biggest dangers of using OOP is that almost no one even understands what it means, so how do you get it right if the basics aren’t understood? To do wrong OOP has no advantage. And an important detail is that OOP in PHP is wrong by definition, PHP is a language of script and OOP is for complex problems with complex state, which does not exist in script.

Specifically on the problem presented has to do with the behavior of clone and nothing with OOP.

Class doesn’t make any sense. I’m not even going to say that uses term attribute incorrectly. Or that should have a builder, or that abuse of getter and Setter, or even that this shouldn’t be a class. It already has so many errors that by itself it is a bad example, and if it is just to show the error of the parameter passage it would not need all this, but I can’t help but mention that the fact that this is not even a clear class is part of the problem. If you used the mechanism the right way you wouldn’t have this problem.

But the serious conceptual error is to pass the object itself created as parameter to the method. You are passing as a parameter something that is already being passed implicitly (every method is like that), so you have zero sense in doing this, and then you start to have problems by mistaken use of the mechanism. This method incrementar() should not exist this way, so simple.

It would make some sense if the function were static. Not much for this case, but it wouldn’t be so absurdly wrong. It would still be strange to pass an instance of this very class.

The examples of the other answer that begin to be a little more OOP make perfect sense, but does something quite different than what was proposed by the AP.

Reinforcement that if there were no conceptual error there would be no error in the mechanism.

I don’t think bad names, and they’re bad names, were determinant for the wrong understanding of what happened. All names and concepts are wrong in this example.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.