Vamos supor uma classe (primeira classe) com o nome ContaBanco, que pode ser criada por várias pessoas (portando, teria várias contas), com funções básicas como sacar, abrir e fechar contas, pagar mensalidades, etc. Ela teria um formato parecido com esses atributos e métodos:
| ContaBanco |
|---|
|
+ numConta: int # tipo: String - dono: String - saldo: float - status: boolean |
|
+ abrirConta(t: String): void + fecharConta(): void + depositar(v: float): void + sacar(v: float): void + pagarMensal(): void |
Crie um arquivo de classe PHP com o nome ContaBanco.php, e coloque esse código:
class ContaBanco {
public $numConta;
protected $tipo;
private $dono;
private $saldo;
private $status;
public function abrirConta($t) {
$this->setTipo($t);
$this->setStatus(true);
if($t == "CC") {
$this->setSaldo(50);
}
else if($t == "CP") {
$this->setSaldo(150);
}
echo "<p>Conta aberta com sucesso!</p>";
}
public function fecharConta() {
if($this->getSaldo() > 0) {
echo "<p>A conta ainda tem dinheiro, não podemos fechá-la!</p>";
}
else if($this->getSaldo() < 0) {
echo "<p>A conta está em débito, não podemos fechá-la!</p>";
}
else {
$this->setStatus(false);
echo "<p>A conta de {$this->getDono()} foi encerrada com sucesso!</p>";
}
}
public function depositar($v) {
if($this->getStatus()) {
$this->setSaldo($this->getSaldo() + $v);
// $this->saldo = $this->saldo + $v;
echo "<p>Depósito de R\${$v} autorizado na conta de {$this->getDono()}.</p>";
// O \$ é pra não conflitar com o $ da variável.
}
else {
echo "<p>Conta Fechada ou Inexistente!</p>";
}
}
public function sacar($v) {
if($this->getStatus()) {
if($this->getSaldo() >= $v) {
$this->setSaldo($this->getSaldo() - $v);
// $this->saldo = $this->saldo - $v;
echo "<p>Saque de R\${$v} autorizado na conta de {$this->getDono()}.</p>";
}
else {
echo "<p>Saldo insuficiente para saque!</p>";
}
}
else {
echo "<p>Essa conta tá fechada ou não existe!</p>";
}
}
public function pagarMensal() {
if($this->getTipo() == "CC") {
$v = 12;
}
else if($this->getTipo() == "CP") {
$v = 20;
}
// Outra condição
if($this->getStatus()) {
$this->setSaldo($this->getSaldo() - $v);
echo "<p>Mensalidade de R\${$v} debitada da conta de {$this->getDono()}.</p>";
}
else {
echo "<p>Problemas na conta, não podemos cobrar.</p>";
}
}
// Especiais:
public function __construct() {
$this->setSaldo(0);
$this->setStatus(false);
echo "<p>Conta criada com sucesso.</p>"; // Apague depois. Não é recomendado colocar echo aqui, é só pra testar.
}
public function __destruct() {
echo "Objeto ContaBanco Destruído!<br/>";
}
public function getNumConta() {
return $this->numConta;
}
public function getTipo() {
return $this->tipo;
}
public function getDono() {
return $this->dono;
}
public function getSaldo() {
return $this->saldo;
}
public function getStatus() {
return $this->status;
}
public function setNumConta($numConta) {
$this->numConta = $numConta;
}
public function setTipo($tipo) {
$this->tipo = $tipo;
}
public function setDono($dono) {
$this->dono = $dono;
}
public function setSaldo($saldo) {
$this->saldo = $saldo;
}
public function setStatus($status) {
$this->status = $status;
}
}
PS: Prefira usar métodos setter do que mexer direto nos atributos.
Com esse código pronto, vá no index e coloque esses comandos pra criar os objetos:
require_once "ContaBanco.php";
$p1 = new ContaBanco();
$p2 = new ContaBanco();
$p1->abrirConta("CC");
$p1->setNumConta(1111);
$p1->setDono("Jubileu");
$p2->abrirConta("CP");
$p2->setNumConta(2222);
$p2->setDono("Creuza");
print_r($p1);
print_r($p2);
unset($p1);
unset($p2);
Para depositar, basta colocar isso, no final do código, antes do print_r():
$p1->depositar(300);
$p2->depositar(500);
Para sacar, da mesma forma:
$p2->sacar(100);
E cobrar mensalidade também:
$p1->pagarMensal();
$p2->pagarMensal();
Tente outros números, principalmente na parte de sacar. E tente as outras funções.
Paralelamente, podemos fazer um exemplo apenas com métodos e atributos estáticos:
class Ventilador {
private static $ligado = false;
private static $velocidade = 0;
public static function ligar($vel) {
self::$ligado = true;
if($vel < 1) {
$vel = 1;
}
else if($vel > 3) {
$vel = 3;
}
self::$velocidade = $vel;
printf("O ventilador está ligado, na velocidade de %d.<br/>", self::$velocidade);
}
public static function desligar() {
self::$velocidade = 0;
self::$ligado = false;
echo "O ventilador está desligado!<br/>";
}
public static function getLigado() {
return self::$ligado;
}
public static function getVelocidade() {
return self::$velocidade;
}
public static function setLigado($ligado) {
self::$ligado = $ligado;
}
public static function setVelocidade($velocidade) {
self::$velocidade = $velocidade;
}
}
E no programa principal:
require_once "Ventilador.php";
Ventilador::ligar(3);
Ventilador::ligar(2);
Ventilador::desligar();
echo (Ventilador::getLigado() ? "true" : "false") . "<br/>";
Como sabemos, a POO tem três pilares, Encapsulamento, Herança e Polimorfismo (representados pelas letras EHP). O primeiro que vamos tratar é o encapsulamento.
PS: Alguns consideram a Abstração um dos pilares também, o primeiro deles, mas na verdade ele está dentro do encapsulamento.
Imagine que o carro, uma pilha ou qualquer outra coisa esteja numa "cápsula", no exemplo, vamos usar um controle remoto. O código estando encapsulado, ele está protegido internamente (como uma cápsula de remédio mesmo), deixando o que é pra ser privado "blindado" do usuário, e o que é público com contato permitido.
A pilha também é um ótimo exemplo de encapsulamento, onde os componentes químicos que geram a eletricidade estão protegidos do contato humano (para proteger tanto os componentes quanto a pessoa), mas as partes metálicas estão disponíveis para fornecer energia ao produto. Além disso, a pilha sempre tem um formato padrão (tipo o AAA) para servir em vários produtos, independente da funcionalidade do produto. Mesmo se o componente interno da pilha for diferente (alcalina, comum, recarregável, etc.).
No exemplo que usaremos, o controle remoto, a "cápsula" seria a capa que permite que nós interaja com ele, e o código seria os circuitos que operam isso, mas que não podemos tocar diretamente por segurança.
O código "encapsulado" tem o mesmo padrão, e protege o código do usuário e vice-versa, impedindo que o programador cometa erros e prejudique o programa como um todo, independente do código encapsulado (ele só verificará as informações necessárias, sem precisar saber como ele funciona exatamente).
Em outras palavras, encapsular é ocultar partes independentes da implementação, permitindo construir partes invisíveis ao mundo exterior.
Basicamente, o uso de atributos privados e o acesso a eles ser feito através de métodos (como os getters e setters), já é o encapsulamento. Mas podemos fazer isso a nível de classe, usando uma interface, que obrigará a classe a ter todos os métodos descritos nesta interface e usar somente eles, além de proteger os atributos da classe implementadora do programa principal.
Para criar um exemplo disso com um controle remoto, temos que definir uma interface (seria a "cápsula" do controle com os botões), de forma parecida com uma classe, mas sem atributos, e todos os métodos são públicos. E a classe ControleRemoto (como os circuitos encapsulados pela interface) terá os atributos e os métodos getters e setters privados (é o passo principal pra encapsular). Veja o que faremos no PHP:
Crie uma nova Interface em PHP com o nome Controlador.php com esse código:
interface Controlador {
public function ligar();
public function desligar();
public function abrirMenu();
public function fecharMenu();
public function maisVolume();
public function menosVolume();
public function ligarMudo();
public function desligarMudo();
public function play();
public function pause();
}
PS: Os métodos abstratos significam que o método não será desenvolvido na interface, e sim na classe. É como um aviso pra interface que existe um método (por exemplo, de abrir, aumentar volume, etc.), mas a interface não precisa e nem sabe como o código funciona na classe, apenas esta que tem que implementar (executar). Se um método a ser implementado numa classe for estático, ele não pode ser indicado na interface. Interfaces agem de forma parecida com uma classe abstrata, só que sem implementações e sem atributos, apenas a assinatura dos métodos, que são abstratos por padrão, dispensando a indicação de abstract neles.
Crie também a classe ControleRemoto.php, assim (os atributos deverão estar privados, os métodos getter e setter não são obrigatórios ser):
require_once "Controlador.php";
class ControleRemoto implements Controlador {
private $volume;
private $ligado;
private $tocando;
// Métodos especiais.
public function __construct() {
$this->volume = 50;
$this->ligado = false;
$this->tocando = false;
}
public function ligar() {
$this->setLigado(true);
}
public function desligar() {
$this->setLigado(false);
}
public function abrirMenu() {
echo "<br/>Está ligado? " . ($this->getLigado() ? "SIM" : "NÃO");
echo "<br/>Está tocando? " . ($this->getTocando() ? "SIM" : "NÃO");
echo "<br/>Volume: " . $this->getVolume();
for($i = 0; $i < $this->getVolume(); $i += 2) {
echo "|";
}
echo "<br/>";
}
public function fecharMenu() {
echo "<br/>Fechando menu...";
}
public function maisVolume() {
if($this->getLigado()) {
if($this->getVolume() < 100) {
$this->setVolume($this->getVolume() + 2);
}
}
else {
echo "<br/>ERRO! Está desligado, não posso aumentar o volume!";
}
}
public function menosVolume() {
if($this->getLigado()) {
if($this->getVolume() > 0) {
$this->setVolume($this->getVolume() - 2);
}
}
else {
echo "<br/>ERRO! Está desligado, não posso diminuir o volume!";
}
}
public function ligarMudo() {
if($this->getLigado() && $this->getVolume() > 0) {
$this->setVolume(0);
}
}
public function desligarMudo() {
if($this->getLigado() && $this->getVolume() == 0) {
$this->setVolume(50);
}
}
public function play() {
if($this->getLigado() && !($this->getTocando())) {
$this->setTocando(true);
}
}
public function pause() {
if($this->getLigado() && $this->getTocando()) {
$this->setTocando(false);
}
}
private function setVolume($volume) {
$this->volume = $volume;
}
private function getVolume() {
return $this->volume;
}
private function setLigado($ligado) {
$this->ligado = $ligado;
}
private function getLigado() {
return $this->ligado;
}
private function setTocando($tocando) {
$this->tocando = $tocando;
}
private function getTocando() {
return $this->tocando;
}
}
Lembrando que, como poderemos usar apenas os métodos descritos na interface Controlador, os getters e setters de ControleRemoto não estariam acessíveis no programa principal, mesmo se estiverem públicos, isso garante o encapsulamento à nível de classe (que não faz muita diferença no PHP por ser uma linguagem dinamicamente tipada).
PS: Usamos implements quando precisamos implementar uma interface, sendo obrigado executar todos os métodos descritos nela, é possível uma classe implementar mais de uma interface, com os nomes separados por vírgulas, usando algo como class NomeDaClasse implements NomeDaInterface1, NomeDaInterface2.
E para criar o controle, fazemos assim no index:
require_once "ControleRemoto.php";
$c = new ControleRemoto();
$c->ligar();
$c->maisVolume(); // Aqui podemos colocar os outros métodos.
$c->abrirMenu();
Como exemplo, crie outros exemplos e funções baseados nesse controle.
Essa é a vantagem do encapsulamento, ele protege o código contra ações externas e internas, e torna a forma interna de implementação invisível pro "lado de fora" (sabemos que quem mexe nela sabe como usar as funções, mas não como funcionam, até os métodos getter e setter são privados por isso).
Em outras palavras, as interfaces estabelecem contratos entre as classes que as implementam, garantindo que elas tenham um certo comportamento.
As interfaces também garantem o encapsulamento, já que podemos usar somente os métodos descritos na interface nos objetos, e não permitir a visualização de atributos da classe implementadora. Só que isso faz mais diferença em linguagens fortemente tipadas, como o Java, o que não é o caso do PHP.