Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

João Batista Neto

1.3 - Visibilidade e Encapsulamento

Recommended Posts

Anteriormente falamos sobre como construir um objeto e sobre herança de classes, falamos também que usamos herança de classes para compartilhar código entre os herdeiros, mostrei que uma classe é composta por propriedades (características) e métodos (comportamento) e ilustrei que tanto as propriedades quando os métodos possuem um modificador de visibilidade.

O fato é que em programação orientada a objetos, a ocultação de informações é um dos aspectos mais importantes, imagine a seguinte situação:

"Fulano: Destranque a porta, abra-a e vá para o outro lado"

Quando Fulano for destrancar a porta ele simplesmente enfiará a chave correta no orifício específico, girará e terá a porta destrancada.

Fulano não precisa saber como a porta será destrancada, na verdade, ele não quer sequer saber que dentro da fechadura possui todo um mecanismo que somente destrancará se a chave possuir o segredo correto.

Em orientação a objetos, podemos chamar a fechadura de objeto que possui uma operação destrancar que somente aceita um objeto do tipo chave com o segredo correto, a lógica do mecanismo de tranca é oculto e nem o participante Fulano nem o participante chave precisam saber como o mecanismo funciona. Essa ocultação de informação chama-se encapsulamento.

Para se conseguir ocultar informações, usamos os modificadores de visibilidade, tanto para os métodos quanto para as propriedades, esses modificadores são três:

public:

A visibilidade pública (public) fará com que não haja ocultação nenhuma, toda propriedade ou método declarado com public serão acessíveis por todos que quiserem acessá-los, utiliza-se public para os métodos de interface, isso é, as operações que queremos que outros possam executar em nossos objetos.

private:

 

Ao contrário do public, esse modificador faz com que qualquer método ou propriedade seja visível só e somente só pela classe que a declarou, ninguém terá acesso a essas propriedades ou métodos diretamente, o acesso será possível somente através de métodos de interface.

protected:

 

 

A visibilidade protegida é como um mix da pública com a privada. Quando falamos sobre herança de classes e como os descendentes herdam características e comportamento de seus pais, a visibilidade protected faz com que todos os herdeiros vejam as propriedades ou métodos protegidos como se fossem públicos porém, do lado de fora, um outro objeto não conseguirá acessar diretamente essas informações já que, do lado de fora é como se fosse privada. Então, dentro da classes que declarante e todos seus herdeiros, um método ou propriedade é visível e fora da classes não.


Voltando a nossa fechadura, imaginem se o segredo para abrir a tranca fosse public, a fechadura simplesmente perderia o sentido de existir já que qualquer bandido viria, copiaria o segredo, confeccionaria uma chave e abriria nossas portas e também não tem sentido do segredo ser protected porque a fechadura é uma classe final, já que você não criará outras coisas com ela.

Vamos ver como ficaria essa situação em PHP:

class Chave {
public $segredo;
}



Nesse caso específico, vamos deixar o segredo da chave público, por uma mera questão de ilustração, já que, basta pegar uma chave qualquer para vermos o segredo dela.

final class Fechadura {
private $segredo = 123456;

public function destranca( Chave $chave ){
if ( $chave->segredo == $this->segredo ){
echo 'Destrancado';
} else {
echo 'Chave inválida';
}
}
}



Já no caso da fechadura, o segredo é privado, só a Fechadura poderá acessar essa propriedade através da variável especial $this. De fato, essa variável é muitíssimo utilizada em orientação a objetos no PHP, ela faz referência ao próprio objeto. Usar $this é o mesmo que o objeto dizer Eu Mesmo, então, quando queremos acessar um método ou propriedade do lado de dentro do objeto usaremos essa variável especial.

Dois detalhes novos aparecem na nossa classe Fechadura, o primeiro e a palavra chave final antes de class, ao utilizar final estamos dizendo que não será possível utilizar herança de classes com essa classe específica e que qualquer ocorrência de Fechadura tratar-se-a dessa Fechadura específica.
O segundo detalhe é o uso do Chave antes do parâmetro $chave do método destranca, isso fará com que essa operação aceite só e somente só parâmetros do tipo Chave, isso chama-se type hinting ou dica de tipo e instruirá o método destranca a rejeitar qualquer coisa que não for uma Chave.

Agora, vamos tentar destrancar nossa fechadura com duas chaves diferentes:

$chave1 = new Chave();
$chave1->segredo = 789;

$fechadura = new Fechadura();
$fechadura->destranca( $chave1 );



Perceba que, como a propriedade segredo da Chave é pública, modificamos seu valor do lado de fora da classe e o definimos com 789, o resultado será:

 

 

Chave inválida


Claro, o segredo está errado, vamos tentar com outra chave:

$chave2 = new Chave();
$chave2->segredo = 123456;

$fechadura = new Fechadura();
$fechadura->destranca( $chave2);



A saída será:

 

 

Destrancado


O segredo estava correto, Fulano pode abrir a porta e entrar agora.

