Nos diversos projetos em php que já me envolvi e naqueles que ainda me envolvo, percebo que as Interfaces, que é algo muito importante do Paradigma de Orientação à Objetos, são muito pouco utilizadas e/ou na maioria das vezes, NÃO utilizadas. Diversos programadores não dão a mínima para as interfaces. Alguns a julgam desnecessárias, outros pouco importante mas no geral acredito que essa subutilização se dá devido ao não conhecimento de suas características e de sua concepção.
Por isso que estou aqui escrevendo esse post. Para tentar explicar a vocês leitores o que é uma Interface, para que serve, como implementar/utilizar e quais são as vantagens que ela traz.
Antes de entrar no assunto Interface propriamente dito, vamos contextualizar um caso de uso fictício e tentar fixar dois conceitos importantes presentes na Orientação à Objetos. São eles: forte/fraco acoplamento e inversão de controle(esse é um design pattern muito importante).
O manual do bom desenvolvimento da POO reza que as classes devem ser fracamente acopladas entre si. Por exemplo, imagine que em um sistema possua uma área restrita que é acessada mediante o preenchimento de usuário/senha que serão validados na tabela “usuario” em uma base de dados. Assim, poderíamos ter uma classe “ConexaoMysql”, responsável por conectar a aplicação com o banco (MySQL) e uma classe “Usuario” para para receber e validar os dados de um usuário.
As implementações dessas classes poderiam ser da seguinte forma (OBS: O foco aqui não é segurança e padrões em conexão com banco de dados. Logo, as implementações são feitas de forma “grosseira”, com o mysql_connect e mysql_select_db).
1 2 3 4 5 6 7 8 | class ConexaoMysql { public function conectar() { mysql_connect('host', 'UsuarioBD', 'SenhaBD'); mysql_select_db('schema'); } } |
1 2 3 4 5 6 7 8 9 10 | class Usuario { private $con; public function __construct() { $this->con = new ConexaoMysql(); $this->con->conectar(); } } |
Note que, como a autenticação de um usuário se baseará em uma tabela do banco de dados, precisamos consumir a classe “ConexaoMysql” na classe “Usuario”. No nosso exemplo isso é feito através do atributo “$con” que é instanciado no método construtor. Essa não seria uma boa prática, pois criamos uma dependência da classe “Usuario” para com a classe “ConexaoMysql”. Isso pode significar que futuras implementações na classe “ConexaoMysql” resulte em alterações na classe “Usuario”. Se tivermos um projeto com 10 classes que utilizam a classe “ConexaoMysql”, uma alteração nessa pode nos forçar a editar códigos em 10 classes distintas.
Para resolver isso, ao invés do “objeto” conexão ser criado dentro da classe que a consome, ele será passado (injetado) para a classe o consumir. Dessa forma, a classe “Usuario” ficaria da seguinte forma:
1 2 3 4 5 6 7 8 9 10 | class Usuario { private $con; public function __construct(ConexaoMysql $con) { $this->con = $con; $this->con->conectar(); } } |
Note que agora a classe “ConexaoMysql” não é instanciada em “Usuario”. Ela já vem criada e setada como parâmetro no método construtor (Poderia ser por um método setCon($con) também!).
Para usar a classe “Usuario”.
1 2 | $conMysql = new ConexaoMysql(); $usuario = new Usuario($conMysql); |
Podemos falar que agora as classes do nosso projeto estão fracamente acopladas devido à inversão de controle. Ou seja, não temos mais dependência entre as classes.
Beleza. Entendi. Maaas…. Quando as Interfaces entram?
Calma, chegaremos lá!
Ainda nesse projeto, imagine que surgiu a necessidade de se implementar uma nova classe de conexão, com o Postgre agora. Então, vamos criar a classe “ConexaoPg”.
1 2 3 4 5 6 7 | class ConexaoPg { public function conectar() { pg_connect("host=host dbname=schema user=UsuarioBD password=SenhaBD port=5432"); } } |
Em alguns casos, a classe “Usuario” irá consumir a classe “ConexaoMysql” e em outros a classe “ConexaoPg”.
Atualmente não é possível pois o método construtor de “Usuario” recebe um objeto do tipo “ConexaoMysql” como parâmetro. Logo, não tem como consumir a classe “ConexaoPg”.
Por isso, vamos criar uma interface chamada “IConexao”. Uma interface não possui implementações. Ela possui somente a declaração de cabeçalhos de métodos. E assim, todas as classes que implementarem uma determinada interface, deverá obrigatoriamente possuir os métodos nela declarados.
Interface “IConexao”.
1 2 3 4 | interface IConexao { public function conectar(); } |
Veja que todas as classes que implementarem a interface “IConexao” deverão possuir obrigatoriamente o método “conectar()”. As nossas classes “ConexaoMysql” e “ConexaoPg” irão implementar essa interface. Isso é informado através do comando “implements”, como segue:
1 2 3 4 5 6 7 8 | class ConexaoMysql implements IConexao { public function conectar() { mysql_connect('host', 'UsuarioBD', 'SenhaBD'); mysql_select_db('schema'); } } |
1 2 3 4 5 6 7 | class ConexaoPg implements IConexao { public function conectar() { pg_connect("host=host dbname=schema user=UsuarioBD password=SenhaBD port=5432"); } } |
Agora, na classe “Usuario”, o método construtor, ao invés de receber um “objeto concreto/físico” irá receber uma instância de qualquer classe que implemente a interface “IConexao”. Assim:
1 2 3 4 5 6 7 8 9 10 | class Usuario { private $con; public function __construct(IConexao $con) { $this->con = $con; $this->con->conectar(); } } |
Assim, a classe recebe tanto um objeto do tipo “ConexaoMysql” como “ConexaoPg” pois ambas as classes implementam IConexao.
1 2 3 4 5 | $conMysql = new ConexaoMysql(); $usuario = new Usuario($conMysql); $conPg = new ConexaoPg(); $usuario = new Usuario($conPg); |
Se no futuro surgirem necessidades de conexões com bancos Oracle e SQL Server, basta que as respectivas classes implementem a interface “IConexao” e automaticamente poderão serem consumidas na classe “Usuario”.
Informações importantes:
- Uma classe pode ter outros métodos particulares que não foram declarados na Interface.
- Uma única classe pode implementar várias interfaces distintas.
Bom, terminamos por aqui.
Qualquer dúvida deixe um comentário.
Abs!
Comentários