PHP POO error Polymorphism

Asked

Viewed 540 times

4

When developing a simple PHP POO application, I came across an unexpected error, and I have no idea why. I’m starting now to study object-oriented programming and only have a small C-base#.

The program itself is simple: two classes (Pessoa and Funcionario) being Funcionario inherited from the class Pessoa, each with 2 methods, ler()and mostrarDados().

My goal is simple to create an object and pass by reference all information to the method lerDados() class Funcionario, and within this, call the method LerDados() class Pessoa (parent::lerDados()) passing only relevant class information (nome, idade and sexo), the others is "read" in the class itself Funcionario (empresa and salario).

Error: Declaration of Funcionario::lerDados($stringNome, $stringIdade, $stringSexo, $stringEmpresa, $stringSalario) should be compatible with Pessoa::lerDados($stringNome, $stringIdade, $stringSexo) in C:\wamp64\www\POO\namespace1.php on line 31

  <?php
  class Pessoa{     
  // PROPRIEDADES
  protected $nome;
  protected $idade;
  protected $sexo;  
  // METODOS      
   public function lerDados($stringNome, $stringIdade, $stringSexo){
    $this->nome  = $stringNome;
    $this->idade = $stringIdade;
    $this->sexo  = $stringSexo; 
  }  
  public function mostrarDados(){
    return "Nome: ".$this->nome."<br>\nIdade:".$this->idade."
  <br>\nSexo:".$this->sexo;
  }
  } 


  class Funcionario extends Pessoa{ 
  // PROPRIEDADES
  protected $empresa;
  protected $salario;
  // METODOS    
   public function lerDados($stringNome, $stringIdade, $stringSexo, 
   $stringEmpresa, $stringSalario){
     $this->nome  = $stringNome;
     $this->idade = $stringIdade;
     $this->sexo  = $stringSexo; 
     parent:: lerDados($this->nome,$this->idade,$this->sexo); // CHAMAR METODO DAS CLASSE PAI 
     $this->empresa = $stringEmpresa;
     $this->salario = $stringSalario;     
   }
   //public function mostrarDados(){}

   } // <------ERRO NESTA LINHA <-------

   $vendedor = new Funcionario();
   $vendedor->lerDados("Yuri", "19", "Masculino", "Tam", "3000");
   ?>

Is it me who is seriously wrong, or does PHP not accept this kind of polymorphism? Could someone guide me on how to fix this, and answer why this brutally fatal mistake happened?

  • Did any of the answers solve your question? Do you think you can accept one of them? Check out the [tour] how to do this, if you haven’t already. You would help the community by identifying what was the best solution for you. You can accept only one of them. But you can vote on any question or answer you find useful on the entire site (when you have enough score).

2 answers

4


I often say that if everyone who says they do OOP did it right, I would quit. OOP adds complexity to the code. If done where you need to bring advantages.

I see some problems in this code. First I think there is no inheritance there. There are those who disagree, but I think being an employee is just one paper that one can have and there is no relationship of is a.

That one mostraDados() requires the use of a specific device to show that it should not be part of a person or employee. What is data printing should be elsewhere.

Almost always putting protected limbs is a mistake. Some people think that languages should not even have this because it is either misused or is used for gambiarra. In general it violates encapsulation. It’s either public or private. I see reasons to use, but not that.

And there the lerDados() of Funcionario it got weird because it initializes the protected members and calls the mother class method to initialize again (?!?!?!).

This method reads no dice, so his name is wrong.

Objects should always be created in valid state, so this should probably be a builder. Done this way everything will be ok.

Polymorphism occurs with methods of the same signature. So the method of one class is not polymorphic in relation to the other. PHP has no method overload and therefore prevents two methods with the same name and different signature. If the signature were the same there it would be polymorphic and the daughter class would only have a method with the same name.

Note that this is not exactly the polymorphism even in C# and other languages, the difference is that other languages accept overload and will allow to have two methods with the same name and non-corphic signatures.

Constructors are not polymorphic, in fact they are static. So it works:

class Pessoa {     
    private $nome;
    private $idade;
    private $sexo;  
    public function __construct($stringNome, $stringIdade, $stringSexo) {
        $this->nome  = $stringNome;
        $this->idade = $stringIdade;
        $this->sexo  = $stringSexo; 
    }  
} 

class Funcionario extends Pessoa { 
    private $empresa;
    private $salario;
    public function __construct($stringNome, $stringIdade, $stringSexo, $stringEmpresa, $stringSalario) {
        parent::__construct($stringNome, $stringIdade, $stringSexo);
        $this->empresa = $stringEmpresa;
        $this->salario = $stringSalario;     
    }
}

$vendedor = new Funcionario("Yuri", "19", "Masculino", "Tam", "3000");

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

Of course, one day if that same person ceases to be an employee and becomes something else in the organization, you have to kill that person if you do the resurrection as something else. I find that very strange.

If the person can be an employee and something else at the same time, will he have two objects that are basically the same person? If you can be a client and the person is legal, will you have to differentiate whether the client is physical or legal? Which roll.