Bom, por hora é só, no próximo artigo da série PHP Orientado a Objetos, falaremos sobre métodos de interface e polimorfismo.

Imagem Postada

 

 

 

Próximo Imagem Postada
Índice Imagem Postada

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Outro excelente artigo, mas você comentou que não faz sentido o segredo de uma fechadura ter visibilidade pública.

 

Mas também não faz sentido o segredo de uma chave ser público pois, trazendo para o mundo real, se uma chave não servir numa fechadura, você vai e pega outra ao invés de pegar uma lixa de metal para reconfigurar o segredo.

 

Esse pensamento, se aplicado, envolve a duplicação da classe Chave. Comoficaria uma estruturação lógica para um segredo com visibilidade privada?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Imaggens

 

Eu penso que o segredo da chave é público, já que está visível para quem pega a chave. Mas também pode ser privado, imaginando uma fechadura que seja aberta com cartão magnético (como aquele de hotéis). Neste caso, o segredo da fechadura e da chave são privados. Aplicando isto no PHP, a classe Cartão estenderia a classe Chave.

 

Se for isto mesmo, o que se faria neste caso, já que o segredo não seria conhecido pela aplicação (no exemplo, a pessoa que vai abrir a porta)? Se for algo que falaremos mais a frente, fica pra depois a explicação. Se eu falei besteira, me corrija.

 

Carlos Eduardo

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eis o X da questão. O segredo de uma chave pode ser tanto público como privado, ambas as possibilidades são válidas.

 

Vou apimentar um pouco mais, usando seu exemplo do cartão de hotel. Você solicita um cartão para combinar com o segredo de uma porta mas, se você o perde, por medidas de segurança, o cartão é recriado e o segredo da porta automaticamente reconfigurado.

 

Isso faz com que o segredo da fechadura também passe a requerer ser protegido pois precisará ser alterado por uma classe-pai que gerencie os segredos em tempo de execução.

 

Agora junte tudo e teremos uma situação que, acredito eu, o encapsulamento sozinho não bastaria.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas também não faz sentido o segredo de uma chave ser público pois, trazendo para o mundo real, se uma chave não servir numa fechadura, você vai e pega outra ao invés de pegar uma lixa de metal para reconfigurar o segredo.

 

Bom, Bruno, como eu disse:

Nesse caso específico, vamos deixar o segredo da chave público, por uma mera questão de ilustração, já que, basta pegar uma chave qualquer para vermos o segredo dela.

 

Obviamente, se é segredo deve ser encapsulado, apenas deixei público para ilustrar a diferença entre os dois.

 

Eu penso que o segredo da chave é público, já que está visível para quem pega a chave. Mas também pode ser privado, imaginando uma fechadura que seja aberta com cartão magnético (como aquele de hotéis). Neste caso, o segredo da fechadura e da chave são privados. Aplicando isto no PHP, a classe Cartão estenderia a classe Chave.

 

A idéia da ilustração foi justamente esta, o segredo de uma chave simples, por ser visível à quem pega a chave, foi colocado como público, contudo, independentemente de ser visível ou não, se é segredo, deve ser privado.

 

Se for isto mesmo, o que se faria neste caso, já que o segredo não seria conhecido pela aplicação (no exemplo, a pessoa que vai abrir a porta)? Se for algo que falaremos mais a frente, fica pra depois a explicação.

 

Vou apimentar um pouco mais, usando seu exemplo do cartão de hotel. Você solicita um cartão para combinar com o segredo de uma porta mas, se você o perde, por medidas de segurança, o cartão é recriado e o segredo da porta automaticamente reconfigurado.

 

Isso faz com que o segredo da fechadura também passe a requerer ser protegido pois precisará ser alterado por uma classe-pai que gerencie os segredos em tempo de execução.

 

Agora junte tudo e teremos uma situação que, acredito eu, o encapsulamento sozinho não bastaria.

 

Na verdade, Bruno, nesse caso do cartão, o encapsulamento seria ainda maior, transferindo a responsabilidade da autenticação para um terceiro objeto que encapsularia o segredo de ambos, da chave e da fechadura. Vamos discutir estados em um outro tópico, mas, basicamente, teríamos a seguinte situação:

 

1. A porta possui 2 estados:

1.1. Aberta

1.2 Fechada

 

2. A porta possui também uma fechadura eletrônica, que possuirá 2 estados:

2.1. Trancada

2.2. Destrancada

 

 

O estado "Destrancado" da fechadura é responsável pela autenticação, ou seja, encapsularemos o segredo da fechadura e teremos um objeto autenticador que ficará responsável apenas pela autenticação e o cartão de segurança possuirá um sistema de armazenamento qualquer que guardará o segredo gravado pela administração do hotel.

 

1. A porta está em seu estado "Fechado".

2. O usuário enfia o cartão no local específico.

3. O mecanismo da fechadura verifica seu estado atual, se estiver "Trancado" pega a autenticação do cartão e passa para o objeto de estado

4. O objeto de estado recebe uma instância do objeto fechadura e da autenticação

