PHP Exercicios POO

De MediaWiki
Ir a la navegación Ir a la búsqueda

Definición de clases. Creación de obxectos e acceso a variables e métodos

  • Un programador novato (PN) decide introducirse no mundo da POO e intenta implantar

unha solución informática para a xestión dos vehículos da Empresa de Transporte na que traballa. Decide utilizar como linguaxe de programación PHP.

  • O problema que se lle expón é o seguinte: necesita almacenar información sobre os

distintos vehículos de que dispón a Empresa. Entre esta información está o ano de compra do vehículo, a súa matrícula, a súa marca.

  • O PN decide implantar nunha clase, a información necesaria.
Na páxina que vai amosar os datos (chamarémoslle Principal, o PN vai crear os catro vehículos de que dispón a Empresa neste momento, e vailles a asignar uns valores:
Php POO Ex 1.jpg

Exercicio: implantar a solución




Solución:

  • Arquivo: Vehiculo.php
<?php

class Vehiculo{
    public $matricula;
    public $marca;
    public $ano;
    
}
  • Arquivo: incluir-clases.inc.php
<?php
    function cargadorClases($class) {
        require $class . '.php';
    }

    spl_autoload_register('cargadorClases');


  • Arquivo: principal.php
<?php
    include 'incluir-clases.inc.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Exemplos de uso de clases</title>
    </head>
    <body>
        <?php
            $veh1 = new Vehiculo();
            $veh2 = new Vehiculo();
            $veh3 = new Vehiculo();
            $veh4 = new Vehiculo();
            
            $veh1->marca = "Peugot";
            $veh1->matricula = "C-1234-BZ";
            $veh1->ano = 1999;
            
            $veh2->marca = "Fiat";
            $veh2->matricula = "C-4325-AZ";
            $veh2->ano = 1998;
            
            $veh3->marca = "Opel";
            $veh3->matricula = "C-2342-AF";
            $veh3->ano = 1996;
            
            $veh4->marca = "Ford";
            $veh4->matricula = "C-4322-CF";
            $veh4->ano = 2000;
        ?>
    </body>
    
</html>


  • Comproba que non lle deu erros, pero queda coa dúbida se terá almacenados os valores dunha forma correcta, polo que é necesario que imprima devanditos valores.
Para 'imprimir' os valores, necesita un procedemento (método) que os saque por pantalla.
Así que decide crear dentro da clase Vehículo, un método que imprima devandita información. Podería facer un único método que imprima o ano, matrícula e marca á vez, pero decide crear tres métodos, un para cada tipo de información a imprimir, de nome impAno, impMatricula, impMarca, por se necesita mostrar a información de forma individualizada.
  • Unha vez implantados, decide probalos, a ver se mostran a información correcta, Para iso modifica a páxina Principal, facendo que cada obxecto Vehículo chame a cada un dos métodos.




Solución:

  • Arquivo: Vehiculo.php
<?php

class Vehiculo {
    public $matricula;
    public $marca;
    public $ano;
    
    public function impMatricula() {
        printf("<div>%s</div>",$this->matricula);
    }
    public function impMarca() {
        printf("<div>%s</div>",$this->marca);
    }
    public function impAno() {
        printf("<div>%s</div>",$this->ano);
    }
    
}


  • Arquivo: Principal.php
            ...........

            echo "<b>Datos veh1</b><br />";
            $veh1->impMatricula();
            $veh1->impMarca();
            $veh1->impAno();
            
            echo "<b>Datos veh2</b><br />";
            $veh2->impMatricula();
            $veh2->impMarca();
            $veh2->impAno();

            echo "<b>Datos veh3</b><br />";
            $veh3->impMatricula();
            $veh3->impMarca();
            $veh3->impAno();
            ...........

Nota: Soamente se amosa o código engadido á páxina Principal.

Herdanza

  • O PN atópase bastante satisfeito do conseguido ata agora, pero isto só era unha pequena parte do que ten que facer. Digamos que a súa empresa adícase ao transporte de

diñeiro, concretamente dous dos seus vehículos que son furgonetas, e que cada vehículo pode transportar unha cantidade de diñeiro. Ademais cada furgoneta leva consigo un número de vixiantes xurados. Devandita información é necesario almacenala.

Ante esta situación, o PN decide crear unha nova clase, Furgoneta, cos procedementos

da clase Vehículo e os novos atributos (cantidade de diñeiro e número de vixiantes xurados). Pero cando ía empezar a implantar devandita solución, aparece o Programador Experto, alguén que xa traballou en Xava moitos anos, e ao ver o que ía facer, explícalle que en Xava, ao ser unha Linguaxe de POO pódese realizar o que se chama Herdanza. É dicir, crear unha subclase que herde todo o que ten a superclase.

Con esta gran vantaxe, o PN decide crear un subclase Furgoneta da superclase Vehículo. Dentro da nova clase Furgoneta, implementa os novos atributos (cantidade de diñeiro e número de vixiantes

xurados), xa que son atributos propios da clase Furgoneta, que non ten a clase Vehículo. Por outra banda, como o PN quererá mostrar a información destes dous novos atributos, tamén crea, dentro da propia clase Furgoneta, dous métodos, para mostrar devanditos atributos de nome impDiñeiro, impXurados.

Para probar esta nova clase, vai ter que modificar a súa páxina Principal, xa que tiña implantados catro obxectos da clase Vehiculos, pero agora sabe que dúas deses vehículos son Furgonetas, polo que terá que cambiar o seu código, e se antes tiña catro

vehículos da forma:

            $veh1 = new Vehiculo();
            $veh2 = new Vehiculo();
            $veh3 = new Vehiculo();
            $veh4 = new Vehiculo();
Agora terá dous vehículos e dúas Furgonetas da forma:
            $veh1 = new Vehiculo();
            $veh2 = new Vehiculo();
            $furg1 = new Furgoneta();
            $furg2 = new Furgoneta();


A cada un dos seus vehículos (furgonetas e vehículos) asignaralles os valores aos seus

atributos e chamará aos métodos que imprimen devanditos valores.

O PN realiza todo o anterior, comprobando que todo é correcto.




Solución:


Arquivo: Furgoneta.php

<?php
class Furgoneta extends Vehiculo {
    public $diñeiro;
    public $numVixiantes;
    
    public function impDiñeiro() {
        printf("<div>%s</div>",$this->diñeiro);
    }
    public function impNumVixiantes() {
        printf("<div>%s</div>",$this->numVixiantes);
    }
    
}

Arquivo: principal.php

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);  // Non debería ser necesario xa que amosa posibles erros de configuración no php.ini ou erros antes da execución do script, pero podemos poñelo por se acaso.
error_reporting(E_ALL);

include 'incluir-clases.inc.php';
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">

    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Exemplos de uso de clases</title>
    </head>
    <body>
    <?php
        $veh1 = new Vehiculo();
        $veh2 = new Vehiculo();
        $furg1 = new Furgoneta();
        $furg2 = new Furgoneta();

        $veh1->marca = "Peugot";
        $veh1->matricula = "C-1234-BZ";
        $veh1->ano = 1999;
        
        $veh2->marca = "Fiat";
        $veh2->matricula = "C-4325-AZ";
        $veh2->ano = 1998;
        
        $furg1->marca = "Opel";
        $furg1->matricula = "C-2342-AF";
        $furg1->ano = 1996;
        
        
        $furg2->marca = "Ford";
        $furg2->matricula = "C-4322-CF";
        $furg2->ano = 2000;
        
        $furg1->diñeiro = 1000;
        $furg1->numVixiantes = 1;
        $furg2->diñeiro = 5000;
        $furg2->numVixiantes = 3;

        echo "<b>Datos veh1</b><br />";
        $veh1->impMatricula();
        $veh1->impMarca();
        $veh1->impAno();
        
        echo "<b>Datos veh2</b><br />";
        $veh2->impMatricula();
        $veh2->impMarca();
        $veh2->impAno();

        echo "<b>Datos Furgoneta 1</b><br />";
        $furg1->impMatricula();
        $furg1->impMarca();
        $furg1->impAno();
        $furg1->impDiñeiro();
        $furg1->impNumVixiantes();

        echo "<b>Datos Furgoneta 2</b><br />";
        $furg2->impMatricula();
        $furg2->impMarca();
        $furg2->impAno();
        $furg2->impDiñeiro();
        $furg2->impNumVixiantes();        ?>
    </body>

    

