Orientação à Objetos: Entendendo as Interfaces

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:

  1. Uma classe pode ter outros métodos particulares que não foram declarados na Interface.
  2. Uma única classe pode implementar várias interfaces distintas.

Bom, terminamos por aqui.

Qualquer dúvida deixe um comentário.

Abs!

É formado em Sistemas de Informação, pós-graduado em Sistemas de Banco de Dados e mestre em Educação com foco em Tecnologias Sociocomunitárias. Trabalha como professor de ensino técnico e tecnológico no Instituto Federal de Educação, Ciência e Tecnologia de São Paulo ministrando disciplinas nas áreas de programação, banco de dados, desenvolvimento de projetos e engenharia de software.

Posts relacionados

Comentários

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *