Padrões de Design: O padrão Adapter
() translation by (you can also view the original English article)
No último articulo, nós vimos como o padrão de design facade(fachada) pode ser utilizado para simplificar a implementação de qualquer grande e complexo sistema usando uma simples classe facade(fachada).
Neste artigo, iremos continuar a nossa discussão nos padrões de design, vendo o padrão de design adapter. Este padrão em particular pode ser usado quando o teu código é dependente de alguma API externa, ou qualquer outra classe que é propensa a mudar com frequência. Este padrão assenta na categoria dos "padrões de estrutura", porque ele ensina como o nosso código e as nossa classes devem ser estruturadas de forma a gestão e/ou extensão facilmente.
Outra vez, eu gostaria de reiterar que os padrões de design não tem nada de novo em relação com as classes tradicionais. Em vez disso, eles mostram-nos uma forma melhor de estruturar as nossas classes, lidar com o seu comportamento e gerir o seu comportamento.
O problema
1 |
class Twitter { |
2 |
|
3 |
public function __construct() { |
4 |
// Your Code here //
|
5 |
}
|
6 |
|
7 |
public function send($msg) { |
8 |
// Posting to Twitter //
|
9 |
echo $msg; |
10 |
}
|
11 |
}
|
12 |
|
13 |
$twitter = new Twitter(); |
14 |
$twitter->send('Posting on Twitter'); |
No código acima, podes ver que estamos a utilizar a classe Twitter para simplesmente mandar uma mensagem. Aqui, nós estamos diretamente a criar um objeto da API do Twitter e postar tweets no Twitter. Tu tens esse código disperso em múltiplos locais. Assim nós podemos ver que o código está a usar este método $twitter->send('Posting on Twitter');
para fazer tweet.
Algum tempo atrás, o Twitter alterou o nome do método da API de send
parasendTweet
.
Isto deve claramente indicar um problema para aqueles de nós que estamos a utilizar o código send
. Especificamente, nós temos que alterar todo os métodos send
para sendTweet
.
Imagina a quantidade de código que nós necessitamos de alterar e o tempo que nós temos que despender para testar todas as funcionalidades, outra vez.
A solução
Uma solução para este problema é usar o padrão de design adapter.
De acordo com a Wikipedia:
Em engenharia de software, o padrão adapter é um padrão de design de software que permite a interface de uma classe existente ser utilizada por outra interface. É frequentemente usada para fazer classes existentes trabalharem com outras sem modificar o seu código fonte.
Neste caso, nós devíamos criar uma interface wrapper, que faz com que seja possível. Nós não vamos fazer quaisquer alterações na classe externa da biblioteca, porque nós não temos o controlo sobre ela e esta pode ser alterada a qualquer altura.
Vamos para o código agora, o qual demonstra o padrão adapter em ação:
1 |
// Concrete Implementation of Twitter Class
|
2 |
class Twitter { |
3 |
|
4 |
public function __construct() { |
5 |
// Your Code here //
|
6 |
}
|
7 |
|
8 |
public function send($msg) { |
9 |
// Posting to Twitter //
|
10 |
echo $msg; |
11 |
}
|
12 |
}
|
13 |
|
14 |
// Simple Interface for each Adapter we create
|
15 |
interface socialAdapter { |
16 |
public function send($msg); |
17 |
}
|
18 |
|
19 |
class twitterAdapter implements socialAdapter { |
20 |
|
21 |
private $twitter; |
22 |
|
23 |
public function __construct(Twitter $twitter) { |
24 |
$this->twitter = $twitter; |
25 |
}
|
26 |
|
27 |
public function send($msg) { |
28 |
$this->twitter->send($msg); |
29 |
}
|
30 |
}
|
Estuda o código anterior e deves ser capaz de dizer, que nós não introduzimos quaisquer alterações na classe mainTwitter
. Em vez disso, nós criamos uma interface para o nosso adapter social e uma classe adapter para o Twitter.
Assim, nós criamos o objeto da classe adapter em vez da classe main
Twitter
. Enquanto criar um objeto da classe do adapter irá passar o objecto da classe main Twitter
como argumento, assim a classe adapter pode ter uma referência da classe main e pode chamar os métodos necessários da classe main Twitter
.
Vamos ver como utilizar este método de forma direta:
1 |
// Client Code
|
2 |
$twitter = new twitterAdapter(new Twitter()); |
3 |
$twitter->send('Posting to Twitter'); |
Agora imagina que o Twitter muda o nome dos métodos de send
para sendTweet
.
Então nós simplesmente temos que fazer alterações em twitterAdapter
. Simplesmente observa o código revisto de adapter, que tem apenas uma alteração.
1 |
class twitterAdapter implements socialAdapter { |
2 |
|
3 |
private $twitter; |
4 |
|
5 |
public function __construct(Twitter $twitter) { |
6 |
$this->twitter = $twitter; |
7 |
}
|
8 |
|
9 |
public function send($msg) { |
10 |
$this->twitter->sendTweet($msg); |
11 |
}
|
12 |
}
|
Assim com apenas uma alteração e conseguimos.
Adicionar um novo adapter
Neste momento, nós vimos como podemos utilizar o padrão de design adapter para superar os cenários acima mencionados. Agora é muito fácil para adicionar uma nova classe dependente do adapter existente. Vamos dizer que temos a API do Facebook, para atualizar o estado da própria API.
Então em vez de usar a classe do Facebook diretamente, nós devíamos estar a aplicar o mesmo padrão adapter que nós utilizamos para o Twitter.
1 |
// Concrete Implementation of Twitter Class
|
2 |
class Facebook { |
3 |
|
4 |
public function __construct() { |
5 |
// Your Code here //
|
6 |
}
|
7 |
|
8 |
public function updateStatus($msg) { |
9 |
// Posting to Facebook //
|
10 |
echo $msg; |
11 |
}
|
12 |
}
|
13 |
|
14 |
// Facebook Adapter
|
15 |
class facebookAdapter implements socialAdapter { |
16 |
|
17 |
private $facebook; |
18 |
|
19 |
public function __construct(Facebook $facebook) { |
20 |
$this->facebook = $facebook; |
21 |
}
|
22 |
|
23 |
public function send($msg) { |
24 |
$this->facebook->updateStatus($msg); |
25 |
}
|
26 |
}
|
27 |
|
28 |
|
29 |
// Client Code
|
30 |
$facebook = new facebookAdapter(new Facebook()); |
31 |
$facebook->send('Posting to Facebook'); |
Como podes ver, o mesmo principio se aplica. Defines o método que está disponível para utilização de terceiros e então se as dependências da API alterarem, tu simplesmente mudas a classe dependente sem expores a interface externa.
Conclusão
Um grande aplicação é constantemente utilizada em outras bibliotecas e APIs, assim eu propunha que nós implementássemos o método adapter, assim nós não iriamos experienciar qualquer problema com API de terceiros ou com bibliotecas que modificam o seu código base.
Eu dei o meu melhor para providenciar um elementar e ainda assim útil exemplo para demonstrar o padrão de design adapter, mas se tiveres quaisquer comentários adicionais ou questões, por favor não hesites em adicioná-los no feed abaixo.
Seja o primeiro a saber sobre novas traduções–siga @tutsplus_pt no Twitter!