</html>
  • O PN pensa agora que as súas outros dous vehículos (que son coches), non transportan diñeiro, senón documentos segredos. Cada coche transporta un documento segredo, do cal é necesario gardar o seu nome, así como o nome do condutor que leva devandito coche.
O PN pensando da mesma forma que o caso das furgonetas, agora sabe que pode crear unha subclase (chamada Coches, por exemplo) da superclase Vehiculos. En dita subclase estarán os atributos NomeCond e NomeDoc. Polo tanto implementa dita subclase e os dous atributos así como os métodos necesarios para mostralos (impNomeDoc, impNomeCond)
Unha vez feito isto, o PN modifica a páxina Principal, igual que fixo no caso das Furgonetas para probar todo o novo.

Solución:

Arquivo: Coche.php

<?php

class Coche extends Vehiculo{
    
    public $nomeCond;
    public $nomeDoc;
    
    public function impNomeCond() {
        printf("<div>%s</div>",$this->nomeCond);
    }
    public function impNomeDoc() {
        printf("<div>%s</div>",$this->nomeDoc);
    }
}

Arquivo: principal.php

<?php
// Lembrar quitar isto cando todo funcione correctamente
ini_set('display_errors', 1);   
ini_set('display_startup_errors', 1);  // Non debería ser necesario xa que amosa posibles erros de configuración no php.ini ou erros antes da execución do script, pero podemos poñelo por se acaso.
error_reporting(E_ALL);

include 'incluir-clases.inc.php';
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">

    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Exemplos de uso de clases</title>
    </head>
    <body>
    <?php
        $coch1 = new Coche();
        $coch2 = new Coche();
        $furg1 = new Furgoneta();
        $furg2 = new Furgoneta();

        $coch1->marca = "Peugot";
        $coch1->matricula = "C-1234-BZ";
        $coch1->ano = 1999;
        $coch1->nomeCond="Pepe";
        $coch1->nomeDoc="PAPEL BARCENAS";
        
        $coch2->marca = "Fiat";
        $coch2->matricula = "C-4325-AZ";
        $coch2->ano = 1998;
        $coch2->nomeCond="Luis";
        $coch2->nomeDoc="Caso naseiro";

        $furg1->marca = "Opel";
        $furg1->matricula = "C-2342-AF";
        $furg1->ano = 1996;
        
        
        $furg2->marca = "Ford";
        $furg2->matricula = "C-4322-CF";
        $furg2->ano = 2000;
        
        $furg1->diñeiro = 1000;
        $furg1->numVixiantes = 1;
        $furg2->diñeiro = 5000;
        $furg2->numVixiantes = 3;

        echo "<b>Datos coch1</b><br />";
        $coch1->impMatricula();
        $coch1->impMarca();
        $coch1->impAno();
        $coch1->impNomeCond();
        $coch1->impNomeDoc();
        
        echo "<b>Datos coch2</b><br />";
        $coch2->impMatricula();
        $coch2->impMarca();
        $coch2->impAno();
        $coch2->impNomeCond();
        $coch2->impNomeDoc();

        echo "<b>Datos Furgoneta 1</b><br />";
        $furg1->impMatricula();
        $furg1->impMarca();
        $furg1->impAno();
        $furg1->impDiñeiro();
        $furg1->impNumVixiantes();

        echo "<b>Datos Furgoneta 2</b><br />";
        $furg2->impMatricula();
        $furg2->impMarca();
        $furg2->impAno();
        $furg2->impDiñeiro();
        $furg2->impNumVixiantes();        ?>
    </body>

</html>

Construtores

Recapitulemos o que fixemos ata este momento. creei 3 clases (Vehiculos, Coches e Furgonetas), as cales poden almacenar información que eu necesito, así como os métodos necesarios para imprimir (mostrar) dita información.
Por outra banda, creei outra páxina Principal, a cal vai facer uso das clases anteriores, mediante a creación de 4 vehículos, dous de tipo Coches e dous de tipo Furgonetas.
Cada un destes vehículos é unha instancia da clase correspondente e van ter almacenados uns valores concretos. Se eu quixese ter almacenado mais información sobre outros coches, non tería mais que crear un novo obxecto (instanciar) da clase correspondente.
O PN séguese rompendo o ‘coco’ para implantar de forma informática todo o proceso da súa empresa. O seguinte que pensa é en refinar un pouco máis o feito ata agora.
Primeiro pensa que sería conveniente ter un método que lle dese valores ás variables ou atributos de que dispomos en cada clase. É dicir, que no canto de pór:
            $furg1->marca = "Opel";
            $furg1->matricula = "C-2342-AF";
            $furg1->ano = 1996;
            $furg1->diñeiro = 1000;
            $furg1->numVixiantes = 1;

chamásese a un método con 5 parámetros, de tal forma que cada un destes parámetros, será asignado a cada atributo, da forma seguinte:

$furg1->asignarValores($param1, $param2, $param3, $param4, $param5)

onde:

$this->marca = $param1
$this->matricula = $param2
$this->ano = $param3
$this->nomeDoc = $param4
$this->nomeCond = $param5
Así poderiamos pór nunha única liña o que antes tiñamos en cinco: $furg1->asignarValores("Peugot","C-1234-BZ",1999, "Documento segredo1","Angel");


Ante esta brillante idea, o PN disponse a implantala. Primeiro pensa onde situar o método que asigna os valores dos parámetros aos atributos e inmediatamente dáse conta de que non pode situalo na clase Vehiculos, xa que en devandita clase non existen os atributos nomeDoc e nomeCond, polo que decide situalos nas subclases. Así, na subclase Coches e na subclase Furgonetas, decide crear un método de nome 'asignarValores' de cinco parámetros, nos que se asigna os valores ás cinco variables ou atributos de que dispomos.

Nota: Podedes probalo ou seguir lendo :)

Pero velaquí que volve o Programador Experto a coller un bolígrafo que se esqueceu e observa o que o PN disponse a facer.
- !!!!! Pero que feixes !!!!!
Como se ve que levas pouco tempo nisto de Xava e da POO. Para realizar a asignación inicial de variables pódese utilizar un método especial, chamado construtor, o cal chámase cada vez que se realiza un new, é dicir, cada vez que se crea un obxecto dunha clase. O nome do construtor chámase __construct, podendo levar ou non parámetros.
Ante a cara de asombro e incomprensión do PN (a parte de porse vermello como unha brasa), o Programador Experto dille:
- Vouche a pór un exemplo para que o entendas:
Supoñamos que dispoño dunha clase denominada Proba, con dous atributos da forma:
class Proba
{
 $atrib1;
 $atrib2;
}

e dunha páxina Principal, a cal fai uso da clase Proba, da forma:

<?php
 $miProba = new Proba();
?>
Supoñamos que quero que os dous atributos que vai ter o obxecto (atrib1, atrib2) teñan un valor por defecto, por exemplo 10. Para facer isto, creo o que se chama un construtor, que non é máis que

un método como outro calquera, pero ao cal PHP chama cada vez que se fai un new.

Así. modifico a clase Proba e creo o construtor da forma:
class Proba
{
 $atrib1;
 $atrib2;

 function __construct() {
    $atrib1 = 10;
    $atrib2 = 10;
 }
}
Agora na páxina Principal,
<?php
 $mProba = new Proba();  // Chama ao construtor
?>
  • Neste momento, $mProba->atrib1 ten o valor 10, igual que $mProba->atrib2.
O PN ante esta demostración de sabedoría non sabe dicir outra cousa que:
- Ahhhhhhhhhhhhhhhhhhh
- Pero isto non é todo, dille o Programador Experto.



