374 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			374 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| ---
 | ||
| title: Polymorphism with Abstract and Interface
 | ||
| localeTitle: Polimorfismo com Resumo e Interface
 | ||
| ---
 | ||
| ## Polimorfismo com Resumo e Interface
 | ||
| 
 | ||
| _Compartilhe e imponha código com Polimorfismo usando a classe e a interface Abstratas_
 | ||
| 
 | ||
| Vamos mergulhar mais profundamente na Programação Orientada a Objetos e tentar pensar em termos de Padrões de Projeto para compartilhar e impor nosso código usando Polimorfismo.
 | ||
| 
 | ||
| ### Classe abstrata
 | ||
| 
 | ||
| Digamos que temos uma classe chamada Man com algumas propriedades ( `name` , `age` , `height` , `fav_drinks` e `fav_sports` ) e métodos ( `giveFirmHandshakes` , `beStubborn` e `notPutToiletPaper` ).
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  
 | ||
|  class Man 
 | ||
|  { 
 | ||
|     public $name; 
 | ||
|     public $age; 
 | ||
|     public $height; 
 | ||
|     public $fav_sports; 
 | ||
|     public $fav_drinks; 
 | ||
|  
 | ||
|     public function __construct($name, $age, $height) 
 | ||
|     { 
 | ||
|         $this->name = $name; 
 | ||
|         $this->age = $age; 
 | ||
|         $this->height = $height; 
 | ||
|     } 
 | ||
|  
 | ||
|     public function giveFirmHandshakes() 
 | ||
|     { 
 | ||
|         return "I give firm handshakes."; 
 | ||
|     } 
 | ||
|  
 | ||
|     public function beStubborn() 
 | ||
|     { 
 | ||
|         return "I am stubborn."; 
 | ||
|     } 
 | ||
|  
 | ||
|     public function notPutToiletPaper() 
 | ||
|     { 
 | ||
|         return "It's not humanly possible to remember to put toilet paper rolls when they are finished"; 
 | ||
|     } 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
| Precisamos especificar nome, idade e altura para instanciar essa classe conforme exigido pelo construtor.
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  $jack = new Man('Jack', '26', '5 Feet 6 Inches'); 
 | ||
|  
 | ||
|  echo sprintf('%s - %s - %s', $jack->name, $jack->age, $jack->height); 
 | ||
|  // => Jack - 26 - 5 Feet 6 Inches 
 | ||
| ```
 | ||
| 
 | ||
| Agora, digamos que queremos adicionar um novo método a essa classe chamada isActive.
 | ||
| 
 | ||
| Este método verifica a propriedade ativa e retorna a mensagem apropriada, dependendo do valor de ativo com valor padrão de false. Podemos definir isso como verdadeiro para os homens que estão ativos.
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  
 | ||
|  class Man 
 | ||
|  { 
 | ||
|     public $name; 
 | ||
|     public $age; 
 | ||
|     public $height; 
 | ||
|     public $fav_sports; 
 | ||
|     public $fav_drinks; 
 | ||
|     public $active = false; 
 | ||
|  
 | ||
|     ..... 
 | ||
|     ..... 
 | ||
|  
 | ||
|     public function isActive() 
 | ||
|     { 
 | ||
|         if ($this->active == true) { 
 | ||
|             return "I am an active man."; 
 | ||
|         } else { 
 | ||
|             return "I am an idle man."; 
 | ||
|         } 
 | ||
|     } 
 | ||
|  } 
 | ||
|  
 | ||
|  $jack = new Man('Jack', '26', '5 Feet 6 Inches'); 
 | ||
|  $jack->active = true; 
 | ||
|  echo $jack->isActive(); 
 | ||
|  // => I am an active man. 
 | ||
|  
 | ||
|  $jake = new Man('Jake', '30', '6 Feet'); 
 | ||
|  echo "\n" . $jake->isActive(); 
 | ||
|  // => I am an idle man. 
 | ||
| ```
 | ||
| 
 | ||
| E se um homem não estiver APENAS ativo ou ocioso?
 | ||
| 
 | ||
| E se houver uma escala de 1 a 4 que mede quão ativo um homem é (1 - inativo, 2 - pouco ativo, 3- moderadamente ativo, 4- muito ativo)?
 | ||
| 
 | ||
| Podemos ter uma declaração if..elseif..else como esta:
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  
 | ||
|  public function isActive() 
 | ||
|  { 
 | ||
|     if ($this->active == 1) { 
 | ||
|         return "I am an idle man."; 
 | ||
|     } elseif ($this->active == 2) { 
 | ||
|         return "I am a lightly active man."; 
 | ||
|     } elseif ($this->active == 3) { 
 | ||
|         return "I am a moderately active man."; 
 | ||
|     } else { 
 | ||
|         return "I am a very active man."; 
 | ||
|     } 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
| Agora, vamos dar um passo adiante.
 | ||
| 
 | ||
| E se a propriedade ativa do Homem não for apenas um inteiro (1, 2, 3, 4, etc)?
 | ||
| 
 | ||
| E se o valor de ativo for “atlético” ou “preguiçoso”?
 | ||
| 
 | ||
| Não temos que adicionar mais instruções elseif procurando por uma correspondência com essas strings?
 | ||
| 
 | ||
| Classes abstratas podem ser usadas para tal cenário.
 | ||
| 
 | ||
| Com as classes abstratas, você basicamente define a classe como abstrata e os métodos que deseja impor como abstratos sem realmente inserir qualquer código dentro desses métodos.
 | ||
| 
 | ||
| Em seguida, você cria uma classe filha estendendo a classe abstrata pai e implementa os métodos abstratos nessa classe filha.
 | ||
| 
 | ||
| Dessa forma, você estará impondo todas as classes filhas para definir sua própria versão de métodos abstratos. Vamos ver como podemos definir nosso método `isActive()` como abstrato.
 | ||
| 
 | ||
| # 1: defina a classe como abstrata.
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  abstract class Man 
 | ||
|  { 
 | ||
|  ..... 
 | ||
|  ..... 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
| # 2: Crie um método abstrato para o método que você deseja impor dentro da classe abstrata.
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  abstract class Man 
 | ||
|  { 
 | ||
|  ..... 
 | ||
|  ..... 
 | ||
|  abstract public function isActive(); 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
| # 3: Crie uma classe filha estendendo a classe abstrata.
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  
 | ||
|  class AthleticMan extends Man 
 | ||
|  { 
 | ||
|  ..... 
 | ||
|  ..... 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
| # 4: Implemente o método abstrato dentro da classe filha.
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  class AthleticMan extends Man 
 | ||
|  { 
 | ||
|     public function isActive() 
 | ||
|     { 
 | ||
|         return "I am a very active athlete."; 
 | ||
|     } 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
| # 5: Instanciar a classe filha (NÃO a classe abstrata).
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  $jack = new AthleticMan('Jack', '26', '5 feet 6 inches'); 
 | ||
|  echo $jack->isActive(); 
 | ||
|  // => I am a very active athlete. 
 | ||
| ```
 | ||
| 
 | ||
| Definição completa da classe abstrata e código de implementação:
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  
 | ||
|  abstract class Man 
 | ||
|  { 
 | ||
|     public $name; 
 | ||
|     public $age; 
 | ||
|     public $height; 
 | ||
|     public $fav_sports; 
 | ||
|     public $fav_drinks; 
 | ||
|  
 | ||
|     public function __construct($name, $age, $height) 
 | ||
|     { 
 | ||
|         $this->name = $name; 
 | ||
|         $this->age = $age; 
 | ||
|         $this->height = $height; 
 | ||
|     } 
 | ||
|  
 | ||
|     public function giveFirmHandshakes() 
 | ||
|     { 
 | ||
|         return "I give firm handshakes."; 
 | ||
|     } 
 | ||
|  
 | ||
|     public function beStubborn() 
 | ||
|     { 
 | ||
|         return "I am stubborn."; 
 | ||
|     } 
 | ||
|  
 | ||
|     public function notPutToiletPaper() 
 | ||
|     { 
 | ||
|         return "It's not humanly possible to remember to put toilet paper rolls when they are finished"; 
 | ||
|     } 
 | ||
|  
 | ||
|     abstract public function isActive(); 
 | ||
|  } 
 | ||
|  
 | ||
|  class AthleticMan extends Man 
 | ||
|  { 
 | ||
|     public function isActive() 
 | ||
|     { 
 | ||
|         return "I am a very active athlete."; 
 | ||
|     } 
 | ||
|  } 
 | ||
|  
 | ||
|  $jack = new AthleticMan('Jack', '26', '5 feet 6 inches'); 
 | ||
|  echo $jack->isActive(); 
 | ||
|  // => I am a very active athlete. 
 | ||
| ```
 | ||
| 
 | ||
| Neste código, você notará que o método abstrato `isActive()` está definido dentro da classe abstrata `Man` e é implementado dentro da classe filha `AthleticMan` .
 | ||
| 
 | ||
| Agora a classe `Man` não pode ser instanciada diretamente para criar um objeto.
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  $ted = new Man('Ted', '30', '6 feet'); 
 | ||
|  echo $ted->isActive(); 
 | ||
|  // => Fatal error:  Uncaught Error: Cannot instantiate abstract class Man 
 | ||
| ```
 | ||
| 
 | ||
| Além disso, todas as classes filhas da classe abstrata (classe `Man` ) precisam implementar todos os métodos abstratos. A falta de tal implementação resultará em um erro fatal.
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  class LazyMan extends Man 
 | ||
|  { 
 | ||
|  
 | ||
|  } 
 | ||
|  
 | ||
|  $robert = new LazyMan('Robert', '40', '5 feet 10 inches'); 
 | ||
|  echo $robert->isActive(); 
 | ||
|  // => Fatal error:  Class LazyMan contains 1 abstract method 
 | ||
|  // => and must therefore be declared abstract or implement 
 | ||
|  // => the remaining methods (Man::isActive) 
 | ||
| ```
 | ||
| 
 | ||
| Usando classes abstratas, você pode impor certos métodos para serem implementados individualmente pelas classes filhas.
 | ||
| 
 | ||
| ### Interface
 | ||
| 
 | ||
| Existe outro conceito de Programação Orientada a Objetos que está intimamente relacionado a Classes Abstratas chamadas de Interface.
 | ||
| 
 | ||
| A única diferença entre Abstract Classes e Interfaces é que em Abstract Classes, você pode ter uma mistura de métodos definidos ( `giveFirmHandshakes()` , `isStubborn()` , etc.) e métodos abstratos ( `isActive()` ) dentro da classe pai, enquanto em Interfaces, você só pode definir (não implementar) métodos dentro da classe pai.
 | ||
| 
 | ||
| Vamos ver como podemos converter a classe abstrata Man acima em uma interface.
 | ||
| 
 | ||
| # 1: Defina a interface com todos os métodos (use interface em vez de classe).
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  interface Man 
 | ||
|  { 
 | ||
|     public function __construct($name, $age, $height); 
 | ||
|  
 | ||
|     public function giveFirmHandshakes(); 
 | ||
|  
 | ||
|     public function beStubborn(); 
 | ||
|  
 | ||
|     public function notPutToiletPaper(); 
 | ||
|  
 | ||
|     public function isActive(); 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
| # 2: Crie uma classe que implemente a interface (use implements em vez de extends). Esta classe deve implementar TODOS os métodos definidos dentro da interface, incluindo o método construtor.
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  class AthleticMan implements Man 
 | ||
|  { 
 | ||
|     public $name; 
 | ||
|     public $age; 
 | ||
|     public $height; 
 | ||
|  
 | ||
|     public function __construct($name, $age, $height) 
 | ||
|     { 
 | ||
|         $this->name = $name; 
 | ||
|         $this->age = $age; 
 | ||
|         $this->height = $height; 
 | ||
|     } 
 | ||
|  
 | ||
|     public function giveFirmHandshakes() 
 | ||
|     { 
 | ||
|         return "I give firm handshakes."; 
 | ||
|     } 
 | ||
|  
 | ||
|     public function beStubborn() 
 | ||
|     { 
 | ||
|         return "I am stubborn."; 
 | ||
|     } 
 | ||
|  
 | ||
|     public function notPutToiletPaper() 
 | ||
|     { 
 | ||
|         return "It's not humanly possible to remember to put toilet paper rolls when they are finished"; 
 | ||
|     } 
 | ||
|  
 | ||
|     public function isActive() 
 | ||
|     { 
 | ||
|         return "I am a very active athlete."; 
 | ||
|     } 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
| # 3: instanciar a classe de implementação (AthleticMan)
 | ||
| 
 | ||
| ```php
 | ||
| <?php 
 | ||
|  $jack = new AthleticMan('Jack', '26', '5 feet 6 inches'); 
 | ||
|  echo $jack->isActive(); 
 | ||
|  // => I am a very active athlete. 
 | ||
| ```
 | ||
| 
 | ||
| Com as interfaces, você precisa ter em mente que:
 | ||
| 
 | ||
| *   Os métodos não podem ser implementados dentro da interface.
 | ||
|     
 | ||
| *   Variáveis (propriedades) não podem ser definidas dentro da interface.
 | ||
|     
 | ||
| *   Todos os métodos definidos dentro da interface precisam ser implementados na classe filho (implementação).
 | ||
|     
 | ||
| *   Todas as variáveis necessárias precisam ser definidas dentro da classe filha.
 | ||
|     
 | ||
| *   Man interface impõe suas classes de implementação para implementar todos os métodos na interface.
 | ||
|     
 | ||
| 
 | ||
| Então, qual é o uso de interfaces?
 | ||
| 
 | ||
| Não podemos simplesmente criar uma nova classe AthleticMan e criar todos os métodos em vez de implementar a interface?
 | ||
| 
 | ||
| É aqui que os _Padrões de Design_ entram em jogo.
 | ||
| 
 | ||
| Interfaces são usadas quando há uma classe base ( `Man` ) que quer obrigar você a fazer as coisas (construir um objeto, darFirmHandshakes, serStubborn, notPutToiletPaper e verificar se você está ativo), mas não quer dizer exatamente como fazê-lo .
 | ||
| 
 | ||
| Você pode ir em frente e criar classes de implementação com implementações conforme julgar adequado.
 | ||
| 
 | ||
| Contanto que todos os métodos sejam implementados, a interface do `Man` não se importa como.
 | ||
| 
 | ||
| Nós revisamos como e quando usar classes abstratas e interfaces em PHP. Usando esses conceitos OOP para ter classes com diferentes funcionalidades compartilhando a mesma base “blueprint” (classe abstrata ou interface) é chamado Polimorfismo. |