Olá pessoal, no artigo de hoje vamos falar sobre classes abstratas em PHP.

Uma classe abstrata é basicamente uma classe que tem métodos abstratos, mas afinal, o que são métodos abstratos?

Métodos abstratos são os métodos que estão declarados em uma classe, mas não são definidos na classe o código desses métodos.

Agora vamos ver com um pouco mais de calma uma explicação um pouco mais detalhada disso tudo.

Pensemos nos produtos lácteos (os derivados do leite). Sabemos que os produtos lácteos são uma grande família. Incluem os iogurtes, manteigas, queijos, sorvetes e inclusive o próprio leite. Entretanto, os produtos lácteos em si não são encontrados na vida real nos supermercados. Não é possível comprar um quilo de produto lácteo, mas sim um litro de leite, uma caixa de sorvete o um pote de iogurtes.

Resumindo, podemos ter um conjunto de objetos que têm características comuns e funcionalidades, também comuns, mas que diferem na maneira de serem realizadas. Isto está a abstração.

Neste caso, a classe produto lácteo terá os métodos abstratos conservar-se() e consumir-se(), mas não se especificará o código fonte destes métodos (por isso são abstratos). As classes que herdem de produto lácteo serão as encarregadas de definir um código para os métodos definidos como abstratos na classe pai. Assim, cada classe que herde de produto lácteo, deverá especificar o mecanismo concreto e específico pelo qual vão ser conservados ou consumidos.

Toda classe que incorpora algum método abstrato deve ser declarada como abstrata. As classes abstratas não podem ser instanciadas, ou seja, não podemos criar objetos a partir delas. Pensemos nos produtos lácteos, estes não existem a não ser como uma ideia geral. Só poderemos encontrar produtos lácteos de um tipo em concreto, como leite ou iogurte, mas não a ideia de produto lácteo em geral.

Uma classe que herde de um produto lácteo deve definir os métodos abstratos declarados na classe abstrata. Do contrário, a classe que herda seria obrigada a ser declarada como abstrata. Quando uma classe herda uma classe abstrata, todos os métodos marcados como abstratos na declaração da classe pai devem ser definidos na classe filha e esses métodos devem ser definidos com a mesma visibilidade.

Por exemplo, se um método abstrato é definido como protected, a implementação da função deve ser definida ou como protected ou public, mas não private. É extremamente importante que as assinaturas dos métodos coincidirão, ou seja, as induções de tipos e o número de argumentos requeridos devem ser os mesmos. Isto também se aplica aos construtores a partir do PHP 5.4.

A sintaxe da abstração

Para declarar classes e métodos abstratos devemos utilizar uma sintaxe simples e que pode ser vista na listagem 1.


<?php
abstract class nome__da_classe{ 

  //propriedades 
public x; 
private y; 

  //métodos 

  public function __construct(){ 
   … 
  } 

  public abstract function nome_do_metodo(); 

} 
?> 
Listagem 1. Sintaxe da Classe Abstrata

No código acima podemos ver que foi utilizada a palavra chave "abstract" para definir as classes ou métodos abstratos. Como podemos ver os métodos abstratos não levam nenhum código associado, nem mesmo as chaves para abrir e fechar o método, como mostra a listagem 2.


public function __construct(){ 
   … 
  } 

  public abstract function nome_metodo(); 

} 
Listagem 2. Método abstrato

Vejamos então um exemplo mais amplo sobre as classes abstratas.


<?php
abstract class ClasseAbstrata
{
    // Força a classe que estende ClasseAbstrata a definir esse método
    abstract protected function pegarValor();
    abstract protected function valorComPrefixo( $prefixo );

    // Método comum
    public function imprimir() {
        print $this->pegarValor();
    }
}

class ClasseConcreta1 extends ClasseAbstrata
{
    protected function pegarValor() {
        return "ClasseConcreta1";
    }

    public function valorComPrefixo( $prefixo ) {
        return "{$prefixo}ClasseConcreta1";
    }
}