Resulta que noutras linguaxes de POO, se poden ter definidos tantos construtores como se queira, sempre que se diferencien no número ou tipo de argumentos (xa que o nome é fixo). Así, eu podería ter outro construtor para asignar valores concretos aos dous atributos. No caso de PHP isto se complica un pouco xa que PHP non permite implementar isto directamente.
Para solucionalo, temos que programar dentro do constructor cantos parámetros estamos a recibir e en función do número de parámetros chamar a diferentes funcións que iniciali
As funcións que temos que empregar dentro de __construct son:
Vexamos un exemplo:
class Proba
{
 $atrib1;
 $atrib2;

 function __construct() {
      $args = func_get_args();
      $num = func_num_args();
      switch ($num) {
            case 0: // Chamamos ao constructor sen parámetros
                $this->$atrib1 = 10;
                $this->$atrib2 = 10;
                break;
            case 1:  // Chamamos ao constructor cun parámetro
                $this->atrib1= func_get_arg(0); // Os argumentos empezan en 0
                break;
            case 2: // Chamamos ao constructor cun 3 parámetro
                $this->atrib1= func_get_arg(0);
                $this->atrib2= func_get_arg(1);
        }

    }
}


De tal forma que agora podo crear un obxecto da clase Proba e asignarlles valores aos seus atributos da forma:
<?php
 $mProba = new Proba(10,20);  // Chama ao construtor con dous parámetros
?>


A isto denomínaselle sobrecargar un método (overload). É dicir, ter dentro dunha clase varios métodos có mesmo nome, pero con distinto tipo ou número de argumentos, dille o Programador Experto cun certo aire de superioridade....(en PHP isto non é exactamente así xa que os métodos en realidade teñen diferentes nomes :) )
O PN ponse risoño como un paxaro ao comprender o que o Programador Experto explícalle (aínda que un pouco mosqueado).
Agora sabe que no caso da clase Coches e a clase Furgonetas, debe crear un construtor que teña cinco parámetros, asignando a cada atributo o valor do parámetro. A cada parámetro asígnalle un nome: param1, param2,....e posteriormente dentro do construtor, asigna os valores deses parámetros a cada variable. Así na clase Coches terá 5 parámetros que asignará a marca, ano, matricula, nomeCond e nomeDoc e no caso de Furgonetas asignará a marca, ano, matricula, diñeiro e numXurados.


Solución:

Arquivo Furgoneta.php

<?php

class Furgoneta extends Vehiculo {
    public $diñeiro;
    public $numVixiantes;
    
    function __construct($matricula,$marca,$ano,$diñeiro,$numVixiantes){
        $this->matricula=$matricula;
        $this->marca=$marca;
        $this->ano=$ano;
        $this->diñeiro=$diñeiro;
        $this->numVixiantes=$numVixiantes;
        
    }
    
    public function impDiñeiro() {
        printf("<div>%s</div>",$this->diñeiro);
    }
    public function impNumVixiantes() {
        printf("<div>%s</div>",$this->numVixiantes);
    }
    
}


Arquivo Coche.php

<?php

class Coche extends Vehiculo{
    
    public $nomeCond;
    public $nomeDoc;
    
    function __construct($matricula,$marca,$ano,$nomeCond,$nomeDoc){
        $this->matricula=$matricula;
        $this->marca=$marca;
        $this->ano=$ano;
        $this->nomeCond=$nomeCond;
        $this->nomeDoc=$nomeDoc;
        
    }

    public function impNomeCond() {
        printf("<div>%s</div>",$this->nomeCond);
    }
    public function impNomeDoc() {
        printf("<div>%s</div>",$this->nomeDoc);
    }
}

Arquivo principal.php

<?php
// Lembrar quitar isto cando todo funcione correctamente
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);  // Non debería ser necesario xa que amosa posibles erros de configuración no php.ini ou erros antes da execución do script, pero podemos poñelo por se acaso.
error_reporting(E_ALL);

include 'incluir-clases.inc.php';
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Exemplos de uso de clases</title>
    </head>
    <body>
        <?php

            $coch1 = new Coche("Peugot", "C-1234-BZ",1999,"Luis se forte","PAPEL BARCENAS");
            $coch2 = new Coche("Fiat","C-4325-AZ",1998,"Pepe","Caso naseiro");
            
            $furg1 = new Furgoneta("Opel","C-2342-AF",1996,1000,1);
            $furg2 = new Furgoneta("Ford","C-4322-CF",2000,5000,3);

            echo "<b>Datos coche1</b><br />";
            $coch1->impMatricula();
            $coch1->impMarca();
            $coch1->impAno();
            $coch1->impNomeCond();
            $coch1->impNomeDoc();
            
            echo "<b>Datos coche2</b><br />";
            $coch2->impMatricula();
            $coch2->impMarca();
            $coch2->impAno();
            $coch2->impNomeCond();
            $coch2->impNomeDoc();

            echo "<b>Datos Furgoneta 1</b><br />";
            $furg1->impMatricula();
            $furg1->impMarca();
            $furg1->impAno();
            $furg1->impDiñeiro();
            $furg1->impNumVixiantes();

            echo "<b>Datos Furgoneta 2</b><br />";
            $furg2->impMatricula();
            $furg2->impMarca();
            $furg2->impAno();
            $furg2->impDiñeiro();
            $furg2->impNumVixiantes();
?>
    </body>
    
</html>



Ben, di o PN, isto marcha. A verdade é que desta forma aforrei moito código en asignacións, pero aínda así hai algo que non me cadra...
Se observo o código, resulta que asignamos os atributos da clase Vehiculos na clase Coches e Furgonetas, e que este código está repetido. Se aplico o mesmo concepto de construtor, podería ter un construtor na clase Vehiculos, que asigne valores iniciais os tres atributos que pertencen a esta clase, quedando os construtores das clases Coches e Furgonetas coa asignación aos seus atributos correspondentes. Isto é moi bonito, pero pódese facer ? como podo chamar ao construtor da clase Vehiculos desde o construtor da clase Coches e a clase Furgonetas ?
  • O PN inicia unha procura exhaustiva coa documentación que posúe e despois de moitas cuncas de café cunhas poucas gotas de whisky, descobre que en PHP pódese utilizar dúas palabras craves:

- $this: é unha referencia á instancia actual (ao obxecto actual). É dicir, supoñamos que temos un obxecto da clase Coches (miCoche). Este obxecto é unha instancia da clase Coches. Se eu asigno á variable $miCoche->marca = "Citroen" e posteriormente chamo a un método da forma $miCoche->metodo() no que teño a instrución $this->marca, estou a referirme aos valores da instancia (obxecto) que chamou a este método (neste caso miCoche). $this é o equivalente a 'miCoche', é dicir, poderíase chamar a calquera método ou referenciar a calquera variable do obxecto miCoche.

- parent::: fai referencia aos métodos e variables da superclase. Só se utiliza en caso de ter dúas clases relacionadas mediante unha herdanza, na que dentro da subclase queiramos referenciar algún atributo e método da superclase.

O PN ponse moi contento e decide tomarse unha cervecita por descubrir esta información tan útil. Fará o seguinte:
  • Primeiro, modificará os nome dos parámetros nos dous construtores creados, xa que cos que tiña, a primeira ollada non se enteraba para que servían (o nome non era moi significativo). Daralles os mesmos nomes que os atributos da clase.
  • Segundo, creará un construtor na clase Vehiculos, con tantos parámetros como atributos teña a clase, para chamalo desde a clase Coches e Furgonetas. Como o vai a facer ? Mediante o uso da palabra super. Desde o construtor de cada unha das clases anteriores, chamará ao construtor da clase superior (Vehiculos)


Solución:

Arquivo Vehiculo.php

<?php

class Vehiculo {
    public $matricula;
    public $marca;
    public $ano;
    
    function __construct($matricula,$marca,$ano){
        $this->matricula=$matricula;
        $this->marca=$marca;
        $this->ano=$ano;
    }
    
    public function impMatricula() {
        printf("<div>%s</div>",$this->matricula);
    }
    public function impMarca() {
        printf("<div>%s</div>",$this->marca);
    }
    public function impAno() {
        printf("<div>%s</div>",$this->ano);
    }
    
}


Arquivo Furgoneta.php

<?php

class Furgoneta extends Vehiculo {
    public $diñeiro;
    public $numVixiantes;
    