5. Se a autenticação for válida o estado da fechadura é modificado para "Destrancado"

 

Cada fechadura possui um segredo único e imutável, se o usuário perder seu cartão chave a administração do hotel irá até a fechadura, colocará um cartão "virgem" no local específico e, através de uma senha, gravará no cartão a autenticação para a porta.

 

Posso até ilustrar esse código aqui no tópico, porém, sairíamos totalmente do escopo já que, apesar de falar sobre encapsulamento, estaríamos falando sobre um nível de colaboração que ainda não chegamos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Muito bom mais uma vez João,a explicação está super fácil.

Abs

Compartilhar este post


Link para o post
Compartilhar em outros sites

Fala João.. depois de ler (bastante) alguns materiais sobre OO na internet..

 

eu acho que finalmente to começando a entender esses conceitos..

 

oq na verdade nao entendi bem ainda é onde colocar private e protected..maas:

eu criei umas classes pra manipular conta.. só para testes mesmo..

 

há algo de errado nessa implementaçao?

se tiver e puder me dar umas dicas agradeço =]

 

conta.class.php

<?php

	abstract class conta {
	
		protected $numero;
		protected $saldo;
		public $nome;
	
		public function __construct($nome, $num, $saldo) {
		
			$this->nome = $nome;
			$this->numero = $num;
			$this->saldo = $saldo;
                        echo 'Voce criou uma conta : ' . $this->nome . '<br />';
		
		}
		
		public function __destruct(){
		
			echo "<br />Finalizando a conta de {$this->nome}";
		
		}
	
	}

contacorrente.class.php

<?php
	include 'conta.class.php';
	
	final class contacorrente extends conta {
                
            private $limite;

            public function __construct($nome, $num, $saldo, $limite){
		
			$this->nome = $nome;
			$this->numero = $num;
			$this->saldo = $saldo;
                        $this->limite = $limite;
                        parent::__construct($nome,$num,$saldo);

            }

            public function sacar($valor) {

                if( $this->valida_saque($valor) == TRUE ){
                    $this->saldo -= $valor;
                    echo '<br />Voce sacou: R$' . $valor;
                }
                else
                    echo '<br />Voce tentou sacar um valor acima do seu limite';

            }

            private function valida_saque($valor){

                if (($this->saldo + $this->limite) >= $valor )
                    return TRUE;
                else
                    return FALSE;

            }

            public function getSaldo(){
                return $this->saldo;
            }
	
	}

index.php

<?php	
	include_once 'contacorrente.class.php';
	
	$test = new contacorrente ('Brayan',123456,5000, 10000);
        echo '<br />Seu saldo atual: ' . $test->getSaldo();

        $test->sacar(10200);

        echo '<br />Seu saldo atual: ' . $test->getSaldo();
?>

Saída:

Voce criou uma conta : Brayan

 

Seu saldo atual: 5000

Voce sacou: R$10200

Seu saldo atual: -5200

Finalizando a conta de Brayan

 

valeeu ;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Private - só a classe enxerga.

Protected - a classe e suas herdeiras enxergam.

Public - todo mundo enxerga.

 

Para a sua implementação, posso apontar uma redundância:

 

public function __construct($nome, $num, $saldo, $limite){
                
                        $this->nome = $nome;
                        $this->numero = $num;
                        $this->saldo = $saldo;

Essas três linhas já estão definidas na classe abstrata pai. Chamando o construtor através de parent::__construct() você repete as linhas. DRY.

 

$num pode ser gerado aleatoriamente, enquanto $saldo SEMPRE começa em 0.

 

Falta um método para depósito ;)

 

A menos que você vá precisar validar o saque para outra operação que não seja um saque, dispense o método novo e incorpore-o no saque. Quanto maior o objeto, mais memória.

 

Falta uma interface.

 

<?php interface iContas {
   public function sacar($quantia);

   public function depositar($quantia);

   public function saldo();
}

abstract class absContas implements iContas {
   protected $titular;
   protected $numConta;
   protected $limite;
   protected $saldo = 0;

   public function __construct($titular, $limite = 0){
       $this->titular = (string)$titular;
       $this->limite = (int)$limite;
       $this->numConta = uid();
   }

   public function depositar($quantia){
       if(!is_numeric($quantia) || $quantia < 0) throw new InvalidArgumentException('Quantia a ser depositada é inválida!');
       $this->saldo += $quantia;
   }

   public function sacar($quantia){
       if(!is_numeric($quantia)) throw new InvalidArgumentException('Quantia a ser sacada é inválida!');
       if($quantia < 0) $quantia *= -1;
       if($this->saldo + $this->limite < $quantia) throw new Exception('Saldo indisponível.');
       $this->saldo -= $quantia;
   }

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

class ContaCorrente extends absContas {
   public function __construct($nome, $limite = 0){
       parent::__construct($nome, $limite);
       echo "Conta corrente para <b>{$this->titular}</b>: {$this->numConta}";
   }

   public function __destruct(){
       echo "Encerrando conta número: {$this->numConta}";
   }
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.