When misconcepting, OOP saves no one.

When you do OOP just because everyone else is doing it, just the way one person said it’s right and the mass is copying without understanding why they’re doing it is not appropriate.

  • Just out of curiosity, if you focus only on the POO issue, the AP could follow an approach using Decorator, since it allows to add or change the behavior to the object individually without affecting the other objects. However, such an approach also depends on the whole (which we do not have here) and, moreover, the Decorator should be used for specific cases (according to their motivation) which may not be the case of AP.

0

How we model our classes depends on the behavior (rules) defined for the application. In the model below I considered that in its application, a person cannot simply be a Person (direct instance of the class), for this reason the constructor of the class Person was defined as protected, that is, it should be called by another class that inherits from Person.

If we consider that Name, Age and Sex are primary attributes of the class, we can use a constructor and receive this data already when we create an instance of the object. Already to recover the values like to use the approach getAtribute, so I know that this public method is used to recover an attribute. Visual processing/formatting can be by an auxiliary class or can be done directly in the file that calls the method.

See the code:

<?php

class Pessoa{     

    private $nome;
    private $idade;
    private $sexo;  

    protected function __construct($nome, $idade, $sexo)
    {
        $this->nome = $nome;
        $this->idade = $idade;
        $this->sexo = $sexo;
    }

    protected function getNome()
    {
        return $this->nome;
    }

    protected function getIdade()
    {
        return $this->idade;
    }

    protected function getSexo()
    {
        return $this->sexo;
    }
} 

class Funcionario extends Pessoa
{ 
    private $empresa;
    private $salario;

    public function __construct($nome, $idade, $sexo, $empresa, $salario)
    {
        parent::__construct($nome, $idade, $sexo);
        $this->empresa = $empresa;
        $this->salario = $salario;
    }

    public function getEmpresa()
    {
        return $this->empresa;
    }

    public function getSalario()
    {
        return $this->salario;
    }
}

$funcionario = new Funcionario("Yuri", "19", "Masculino", "Tam", "3000");

echo $funcionario->getNome() . ' Trabalha na: ' . $funcionario->getEmpresa() . ' e ganha ' . $funcionario->getSalario();

Let’s take a second approach. Personally I like to use the famous setAtribute to define the value of my variables, but why do I do it? Simple, to include validation in the assignment of values. The example below is very simple and does not reflect a real flow of data validation, but serves to give an idea of how the thing works:

<?php

class Pessoa{     

    private $nome;
    private $idade;
    private $sexo;  

    protected function setNome($nome)
    {
        if(is_string($nome)) {
            $this->nome = $nome;
            return true;
        }

        return false;
    }

    protected function setIdade($idade)
    {
        if(is_int($idade)) {
            $this->idade = $idade;
            return true;
        }

        return false;
    }

    protected function setSexo($sexo)
    {
        if($sexo == 'M' || $sexo = "F") {
            $this->sexo = $sexo;
            return true;
        }

        return false;
    }

    public function getNome()
    {
        return $this->nome;
    }

    public function getIdade()
    {
        return $this->idade;
    }

    public function getSexo()
    {
        return $this->sexo;
    }
} 

class Funcionario extends Pessoa
{ 
    private $empresa;
    private $salario;

    public function __construct($nome, $idade, $sexo, $empresa, $salario)
    {
        self::setNome($nome);
        self::setIdade($idade);
        self::setSexo($sexo);
        $this->empresa = $empresa;
        $this->salario = $salario;
    }

    public function getEmpresa()
    {
        return $this->empresa;
    }

    public function getSalario()
    {
        return $this->salario;
    }
}

$funcionario = new Funcionario("Yuri", "19", "Masculino", "Tam", "3000");

echo $funcionario->getNome() . ' Trabalha na: ' . $funcionario->getEmpresa() . ' e ganha ' . $funcionario->getSalario();

Note: I used self::setNome in class Functionary, but it would also function as $this->setNome since we are talking about an inheritance, however, I prefer to use self because it makes it clear that the method has been inherited and assigns semantics to the code.

You can still set the strick_types=1 and then work with 'manual typing' input and output (return). See a simple example:

<?php
declare(strict_types=1);

class Pessoa{     

    private $nome;
    private $idade;
    private $sexo;  

    protected function setNome(string $nome) : bool
    {
        if(!is_null($nome)) {
            $this->nome = $nome;
            return true;
        }

        return false;
    }

    protected function setIdade(int $idade) : bool
    {
        if(!is_null($idade)) {
            $this->idade = $idade;
            return true;
        }

        return false;
    }
    //...
  • I understood, but in this second example self::setName($name), could I use Parent::setName($name) ? I tested here, did not change anything, there is some difference to call the set method or get inherited from the class "->", or to call directly in the parent method ?

  • @Yurigabriel your question is very good. In order not to run the risk of passing an incomplete or incorrect information, I took this question and generated a new question: https://answall.com/questions/247723/dúvida-quanto-ao-uso-de-this-self-e-parent, I generated this new question because in a way they are different subjects.

Browser other questions tagged

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