    function __construct($matricula,$marca,$ano,$diñeiro,$numVixiantes){
        parent::__construct($matricula, $marca, $ano);
        $this->diñeiro=$diñeiro;
        $this->numVixiantes=$numVixiantes;
        
    }
    
    public function impDiñeiro() {
        printf("<div>%s</div>",$this->diñeiro);
    }
    public function impNumVixiantes() {
        printf("<div>%s</div>",$this->numVixiantes);
    }
    
}


Arquivo Coche.php

<?php

class Coche extends Vehiculo{
    
    public $nomeCond;
    public $nomeDoc;
    
    function __construct($matricula,$marca,$ano,$nomeCond,$nomeDoc){
        parent::__construct($matricula, $marca, $ano);
        $this->nomeCond=$nomeCond;
        $this->nomeDoc=$nomeDoc;
        
    }

    public function impNomeCond() {
        printf("<div>%s</div>",$this->nomeCond);
    }
    public function impNomeDoc() {
        printf("<div>%s</div>",$this->nomeDoc);
    }
}

Atributos e métodos de clase

O PN atópase tan satisfeito consigo mesmo que decide ir amosar ao Programador Experto o que conseguiu. Este dille:

- Ben, vexo que vas collendo o espírito da POO, pero aínda che queda moito camiño por percorrer. Vou expor un problema ao teu modelo, a ver se es capaz de achar a solución.

O PN empeza a suar....

- Supón que dentro do teu modelo, queres gardar información acerca do número de vehículos da empresa. Digamos, que esta información é común para todos os vehículos. Como o solucionas....

O PN vólvese ao seu despacho có rabo entre as pernas, pero decide investigar para ver se pode dar coa solución. A primeira idea que ten é a de poñer o atributo numCoches na clase Vehiculos, pero enseguida dáse conta de que isto ten un problema. Se a información é común para todos os vehículos, este atributo é propio para cada obxecto que se cre da clase, é dicir, que se teño os meus catro vehículos, tería que asignar catro veces o mesmo número de coches, e se quixese modificala, tería que modificala para os catro vehículos instancias da clase => obxectos).

Mergullándose entre a documentación da POO atopa a solución.

- !!!EUREKA!!! Resulta que todas as variables que creei ata agora, denomínanse variables de instancia, xa que almacenan información propia de cada obxecto (instancia de clases) fronte a outras denominadas variables de clase cuxo valor é común para todos os obxectos desa clase. É dicir, son como variables globais, pero a nivel de obxectos dunha clase.

Para declarar unha variable de clase, debo interpoñer o modificador static ao nome da variable da forma: static $nombreVariable

Agora só teño que poñer o atributo numVehiculos na clase Vehiculos (non debo poñelo noutra clase, xa que a variable é común ás dúas subclases) e que sexa de tipo estático (static). Cada vez que cre un vehículo, terei que incrementar o seu valor nun.

Este valor será común a todos os obxectos da clase. Tamén crearei un método que imprima devandito valor (impNumVehiculos).

Por outra banda, para acceder ao valor dunha variable de clase, pode poñer:

  • Clase::$variableClase

Se quere acceder dende dentro da clase, podo poñer:

  • self::$variableClase

Se se quere acceder dende unha subclase a unha variable de clase declarada na superclase:

  • parent::$variableClase ou ClasePadre::$variableClase


Seguindo có noso exercicio... Podo crear os catro vehículos e modificar o valor da variable para cada novo obxecto creado da forma:

Coches.numVehiculos++; ou Furgonetas.numVehiculos++;

Pero isto poderíase mellorar, xa que se cada vez que chame á creación dun Vehiculo, vai o construtor de Vehiculos, e podería incrementar neste método o valor da variable numVehiculos.


Solución

Arquivo Vehiculo.php

<?php

class Vehiculo {
    public static $numVehiculos=0;
    
    public $matricula;
    public $marca;
    public $ano;
    
    function __construct($matricula,$marca,$ano){
        $this->matricula=$matricula;
        $this->marca=$marca;
        $this->ano=$ano;
        
        Vehiculo::$numVehiculos+=1;
    }
    
    public function impNumVehiculos() {
        printf("<div>%s</div>",  Vehiculo::$numVehiculos);
    }
    
    public function impMatricula() {
        printf("<div>%s</div>",$this->matricula);
    }
    public function impMarca() {
        printf("<div>%s</div>",$this->marca);
    }
    public function impAno() {
        printf("<div>%s</div>",$this->ano);
    }
    
}

Arquivo principal.php

<?php
          ...............................


            echo "NUM VEHICULOS<br />";
            $coch1->impNumVehiculos();  // Dará o mesmo valor para todos
            $coch2->impNumVehiculos();  // Dará o mesmo valor para todos
            $furg1->impNumVehiculos();  // Dará o mesmo valor para todos
            $furg2->impNumVehiculos();  // Dará o mesmo valor para todos

?>
    </body>
    
</html>



- Ben, ben di o PN...

Pero resulta que o mesmo concepto pode ser aplicado aos métodos, creando métodos de clase, que poden ser chamados sen necesidade de ter un obxecto da mesma como teño ata agora. Isto pode ser moi interesante, xa que pode imprimir o número de vehículos que teño sen necesidade de utilizar obxectos da clase. A forma de chamar a un método de clase é: NomeClase::método();

E a forma de definir un método de clase é:static impNumCoches(){....}

O PN vai cambiar o método impNumCoches a método de clase, e chamarao para facer unha proba.


Solución

Arquivo Vehiculo.php

<?php

class Vehiculo {
    public static $numVehiculos=0;
    
    public $matricula;
    public $marca;
    public $ano;
    
    function __construct($matricula,$marca,$ano){
        $this->matricula=$matricula;
        $this->marca=$marca;
        $this->ano=$ano;
        
        Vehiculos::$numVehiculos+=1;
    }
    
    public static function impNumVehiculos() {
        printf("<div>%s</div>",  Vehiculo::$numVehiculos);
    }
    
    public function impMatricula() {
        printf("<div>%s</div>",$this->matricula);
    }
    public function impMarca() {
        printf("<div>%s</div>",$this->marca);
    }
    public function impAno() {
        printf("<div>%s</div>",$this->ano);
    }
    
}

Arquivo principal.php

<?php
          ...............................


            echo "NUM VEHICULOS<br />";
            Vehiculo::impNumVehiculos();
?>
    </body>
    
</html>

Encapsulación

  • O PN diríxese coa solución para ver ao Programador Experto.
- Téñoo. Xa dei coa solución. Custoume un pouco, pero xa o teño. Ensínalle a solución.
- Moi ben, di o Programador Experto. Vese que verdadeiramente estás a coller o truco a isto da POO. Creo que deica pouco xa estarás preparado para empezar a realizar un sitio web. Só che falta ver que son as clases abstractas así como a seguridade, o que na POO chámase encapsulación, é dicir, como facer que os atributos ou métodos teñan o seu acceso limitado.
Creo que é mellor que mires a parte de encapsulación e o tema das clases

abstractas o trateremos máis adiante.

O PN ponse moi contento de que está xa tan preto do fin e considérase case un igual con respecto ao Programador Experto (nada máis lonxe da realidade).
Mirando a documentación, descobre que para limitar o acceso ás variables ou

métodos dunha clase existe 3 modificadores: public, private ou protected.

Visibilidade Afecta a... Significado
Public Métodos, clases e variables Accesible dende calquera parte
Private Métodos e variables Só accesibles dende métodos desa clase. Non se herdan polas subclases e non son accesibles dende elas.
Protected Métodos e variables Son visibles dentro da clase e subclases.


Todo isto parécelle un pouco lioso, así que lle pregunta ao seu irmán, que traballou noutra linguaxe de POO que significa e para que serve:

- Irmán, teño un pequeno problema no traballo. Non entendo para nada isto dos tipos de acceso a variables e métodos na POO. Pódesmo explicar ?
- Claro. Realmente son formas de limitar o acceso á información, para realizar un programación máis robusta. Desde logo non necesitarías implantar nada disto e o teu programa funcionará igual, pero grazas a esta axuda podes limitar o número de veces que o teu programa fallará se as túas clases son usadas por outras persoas que poidan facer un mal uso delas.
- Que interesante (di o PN). Pois isto vénme de corrida co seguinte que teño que implantar. Resulta que é necesario calcular o diñeiro que ingresamos polos servizos dos nosos automóbiles. Teño que almacenar dúas informacións. Primeiro o diñeiro que ingresamos por cada automóbil, e segundo o diñeiro total que ingresamos, que será a suma dos ingresos dos automóbiles.

A forma de calcular o diñeiro de cada automóbil é no caso das furgonetas, en función da cantidade de diñeiro que leva.

Así, se é < 100.000 ingresamos 10.000 e se é >= 100.000 ingresamos 25.000 (!MIÚDO ABUSO!!).

No caso dos coches, o diñeiro que se lle pide vai en función da importancia do documento que leve. Este dato non estaba contemplado ata agora, polo que terei que incorporalo como un atributo dos Coches e modificar o construtor para asignarlle un valor o iniciar (instanciar) cada coche.

A importancia está representado por un número:

  • Se importancia = 0 ingresamos 5000
  • Se importancia = 1 ingresamos 10000
  • Se importancia = 2 ingresamos 20000

(usar sentenza switch)

Para implantar todo isto, que é o que teño que facer ?


(NOTA: pensar se sodes capaces de implementalo)



Primeiro terei que incorporar a variable importancia á clase Coches, xa que é un atributo propio dos coches e non ten nada que ver coas furgonetas. Esta variable é de instancia (non de clase) xa que cada coche ten o seu propio valor en función do documento que transporte. Unha vez feito isto, terei que modificar o construtor de Coches para asignarlle un valor á importancia do documento, igual que fixemos cos outros atributos.

Despois terei que crear un variable ingreso a cal almacenará o diñeiro que recadará cada Automóbil. Esta variable estará definida na clase Vehiculos, xa que é común tanto a Coches como Furgonetas (os dous teñen uns ingresos en función do que transporte) e é unha variable de instancia, xa que cada Vehiculo terá uns valores concretos.

Agora terei que crear un procedemento que asigne os ingresos en función do que leva cada Vehículo. Este procedemento é propio a cada clase (Coches e Furgonetas) xa que se calcula de diferente forma. Por tanto crearei un método calcularIngreso tanto na clase Coches como na clase Furgonetas. Este método modificará o valor dos variable ingresos en función da importancia no caso dos Coches e en función do diñeiro no caso das Furgonetas.

- Que divertido é isto (exclama o PN)


Agora necesito implantar un método que imprima o ingresado por cada vehículo. Devandito método estará definido na clase Vehículos, xa que é común ás dúas clases.


Solución

Arquivo Coche.php

<?php

class Coche extends Vehiculo{
    
    public $nomeCond;
    public $nomeDoc;
    public $importancia;
    
    function __construct($matricula,$marca,$ano,$nomeCond,$nomeDoc,$importancia){
        parent::__construct($matricula, $marca, $ano);
        $this->nomeCond=$nomeCond;
        $this->nomeDoc=$nomeDoc;
        $this->importancia=$importancia;
        
    }

    public function calcularIngreso() {
        switch($this->importancia)
        {
            case 0:
                $this->ingreso=5000;
                break;
            case 1:
                $this->ingreso=10000;
                break;
            case 2:
                $this->ingreso=20000;
                break;
            
        }
    }
    public function impNomeCond() {
        printf("<div>%s</div>",$this->nomeCond);
    }
    public function impNomeDoc() {
        printf("<div>%s</div>",$this->nomeDoc);
    }
}


Arquivo Furgoneta.php

<?php

class Furgoneta extends Vehiculo {
    public $diñeiro;
    public $numVixiantes;

    
    function __construct($matricula,$marca,$ano,$diñeiro,$numVixiantes){
        parent::__construct($matricula, $marca, $ano);
        $this->diñeiro=$diñeiro;
        $this->numVixiantes=$numVixiantes;
        
    }
    
    public function calcularIngreso() {
        if ($this->diñeiro<100000){
            $this->ingreso = 10000;
        }
        else {
            $this->ingreso = 25000;
        }
    }
    public function impDiñeiro() {
        printf("<div>%s</div>",$this->diñeiro);
    }
    public function impNumVixiantes() {
        printf("<div>%s</div>",$this->numVixiantes);
    }
    
}


Arquivo Vehiculo.php

<?php

class Vehiculo {
    public static $numVehiculos=0;
    
    public $matricula;
    public $marca;
    public $ano;
    public $ingreso;
        
    function __construct($matricula,$marca,$ano){
        $this->matricula=$matricula;
        $this->marca=$marca;
        $this->ano=$ano;
        
        Vehiculo::$numVehiculos+=1;
    }
    
    public static function impNumVehiculos() {
        printf("<div>%s</div>",  Vehiculo::$numVehiculos);
    }
    
    public function impIngreso() {
        printf("<div>%s</div>",$this->ingreso);
    }
    public function impMatricula() {
        printf("<div>%s</div>",$this->matricula);
    }
    public function impMarca() {
        printf("<div>%s</div>",$this->marca);
    }
    public function impAno() {
        printf("<div>%s</div>",$this->ano);
    }
    
}


Arquivo principal.php

<?php
    include 'incluir-clases.inc.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Exemplos de uso de clases</title>
    </head>
    <body>
        <?php

            $coch1 = new Coche("Peugot", "C-1234-BZ",1999,"Luis se forte","PAPEL BARCENAS",1);
            $coch2 = new Coche("Fiat","C-4325-AZ",1998,"Pepe","Caso naseiro",2);
            
            $furg1 = new Furgoneta("Opel","C-2342-AF",1996,1000,1);
            $furg2 = new Furgoneta("Ford","C-4322-CF",2000,500000,3);

            echo "<b>Datos coche1</b><br />";
            $coch1->impMatricula();
            $coch1->impMarca();
            $coch1->impAno();
            $coch1->impNomeCond();
            $coch1->impNomeDoc();
            $coch1->calcularIngreso();
            $coch1->impIngreso();
            
            echo "<b>Datos coche2</b><br />";
            $coch2->impMatricula();
            $coch2->impMarca();
            $coch2->impAno();
            $coch2->impNomeCond();
            $coch2->impNomeDoc();
            $coch2->calcularIngreso();
            $coch2->impIngreso();

            echo "<b>Datos Furgoneta 1</b><br />";
            $furg1->impMatricula();
            $furg1->impMarca();
            $furg1->impAno();
            $furg1->impDiñeiro();
            $furg1->impNumVixiantes();
            $furg1->calcularIngreso();
            $furg1->impIngreso();

            echo "<b>Datos Furgoneta 2</b><br />";
            $furg2->impMatricula();
            $furg2->impMarca();
            $furg2->impAno();
            $furg2->impDiñeiro();
            $furg2->impNumVixiantes();
            $furg2->calcularIngreso();
            $furg2->impIngreso();
            
            echo "<br />NUM VEHICULOS:";
            Vehiculo::impNumVehiculos();
            
            
        ?>
    </body>
    
</html>



Agora queda o problema dos ingresos totais. Isto estará almacenado noutra variable (ingresosTotal) que será unha variable de clase pois é unha información común a todos os vehículos, non é propio de cada un deles.

A continuación, crearei un método calcularIngresosTotal que estará definido na clase Vehiculos (é común a Coches e Automóbiles) o cal sumará o valor do variable ingreso (de cada vehiculo) á variable ingresosTotal.

Teño que modificar os métodos calcularIngresos das clases Coches e Furgonetas, para que chamen ao método calcularIngresos da clase Vehiculos. Unha vez feito isto creo un método para imprimir o valor da variable ingresosTotal que se chame impIngresosTotal(), definíndoo como método de clase.

Solución:


Arquivo Vehiculo.php

<?php

class Vehiculo {
    public static $numVehiculos=0;
    public static $ingresosTotais=0;
    
    public $matricula;
    public $marca;
    public $ano;
    public $ingreso;
        
    function __construct($matricula,$marca,$ano){
        $this->matricula=$matricula;
        $this->marca=$marca;
        $this->ano=$ano;
        
        Vehiculo::$numVehiculos+=1;
    }
    
    public function calcularIngresosTotais() {
        Vehiculo::$ingresosTotais+=$this->ingreso;
    }
    
    public static function impIngresosTotais() {
        printf("<div>%s</div>", Vehiculo::$ingresosTotais);
    }

    public static function impNumVehiculos() {
        printf("<div>%s</div>",  Vehiculo::$numVehiculos);
    }
    
    public function impIngreso() {
        printf("<div>%s</div>",$this->ingreso);
    }
    public function impMatricula() {
        printf("<div>%s</div>",$this->matricula);
    }
    public function impMarca() {
        printf("<div>%s</div>",$this->marca);
    }
    public function impAno() {
        printf("<div>%s</div>",$this->ano);
    }
    
}


Arquivo Coche.php

<?php

class Coche extends Vehiculo{
    
    public $nomeCond;
    public $nomeDoc;
    public $importancia;
    
    function __construct($matricula,$marca,$ano,$nomeCond,$nomeDoc,$importancia){
        parent::__construct($matricula, $marca, $ano);
        $this->nomeCond=$nomeCond;
        $this->nomeDoc=$nomeDoc;
        $this->importancia=$importancia;
        
    }

    public function calcularIngreso() {
        switch($this->importancia)
        {
            case 0:
                $this->ingreso=5000;
                break;
            case 1:
                $this->ingreso=10000;
                break;
            case 2:
                $this->ingreso=20000;
                break;
            
        }
        parent::calcularIngresosTotais(); // Tamén vale Vehiculos::calcularIngresosTotais

    }
    public function impNomeCond() {
        printf("<div>%s</div>",$this->nomeCond);
    }
    public function impNomeDoc() {
        printf("<div>%s</div>",$this->nomeDoc);
    }
}


Arquivo Furgoneta.php

<?php

class Furgoneta extends Vehiculo {
    public $diñeiro;
    public $numVixiantes;

    
    function __construct($matricula,$marca,$ano,$diñeiro,$numVixiantes){
        parent::__construct($matricula, $marca, $ano);
        $this->diñeiro=$diñeiro;
        $this->numVixiantes=$numVixiantes;
        
    }
    
    public function calcularIngreso() {
        if ($this->diñeiro<100000){
            $this->ingreso = 10000;
        }
        else {
            $this->ingreso = 25000;
        }
        parent::calcularIngresosTotais();  // Tamén vale Vehiculos::calcularIngresosTotais
    }
    public function impDiñeiro() {
        printf("<div>%s</div>",$this->diñeiro);
    }
    public function impNumVixiantes() {
        printf("<div>%s</div>",$this->numVixiantes);
    }
    
}


Arquivo principal.php

<?php
    include 'incluir-clases.inc.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Exemplos de uso de clases</title>
    </head>
    <body>
        <?php

            $coch1 = new Coche("Peugot", "C-1234-BZ",1999,"Luis se forte","PAPEL BARCENAS",1);
            $coch2 = new Coche("Fiat","C-4325-AZ",1998,"Pepe","Caso naseiro",2);
            
            $furg1 = new Furgoneta("Opel","C-2342-AF",1996,1000,1);
            $furg2 = new Furgoneta("Ford","C-4322-CF",2000,500000,3);

            echo "<b>Datos coche1</b><br />";
            $coch1->impMatricula();
            $coch1->impMarca();
            $coch1->impAno();
            $coch1->impNomeCond();
            $coch1->impNomeDoc();
            $coch1->calcularIngreso();
            $coch1->impIngreso();
            
            echo "<b>Datos coche2</b><br />";
            $coch2->impMatricula();
            $coch2->impMarca();
            $coch2->impAno();
            $coch2->impNomeCond();
            $coch2->impNomeDoc();
            $coch2->calcularIngreso();
            $coch2->impIngreso();

            echo "<b>Datos Furgoneta 1</b><br />";
            $furg1->impMatricula();
            $furg1->impMarca();
            $furg1->impAno();
            $furg1->impDiñeiro();
            $furg1->impNumVixiantes();
            $furg1->calcularIngreso();
            $furg1->impIngreso();

            echo "<b>Datos Furgoneta 2</b><br />";
            $furg2->impMatricula();
            $furg2->impMarca();
            $furg2->impAno();
            $furg2->impDiñeiro();
            $furg2->impNumVixiantes();
            $furg2->calcularIngreso();
            $furg2->impIngreso();
            
            echo "<br />NUM VEHICULOS:";
            Vehiculo::impNumVehiculos();
            
            echo "<br />INGRESOS TOTAIS:";
            Vehiculo::impIngresosTotais();
            
?>
    </body>
    
</html>



- Ben, di o PN, pero agora é cando entra en xogo isto da privacidade. Resulta que non quero que se poden modificar os valores que establacen os ingresos dos vehículos (é dicir, o diñeiro no caso das furgonetas e a importancia no caso dos coches).
Estes valores só deben ser asignados no momento da creación dos vehiculos (instanciar a clase => new) e non se debería de poder cambiarse. Para facer isto só debo modificar dicha variables como private.
Por outra banda, isto impídeme modificar os valores de dicha variables, pero nada me impide chamar tantas veces como queira ao método calcularIngresos dos Coches e as Furgonetas, incrementando o valor de ingresosTotales tantas veces como queira.
Para evitar isto, tamén declaro os métodos como private, e chamareinos desde o construtor dos Coches e Furgonetas (cando creamos os vehiculos).
Agora xa non teño que chamar ao método da forma:
$fur1->calcularIngreso();

senón que á hora de facer o new, automaticamente xa se me calcula a ganancia dese coche e da empresa.

Só teño que facer o mesmo coa variable ingresosTotales. Declaramos como private, para que non podamos modificala se non é a través dun método da propia clase (calcularIngresosTotales).
Con todo, non podo poñer como private o método calcularIngresosTotales, xa que se o fixese, non sería herdado polo que non existiría para as clases Coches e Furgonetas.
Si poderiamos poñelo como protected.


Solución:


Arquivo Coche.php

<?php

class Coche extends Vehiculo{
    
    public $nomeCond;
    public $nomeDoc;
    private $importancia;
    
    function __construct($matricula,$marca,$ano,$nomeCond,$nomeDoc,$importancia){
        parent::__construct($matricula, $marca, $ano);
        $this->nomeCond=$nomeCond;
        $this->nomeDoc=$nomeDoc;
        $this->importancia=$importancia;
        
        $this->calcularIngreso();
        
    }

    private function calcularIngreso() {
        switch($this->importancia)
        {
            case 0:
                $this->ingreso=5000;
                break;
            case 1:
                $this->ingreso=10000;
                break;
            case 2:
                $this->ingreso=20000;
                break;
            
        }
        parent::calcularIngresosTotais(); // Tamén vale Vehiculos::calcularIngresosTotais

    }
    public function impNomeCond() {
        printf("<div>%s</div>",$this->nomeCond);
    }
    public function impNomeDoc() {
        printf("<div>%s</div>",$this->nomeDoc);
    }
}


Arquivo Furgoneta.php

<?php

class Furgoneta extends Vehiculo {
    private $diñeiro;
    public $numVixiantes;

    
    function __construct($matricula,$marca,$ano,$diñeiro,$numVixiantes){
        parent::__construct($matricula, $marca, $ano);
        $this->diñeiro=$diñeiro;
        $this->numVixiantes=$numVixiantes;
        
        $this->calcularIngreso();
        
    }
    
    private function calcularIngreso() {
        if ($this->diñeiro<100000){
            $this->ingreso = 10000;
        }
        else {
            $this->ingreso = 25000;
        }
        parent::calcularIngresosTotais();  // Tamén vale Vehiculos::calcularIngresosTotais
    }
    public function impDiñeiro() {
        printf("<div>%s</div>",$this->diñeiro);
    }
    public function impNumVixiantes() {
        printf("<div>%s</div>",$this->numVixiantes);
    }
    
}


Arquivo Vehiculo.php

<?php

class Vehiculo {
    public static $numVehiculos=0;
    private static $ingresosTotais=0;
    