class ClasseConcreta2 extends ClasseAbstrata
{
    protected function pegarValor() {
        return "ClasseConcreta2";
    }

    public function valorComPrefixo( $prefixo ) {
        return "{$prefixo}ClasseConcreta2";
    }
}

$classe1 = new ClasseConcreta1;
$classe1->imprimir();
echo $classe1->valorComPrefixo('FOO_') ."\n";

$classe2 = new ClasseConcreta2;
$classe2->imprimir();
echo $classe2->valorComPrefixo('FOO_') ."\n";
?>
Listagem 3. Exemplo de classe abstrata

O exemplo do código acima imprime o seguinte resultado:


ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2

Diferença entre Classes e métodos abstratos e finais

Agora vamos ver alguns exemplos de classes e métodos abstratos e classes e métodos finais.


<?php
abstract class Cheque
{
	private $valor;

	// ... outros métodos da classe

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

	public function getValor()
	{
		return $this->valor;
	}

	public function calculaJuros()
	{
		return $this->valor; // não há juros
	}
}

class ChequeComum extends Cheque
{
	public function calculaJuros()
	{
		return $this->valor * 1.25; // calcula 25% em cima do valor;
	}
}

class ChequeEspecial extends Cheque
{
	public function calculaJuros()
	{
		return $this->valor * 1.10; // calcula 10% em cima do valor;
	}
}

/*
* Ao tentar instanciar a classe irá causar um Fatal Error, 
pois uma classe abstrata não pode ser instanciada.
* 
*/
$cheque = new Cheque();
?>
Listagem 4. Classe Abstrata

Também existem casos onde não queremos que uma classe se estenda por mais alguma outra class, para isso é preciso definir a classe como final.

Para fazer isso é preciso utilizar a palavra-chave com o mesmo nome, final. Vamos supor que a classe ContaEspecial não pode ser estendida, porém alguém tentou de qualquer forma fazer isso:


<?php
final class ChequeEspecial extends Cheque
{
	public function calculaJuros()
	{
		return $this->valor * 1.10; // calcula 10% em cima do valor;
	}
}

/**
       * Ao ver que estamos tentando herdar uma classe final,  
       * o PHP lança um Fatal Error e para a execução do código  
       */  
class ChequeContaUniversitaria extends ChequeEspecial
{
	public function calculaJuros()
	{
		return $this->valor * 1.01; // calcula 1% em cima do valor;
	}
}

// Este código não vai nem ser executado
$cheque = new ChequeContaUniversitaria();
?>
Listagem 5. Classe final

Métodos abstratos

Da mesma forma que acontece com as classes, os métodos abstratos são criados apenas para ajudarem a estruturar as classes filhas.


<?php
abstract class Cheque
{
	// outros métodos

	abstract function calculaJuros();
}

class ChequeContaUniversitaria extends Cheque
{
	/**
           * Esta classe não precisa de calculo de juros.  
           * Mas se este método não for definido o PHP dispara um Fatal Error,  
           * pois na classe pai esse método está como abstrato.  
           */  
	public function calculaJuros()
	{
		return $this->valor; // não há juros;
	}
}
?>
Listegem 6. Métodos abstratos

Repare que na classe Cheque o método calculaJuros() não é escrito, possuindo apenas a sua assinatura. Isso é acontece em métodos abstratos.

Métodos finais

Quando um método é dito como final ele não pode ser sobrescrito ou seja, a classe filha continua tento acesso a ele, mas não pode alterá-lo.


<?php
abstract class Cheque
{
	final function calculaTotal()
	{
		// ...
	}
}

class ChequeContaUniversitaria extends Cheque
{
	/**
           * Isto retorna um Fatal Error  
           */  
	public function calculaTotal()
	{
		return 0.0;
	}
}
?>
Listegem 7. Métodos finais

Conclusão

Nesse artigo foi falado sobre classes e métodos abstratos, espero que tenha ficado claro e que tenham entendido algumas diferenças desses métodos muito utilizados na Orientação a Objetos.