Neste artigo, vou mostrar como aplicar boas práticas ao projeto de envio de SMS usando o padrão Strategy.
No artigo anterior eu mostrei como enviar mensagens SMS usando o serviço da ViaNett com a linguagem VB .NET.
Criamos uma conta na ViaNett. Em seguida, criamos uma aplicação Windows Forms, referenciamos o web service e enviamos mensagens SMS para um celular. Como o objetivo era apenas mostrar como enviar mensagens SMS, podemos dizer que a aplicação funciona.
Se a aplicação for vista como apenas um protótipo de testes tudo bem. Mas se desejarmos criar uma aplicação mais robusta e aderente às boas práticas de programação, não está aderente aos princípios SOLID, e, para contotnar isso, vamos ter que alterar muita coisa em nosso código.
O que há de errado com o nosso projeto?
- O projeto fere princípio da responsabilidade única ou SRP, pois apresenta muitas responsabilidades reunidas na camada de interface do usuário;
- O nosso código não esta aderente ao príncipio Aberto-Fechado ou Open Close, pois qualquer nova implementação vai requerer alteração no código;
- Não estamos programando para uma interface e sim para uma implementação;
- Não estamos usando a injeção de dependência para termos um baixo acoplamento entre os objetos usados.
Esses são alguns dos problemas que podemos identificar no código usado.
Imagine que agora precisamos enviar mensagens SMS usando o serviço de outra empresa. Como faríamos? Basta colocar um if no código e pronto? Pensando e agindo assim, o seu código vai requerer uma constante manutenção e pode apresentar diversos problemas no futuro. Vamos, então, mostrar como melhorar o nosso projeto.
Analisando o problema e se antecipando a mudanças
Para não tornar o problema muito abrangente, vou focar apenas na tarefa de enviar mensagens SMS.
Focando nisso, podemos antever que temos diversos opções para realizar essa tarefa e cada opção vai ter, assim, a sua implementação no código. Podemos enviar SMS usando um modem 3G ou o serviço da ViaNett, da Twilio, da Ipipi, da LocaSMS (Brasil), da Zenia (Brasil) etc.
Então, vamos ter algoritmos diferentes para cada implementação. Pensando nisso, podemos usar o padrão de projeto Estratégia ou Strategy. Eu já apresentei esse padrão neste artigo: Strategy – Macoratti.net.
Apenas para constar: “O padrão Strategy define uma familia de algoritmos intercambiáveis de forma que estes sejam independentes dos clientes que os utilizam”.
Resumindo:
- Objetivo: Encapsular um algoritmo em um objeto;
- Fornecer interfaces genéricas o suficiente para suportar uma variedade de algorítmos;
- Facilitar a escolha e troca (intercâmbio) de algoritmos criados com uma mesma função.
Implementando o padrão Strategy
Vamos abrir o projeto criado no artigo anterior e criar uma pasta chamada Service no projeto.
A seguir, vamos criar uma interface chamada IEnviaSMS onde vamos definir o método EnviarSMS conforme mostrado a seguir:
Public Interface IEnviaSMS Sub EnviaSMS(telefone As Long, mensagem As String) End Interface
Estamos agora programando para um interface e veremos que isso vai desacoplar os objetos usados em nossa aplicação.
Vamos agora criar na pasta Service a classe EnviaSMSViaNett que vai implementar essa interface:
Public Class EnviaSMSViaNett Implements IEnviaSMS Public Sub EnviaSMS(telefone As Long, mensagem As String) Implements IEnviaSMS.EnviaSMS Try Dim usuario As String = "" Dim senha As String = "" Using ViaNettClient = New ViaNett.CPAWebServiceSoapClient("CPAWebServiceSoap12") Dim Resultado = ViaNettClient.SendSMS_Simple1(usuario, senha, telefone, mensagem) End Using Catch Throw End Try End Sub End Class
Neste código, estamos isolando a implementação específica da ViaNett em uma classe encapsulando, assim, o nosso código. Qualquer alteração na metodologia da ViaNett vai afetar somente esta classe.
Se no futuro você precisar enviar SMS usando outro serviço, como o da LocaSMS que veremos no próximo artigo, basta criar uma nova classe e implementar a mesma interface. Assim, nosso código ficou aderente ao princípio Open-Closed, pois agora podemos estender o comportamento do nossa aplicação sem ter que modificar código.
Além disso, isso vai facilitar os testes unitários da nossa aplicação.
Vamos criar no projeto uma classe chamada Comunicacao, que fara o papel da Strategy com o seguinte código:
Public Class Comunicacao Dim _enviaSMS As IEnviaSMS Sub New(enviaSMS As IEnviaSMS) _enviaSMS = enviaSMS End Sub Public Sub EnviaMensagemSMS(fone As Long, msg As String) _enviaSMS.EnviaSMS(fone, msg) End Sub End Class
Observe que não temos nenhum acoplamento forte no código desta classe, pois no construtor da classe estamos injetando uma instância do serviço para enviar SMS definido na interface IEnviaSMS.
Agora, só falta definir o código no formulário Form1.vb que é a nossa interface com o usuário. Nossa interface agora não sabe como enviar SMS, ela apenas usa a classe Comunica que é responsável por realizar essa tarefa. Qualquer alteração na metodologia de enviar SMS não vai afetar nossa interface.
Veja como ficou o código:
Imports System.Text.RegularExpressions Public Class Form1 Private Sub btnEnviarSMS_Click(sender As Object, e As EventArgs) Handles btnEnviarSMS.Click Dim fone As Long = 0 Dim msg As String = "" If Not ValidaTelefone(txtTelefone.Text) Then MessageBox.Show("Número de telefone inválido. Use o formato 55XX999999999.") txtTelefone.Text = "" txtTelefone.Focus() Return Else fone = Convert.ToInt64(txtTelefone.Text.Replace("+", String.Empty)) End If If String.IsNullOrEmpty(txtMensagem.Text) Then MessageBox.Show("Informe a mensagem a ser enviada ") txtMensagem.Focus() Return Else msg = txtMensagem.Text End If Try Dim comunica As Comunicacao comunica = New Comunicacao(New EnviaSMSViaNett) comunica.EnviaMensagemSMS(fone, msg) MessageBox.Show("Mensagem enviada para : " & txtTelefone.Text) Catch ex As Exception MessageBox.Show("Erro : " & ex.Message) End Try End Sub Function ValidaTelefone(fone As String) As Boolean Dim pattern As String = "^[0-9]*quot; 'cria o objeto regex Dim check As New Regex(pattern) Dim valid As Boolean = False 'verifica se um valor foi fornecido e se é maior que 8 caracteres If Not String.IsNullOrEmpty(fone) And fone.Length > 8 Then valid = check.IsMatch(fone) Else valid = False End If Return valid End Function End Class
É claro que poderíamos continuar a aplicar outros padrões (como a injeção de dependência), mas com apenas essas alterações nosso projeto ficou muito melhor
Na próxima parte do artigo vou mostrar como enviar SMS usando o serviço da LocaSMS que é uma empresa localizada no Brasil.