    public $matricula;
    public $marca;
    public $ano;
    public $ingreso;
        
    function __construct($matricula,$marca,$ano){
        $this->matricula=$matricula;
        $this->marca=$marca;
        $this->ano=$ano;
        
        Vehiculo::$numVehiculos+=1;
    }
    
    protected function calcularIngresosTotais() {
        Vehiculo::$ingresosTotais+=$this->ingreso;
    }
    
    public static function impIngresosTotais() {
        printf("<div>%s</div>", Vehiculo::$ingresosTotais);
    }

    public static function impNumVehiculos() {
        printf("<div>%s</div>",  Vehiculo::$numVehiculos);
    }
    
    public function impIngreso() {
        printf("<div>%s</div>",$this->ingreso);
    }
    public function impMatricula() {
        printf("<div>%s</div>",$this->matricula);
    }
    public function impMarca() {
        printf("<div>%s</div>",$this->marca);
    }
    public function impAno() {
        printf("<div>%s</div>",$this->ano);
    }
    
}


Arquivo principal.php

 // ELIMINAMOS AS LIÑAS SEGUINTES:
            $coch1->calcularIngreso();
            $coch2->calcularIngreso();
            $furg1->calcularIngreso();
            $furg2->calcularIngreso();




Ben, isto está de medo. Xa teño máis ou menos claro os conceptos da POO.
PD.: Aínda quedan varios conceptos que iremos vendo a medida que programemos. Só apuntar que as constantes decláranse co modificador const antes do nome e por convención debe levar todo en maiúsculas da forma:
const NOME_CONSTANTE = valor_inicial;
Seguindo o noso exercicio, facer que as cantidades que se asignan aos ingresos sexan constantes.


Solución:


Arquivo Furgoneta.php

<?php

class Furgoneta extends Vehiculo {
    const INGRESO_CARTOS_MENOR_10000 = 10000;
    const INGRESO_CARTOS_MAIOR_25000 = 25000;

    private $diñeiro;
    public $numVixiantes;

    
    function __construct($matricula,$marca,$ano,$diñeiro,$numVixiantes){
        parent::__construct($matricula, $marca, $ano);
        $this->diñeiro=$diñeiro;
        $this->numVixiantes=$numVixiantes;
        
        $this->calcularIngreso();
        
    }
    
    private function calcularIngreso() {
        if ($this->diñeiro<100000){
            $this->ingreso = Furgoneta::INGRESO_CARTOS_MENOR_10000;
        }
        else {
            $this->ingreso = Furgoneta::INGRESO_CARTOS_MAIOR_25000;
        }
        parent::calcularIngresosTotais();  // Tamén vale Vehiculos::calcularIngresosTotais
    }
    public function impDiñeiro() {
        printf("<div>%s</div>",$this->diñeiro);
    }
    public function impNumVixiantes() {
        printf("<div>%s</div>",$this->numVixiantes);
    }
    
}


Arquivo Coche.php

<?php

class Coche extends Vehiculo{
    const INGRESO_IMPORTANCIA_0 = 5000;
    const INGRESO_IMPORTANCIA_1 = 10000;
    const INGRESO_IMPORTANCIA_2 = 20000;
    
    public $nomeCond;
    public $nomeDoc;
    private $importancia;
    
    function __construct($matricula,$marca,$ano,$nomeCond,$nomeDoc,$importancia){
        parent::__construct($matricula, $marca, $ano);
        $this->nomeCond=$nomeCond;
        $this->nomeDoc=$nomeDoc;
        $this->importancia=$importancia;
        
        $this->calcularIngreso();
        
    }

    private function calcularIngreso() {
        switch($this->importancia)
        {
            case 0:
                $this->ingreso=Coche::INGRESO_IMPORTANCIA_0;
                break;
            case 1:
                $this->ingreso=Coche::INGRESO_IMPORTANCIA_1;
                break;
            case 2:
                $this->ingreso=Coche::INGRESO_IMPORTANCIA_2;
                break;
            
        }
        parent::calcularIngresosTotais(); // Tamén vale Vehiculos::calcularIngresosTotais

    }
    public function impNomeCond() {
        printf("<div>%s</div>",$this->nomeCond);
    }
    public function impNomeDoc() {
        printf("<div>%s</div>",$this->nomeDoc);
    }
}


Nota:

  • A partires de PHP7 xa podemos engadir un modificador de visibilidade (public, private, protected) ás constantes.
  • Unha alternativa sería definir unha variable de clase privada. Por exemplo: private static $INGRESO_IMPORTANCIA_2=20000;

Instanceof

  • Crea nun arquivo 'include' de nome 'funcions.inc.php' as seguintes funcións:
  • Agora imos modificar a clase Principal:
  • Definimos un array de Vehiculos onde gardaremos todos os coches/furgonetas creadas.
  • Imos crear unha función de nome imprimirVehiculos($datos), dentro do arquivo de include, e chamaremos a dita función dende a clase Principal. Queremos que amose os datos dos vehiculos enviados como parámetro, pero no caso de que o obxecto actual sexa unha furgoneta terá que amosar o texto "Os datos da furgoneta son:" e no caso de que sexa un coche "Os datos do coche son:". Fai un printf do obxecto.
  • Engade o método máxico __toString para que amose todos os datos do obxecto.
Chamar a dita función dende a clase Principal.


Solución:


Arquivo funcions.inc.php

<?php

function imprimirVehiculos($datos){
    
    foreach ($datos as $veh){
        
        if ($veh instanceof Coche){
            echo "<br /> Os datos do coche son:";
        }
        elseif ($veh instanceof Furgoneta){
            echo "<br /> Os datos da furgoneta son:";
        }
        printf("%s",$veh);
    }
    
}


Arquivo Furgoneta.php

<?php

class Furgoneta extends Vehiculo {
    const INGRESO_CARTOS_MENOR_10000 = 10000;
    const INGRESO_CARTOS_MAIOR_25000 = 25000;
    
    private $diñeiro;
    public $numVixiantes;
    
    function __construct($matricula,$marca,$ano,$diñeiro,$numVixiantes){
        parent::__construct($matricula, $marca, $ano);
        $this->diñeiro=$diñeiro;
        $this->numVixiantes=$numVixiantes;
        
        $this->calcularIngreso();
        
    }
    
    private function calcularIngreso() {
        if ($this->diñeiro<100000){
            $this->ingreso = Furgonetas::INGRESO_CARTOS_MENOR_10000;
        }
        else {
            $this->ingreso = Furgonetas::INGRESO_CARTOS_MAIOR_25000;
        }
        parent::calcularIngresosTotais();  // Tamén vale Vehiculos::calcularIngresosTotais
    }
    public function impDiñeiro() {
        printf("<div>%s</div>",$this->diñeiro);
    }
    public function impNumVixiantes() {
        printf("<div>%s</div>",$this->numVixiantes);
    }
    
    public function __toString() {
        $cadea=$this->impMatricula() . "<br />" . $this->impMarca() . "<br />" . 
                $this->impAno() . "<br />" . $this->impDiñeiro() . "<br />" . 
                $this->impNumVixiantes() . "<br />" . $this->impIngreso();

        return $cadea;
        
    }
    
}


Arquivo Coche.php

<?php

class Coche extends Vehiculo{
    const INGRESO_IMPORTANCIA_0 = 5000;
    const INGRESO_IMPORTANCIA_1 = 10000;
    const INGRESO_IMPORTANCIA_2 = 20000;
    
    public $nomeCond;
    public $nomeDoc;
    private $importancia;
    
    function __construct($matricula,$marca,$ano,$nomeCond,$nomeDoc,$importancia){
        parent::__construct($matricula, $marca, $ano);
        $this->nomeCond=$nomeCond;
        $this->nomeDoc=$nomeDoc;
        $this->importancia=$importancia;
        
        $this->calcularIngreso();
        
    }

    private function calcularIngreso() {
        switch($this->importancia)
        {
            case 0:
                $this->ingreso=Coches::INGRESO_IMPORTANCIA_0;
                break;
            case 1:
                $this->ingreso=Coches::INGRESO_IMPORTANCIA_1;
                break;
            case 2:
                $this->ingreso=Coches::INGRESO_IMPORTANCIA_2;
                break;
            
        }
        parent::calcularIngresosTotais(); // Tamén vale Vehiculos::calcularIngresosTotais

    }
    public function impNomeCond() {
        printf("<div>%s</div>",$this->nomeCond);
    }
    public function impNomeDoc() {
        printf("<div>%s</div>",$this->nomeDoc);
    }
    public function __toString() {
        $cadea=$this->impMatricula() . "<br />" . $this->impMarca() . "<br />" . 
                $this->impAno() . "<br />" . $this->impNomeDoc(). "<br />" . 
                $this->impNomeCond();

        return $cadea;
    }
}


Arquivo principal.php

<?php
    include 'incluir-clases.inc.php';
    include 'funcions.inc.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Exemplos de uso de clases</title>
    </head>
    <body>
        <?php

            ...........................

            
            $vehiculos = array();
            $vehiculos[]=$coch1;
            $vehiculos[]=$coch2;
            $vehiculos[]=$furg1;
            $vehiculos[]=$furg2;
            
           printf("<h1>Amosando os datos do array</h1>");
           imprimirVehiculos($vehiculos);
           
?>
    </body>
    
</html>

Operacións con obxectos

  • Engade ao arquivo 'include' de nome 'funcions.inc.php' as seguintes funcións:
  • Crea unha función de nome 'clonar($obx)' que leve un parámetro que será o obxecto a clonar. Se dito obxecto é unha Furgoneta, cambiaralle a numVixiantes e a poñerá a 0, e a matrícula á cadea 'BALEIRO", devolvendo o obxecto modificado. En calquera outro caso devolverá o obxecto clonado.
Inclúe dito arquivo na clase Principal e proba a clonar un obxecto e comprobade que os datos están modificados no caso de clonar unha Furgoneta e que sexan os mesmos en caso de clonar un Coche.




Solución:

Arquivo funcions.inc.php

<?php

function imprimirVehiculos($datos){
    
    foreach ($datos as $veh){
        
        if ($veh instanceof Coches){
            echo "<br /> Os datos do coche son:";
        }
        elseif ($veh instanceof Furgonetas){
            echo "<br /> Os datos da furgoneta son:";
        }
        printf("%s",$veh);
    }
    
}

function clonar($obj){
    $clon = clone($obj);
    if ($obj instanceof Furgoneta){
        $clon->matricula="BALEIRO";
        $clon->numVixiantes=0;
    }
    return $clon;
}


Arquivo principal.php

<?php
    include 'incluir-clases.inc.php';
    include 'funcions.inc.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Exemplos de uso de clases</title>
    </head>
    <body>
        <?php

           .........................


           
           $clonCoche = clonar($coch1);
           $clonFur = clonar($furg1);
           
           if ($clonCoche==$coch1){
               echo "<br />Os datos do coche e o seu clon son iguais";
           }
           
           if ($clonFur!=$furg1){
                echo "<br />Os datos da furgonenta e o seu clon NO son iguais";
                echo $furg1;
                echo $clonFur;
           }
?>
    </body>
    
</html>

Clases abstractas

  • Crea unha clase abstracta de nome Transportes no que estea definido un método abstracto de nome amosarPercorrido. Dito método terá que amosar a ruta que segue un determinado vehículo.
  • Fai que Vehiculos derive de dita clase. A ruta estará gardada nun array asociativo por ruta, no que se gardan as paradas que terá o vehículo. Por exemplo: 'ruta1' => 'Parada 1', 'Parada2',....; 'ruta2' => 'Parada21', 'Parada22',...



Solución:

Arquivo Transporte.php

<?php

abstract class Transporte {
    
    abstract function amosarPercorrido();
    
}


Arquivo Vehiculo.php

<?php

class Vehiculo extends Transporte{
    public static $numVehiculos=0;
    private static $ingresosTotais=0;
    
    private $rutas=array('Ruta 1'=>array('C/ Rua 1_1','C/ Rua 1_2'), 'Ruta 2' => array('C/ Rua 2_1','C/ Rua 2_2'));
    public $matricula;
    public $marca;
    public $ano;
    public $ingreso;
        
    function __construct($matricula,$marca,$ano){
        $this->matricula=$matricula;
        $this->marca=$marca;
        $this->ano=$ano;
        
        Vehiculos::$numVehiculos+=1;
    }
    
    public function amosarPercorrido(){
        
        foreach ($this->rutas as $key=>$ruta){
            printf("<br />%s",$key);
            foreach ($ruta as $rua){
                printf("<br /> ----->%s",$rua);
            }
        }
    }
    
    protected function calcularIngresosTotais() {
        Vehiculos::$ingresosTotais+=$this->ingreso;
    }
    
    public static function impIngresosTotais() {
        printf("<div>%s</div>", Vehiculos::$ingresosTotais);
    }

    public static function impNumVehiculos() {
        printf("<div>%s</div>",  Vehiculos::$numVehiculos);
    }
    
    public function impIngreso() {
        printf("<div>%s</div>",$this->ingreso);
    }
    public function impMatricula() {
        printf("<div>%s</div>",$this->matricula);
    }
    public function impMarca() {
        printf("<div>%s</div>",$this->marca);
    }
    public function impAno() {
        printf("<div>%s</div>",$this->ano);
    }
    
}


Arquivo principal.php

           ....................


           $coch1 = new Coche("Peugot", "C-1234-BZ",1999,"Luis se forte","PAPEL BARCENAS",1);
           ....................

           $coch1->amosarPercorrido();
?>
    </body>
    
</html>

Namespaces

  • Facer que as clases de Vehiculos, Coches, Furgonetas e Transportes estean nun namespace de nome 'Transportes'.
Facede o include individual de cada clase que necesitades e non utilicedes o arquivo de include 'IncluirClases.inc.php'.




Solución Arquivo: Transporte.php

<?php
namespace Transportes;

abstract class Transporte {
    
    abstract function amosarPercorrido();
    
}


Arquivo: Vehiculo.php

<?php

namespace Transportes;
require 'Transporte.php';

class Vehiculo extends Transporte{
..........

Arquivo: Coche.php

<?php

namespace Transportes;
require_once 'Vehiculo.php';

class Coche extends Vehiculo{
..........

Arquivo: Furgoneta.php

<?php
namespace Transportes;
require_once 'Vehiculo.php';

class Furgoneta extends Vehiculo {
............

Arquivo: Principal.php

<?php
// Lembrar quitar isto cando funcione
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);  // Non debería ser necesario xa que amosa posibles erros de configuración no php.ini ou erros antes da execución do script, pero podemos poñelo por se acaso.
error_reporting(E_ALL);

   // include 'incluir-clases.inc.php';
    include 'funcions.inc.php';

    require_once 'Coche.php';
    require_once 'Furgoneta.php';
    require_once 'Vehiculo.php';

    use Transportes\Coche;
    use Transportes\Furgoneta;
    use Transportes\Vehiculo;

..................
?>





Exercicio proposto:

  • Fai que a clase Vehiculos estea definido nun espazo de nome 'Transportes'
  • Fai que a clase Coches e Furgonetas estea definidas nun espazo de nome 'Transportes\Terrestres'
Cambia a clase Principal para facer uso destas clases.
Utiliza un alias para 'Transportes\Terrestres' de nome 'Terrestres'.




Variante do exercicio anterior:

Volve a empregar a función sql_autoload_register.

Nota Importante: Como estamos a utilizar a función sql_autoload_register cando facemos un new dunha clase que se atopa nun espazo de nome, poñemos $a = new Espazo\Clase() e polo tanto á función 'cargadorClases($class):

<?php
    function cargadorClases($class) {
        echo $class;
        require $class . '.php';
    }

    spl_autoload_register('ca
Vai chegarlle 'Espazo\Clase' e cando faga o require terá: require Espazo\Clase.php;
Como vemos intentará buscar a clase dentro dun cartafol de nome Espazo (que non existe).
  • Para resolvelo podemos crear unha estrutura de cartafoles igual ao espazo de nomes.





-- Ángel D. Fernández González -- (2024).