Olá, meu nome é Giovanna Moeller e sou instrutora aqui na Alura.
Audiodescrição: Giovanna é uma mulher branca. Tem cabelo loiro e liso. Veste uma camiseta azul marinho com o escrito Alura e usa óculos de armação de gatinho. Está nos estúdios da Alura; ao fundo, há uma iluminação que vai do roxo para o azul.
Boas-vindas ao curso de Solid com iOS!
Neste curso, temos o projeto Swift Bank. Nele, é possível realizar depósitos, como um exemplo de R$200. Para isso, na tela de boas-vindas no emulador do projeto, clicamos em "depósito" e depois informamos o valor desejado. Logo após inserir o valor, selecionamos o botão "Confirmar depósito".
Após realizar o depósito no Swift Bank, o sistema exibe uma mensagem de confirmação para a pessoa usuária. Além disso, há a possibilidade de efetuar saques, como por exemplo, retirar R$100. Após cada operação de saque, o sistema apresenta uma mensagem indicando o sucesso da operação.
Outra funcionalidade relevante do Swift Bank é a disponibilidade de uma tabela na seção "últimas transações" que registra o histórico de transações.
Este projeto está em funcionamento e visualmente agradável, porém, ainda há muito código que podemos aprimorar. Vamos realizar uma refatoração utilizando os princípios do SOLID.
É essencial dominar esses princípios para escrever um código que seja facilmente modificável, que permita a introdução de novas funcionalidades com facilidade, que seja menos desacoplado e mais extensível.
Como pessoa desenvolvedora, é importante aplicar esses princípios para atender às exigências do mercado de trabalho, onde a qualidade do código é fundamental.
Para aproveitar ao máximo este curso, é fundamental ter um bom entendimento da linguagem Swift e suas principais funcionalidades relacionadas à programação orientada a objetos.
Espero que tenha gostado da proposta apresentada neste vídeo e aguardo sua participação nos próximos conteúdos!
Antes de entendermos o conceito do SOLID, vamos analisar um exemplo no Playground.
Suponha que o código a seguir faça parte de um sistema de gerenciamento de pedidos.
SOLID - Single Responsibility Principle (Ruim).xcplaygroundpage
import Foundation
class Order {
let product: String
let quantity: Int
let customerEmail: String
init(product: String, quantity: Int, customerEmail: String) {
self.product = product
self.quantity = quantity
self.customerEmail = customerEmail
}
}
// código omitido
Na linha 6, encontramos a classe Order
, responsável por representar um pedido, junto com seus atributos, como product
(nome do produto), quantity
(quantidade do produto) e customerEmail
(e-mail do cliente). O construtor está devidamente definido no init()
.
// código omitido
class OrderManager {
func createOrder(product: String, quantity: Int, customerEmail: String) {
// Lógica para criar um pedido
print("Pedido para \(product), quantidade \(quantity) criado.")
let order = Order(product: product, quantity: quantity, customerEmail: customerEmail)
saveOrderToDatabase(order: order)
sendConfirmationEmail(to: customerEmail)
}
func saveOrderToDatabase(order: Order) {
// Salvar pedido no banco de dados
print("Pedido salvo no banco de dados.")
}
func sendConfirmationEmail(to email: String) {
print("Enviando email para \(email)")
}
}
let orderManager = OrderManager()
orderManager.createOrder(product: "Livro Swift", quantity: 1, customerEmail: "giovanna@gmail.com")
Na linha 18 do código, temos uma classe denominada OrderManager
. Dentro dessa classe, estão definidas várias funções, incluindo CreateOrder
, que tem a finalidade de criar um pedido e contém a lógica necessária para isso.
Na linha 22, ela executa a criação do pedido. Em seguida, ela invoca a função saveOrderToDatabase()
para salvar o pedido no banco de dados. Além disso, há uma função chamada sendConfirmationEmail()
, que é responsável por enviar um e-mail de confirmação ao cliente.
Dessa forma, a classe OrderManager
possui três funções distintas para gerenciar o processo de pedidos.
Você consegue identificar algum problema neste código? Suponha que precisamos modificar a lógica de salvar pedidos no banco de dados. Nesse caso, seria preciso fazer alterações diretamente na OrderManager
. O mesmo se aplica à necessidade de modificar a lógica de envio de e-mails. Mais uma vez, teríamos que alterar diretamente essa mesma classe.
Portanto, a OrderManager
está sobrecarregada com diversas responsabilidades: gerenciamento de pedidos (createOrder
), persistência de dados no saveOrderToDatabase
(ou seja, salvar no banco de dados) e também envio de e-mails (sendConfirmationEmail
). Para solucionar esse problema, é necessário dividir essas responsabilidades em classes menores e mais especializadas.
Vamos resolver esse problema.
Abaixo da OrderManager
, criaremos uma nova classe denominada OrderPersistenceManager
. Essa classe será responsável pela persistência de dados, ou seja, pelo processo de salvar no banco de dados ou em qualquer outro local adequado.
Para fazer isso, movemos a função saveOrderToDatabase
da OrderManager
para a OrderPersistenceManager
, utilizando o "Command + X" para realizar essa transferência.
// código omitido
class OrderPersistenceManager {
func saveOrderToDatabase(order: Order) {
// Salvar pedido no banco de dados
print("Pedido salvo no banco de dados.")
}
}
// código omitido
Procedemos criando uma outra classe que chamaremos de EmailService
, onde incluiremos a função sendConfirmationEmail
. Para isso, faremos uma cópia (Ctrl + C) e em seguida colamos (Ctrl + V).
// código omitido
class EmailService {
func sendConfirmationEmail(to email: String) {
print("Enviando email para \(email)")
}
}
// código omitido
Após a criação das duas classes distintas com suas responsabilidades específicas, é necessário injetar essas dependências na nossa classe OrderManager
. Para isso, vamos inicializar essas instâncias no nosso OrderManager
. Antes da função CreateOrder
, vamos declarar dois atributos privados: orderPersistenceManager
do tipo OrderPersistenceManager
e emailService
do tipo EmailService
.
Em seguida, precisamos criar o construtor usando init()
, e o Xcode se encarregará de realizar essa configuração automaticamente para nós.
// código omitido
class OrderManager {
private var orderPersistenceManager: OrderPersistenceManager
private var emailService: EmailService
init(orderPersistenceManager: OrderPersistenceManager, emailService:
EmailService) {
self.orderPersistenceManager = orderPersistenceManager
self.emailService = emailService
func createOrder(product: String, quantity: Int, customerEmail: String) {
// código omitido
}
// código omitido
Na linha 32 e 33, vamos realizar duas ações importantes. Primeiro, comentaremos as linhas do saveOrderToDatabase
e sendConfirmationEmail
para deixar a explicação mais clara.
Em seguida, chamamos o método orderPersistenceManager.SaveOrderToDatabase()
, passando o nosso pedido (a Order
). Após isso, chamaremos o método emailService.sendConfirmationEmail(to: customerEmail)
. Podemos remover a exceção entre essas linhas.
// código omitido
class OrderManager {
func createOrder(product: String, quantity: Int, customerEmail: String) {
// Lógica para criar um pedido
print("Pedido para \(product), quantidade \(quantity) criado.")
let order = Order(product: product, quantity: quantity, customerEmail: customerEmail)
//saveOrderToDatabase(order: order)
//sendConfirmationEmail(to: customerEmail)
orderPersistenceManager.saveOrderToDatabase(order: order)
emailService.sendConfirmationEmail(to: customerEmail)
}
// código omitido
No uso, é importante passar as dependências corretamente. Primeiro, criamos um persistenceManager
antes do nosso OrderManager
, definindo-o como uma instância de OrderPersistenceManager
. Em seguida, criaremos um let EmailService
como uma instância de EmailService
.
// código omitido
let persistenceManager = OrderPersistenceManager()
let emailService = EmailService()
// código omitido
E então, passamos as dependências na linha 54, durante a instanciação do nosso OrderManager
. Se houver um erro de "Missing arguments for parameters 'orderPersistenceManager', 'emailService' in call", podemos clicar em "Fix" no canto direito da mensagem para permitir que o Xcode nos auxilie nessa correção.
Começamos definindo o orderPersistenceManager
para gerenciar nossa persistência com persistenceManager
e, em seguida, configuramos o emailService
para lidar com nossos serviços de e-mail.
// código omitido
let orderManager = OrderManager(orderPersistenceManager: persistenceManager, emailService: emailService)
orderManager.createOrder(product: "Livro Swift", quantity: 1, customerEmail: "giovanna@gmail.com")
// código omitido
Ao executar o código clicando no ícone de play na parte inferior esquerda, verificamos no Playground.
Pedido para Livro Swift, quantidade 1 criado
Pedido salvo no banco de dados
Enviando email para giovanna2gmail.com
Agora que está tudo funcionando conforme o esperado, as classes estão estruturadas com responsabilidades únicas.
Parabéns pela compreensão do primeiro princípio do SOLID: o princípio da responsabilidade única (SRP). Isso implica que cada classe deve ter apenas uma razão para existir e uma razão para mudar, ou seja, deve possuir apenas uma responsabilidade específica. Isso resulta em um código mais fácil de manter e de modificar.
Até mais!
Agora que compreendemos mais sobre o princípio de responsabilidade única no Playground, vamos conhecer nosso aplicativo iOS que foi construído com UI Kit e com Vue Code, que nós vamos refatorar também para seguir os princípios SOLID.
Vamos seguir uma dinâmica organizada para entender melhor: primeiro, examinaremos um exemplo no Playground. Em seguida, aplicaremos os conceitos aprendidos ao refatorar um código de um aplicativo iOS chamado Swift Bank.
O Swift Bank é uma aplicação simples que simula funções bancárias básicas, como depósito, saque e visualização das últimas transações.
No simulador, clicamos em "Depósito" e na tela seguinte inserimos o valor R$100. Após confirmar clicando em "Confirmar depósito", recebemos um alerta confirmando o depósito e um botão "Ok".
Depósito efetuado
Você depositou R$100 com sucesso
OK
Clicamos em "Ok".
Ao voltar clicando em "Home" no canto superior esquerdo, observamos que as telas estão interconectadas usando o padrão Delegate. A tabela em "Últimas transações" exibe o depósito de R$100, incluindo data e hora da transação.
Primeiro, faremos um saque de R$50 clicando em "Saque" na parte superior direita da tabela e digitando "50" no campo "Faça um saque". Confirmaremos o saque selecionando o botão "Confirmar saque" e em seguida receberemos a confirmação de que o saque foi realizado com sucesso.
Saque efetuado
Você sacou R$50,00 com sucesso
OK
Ao retornarmos à nossa tela clicando em "Home" na parte superior esquerda, perceberemos que o saldo da nossa conta foi atualizado, e também veremos o registro do saque nas últimas transações. Além disso, realizaremos uma verificação para garantir que caso a pessoa queira sacar um valor maior do que o saldo disponível, isso não será possível.
Para isso, selecionamos "Saque" e digitamos o valor de R$500. Logo após, clicamos em "Confirmar saque". Obtemos a seguinte mensagem:
Erro ao sacar
Você não possui saldo o suficiente.
Vamos entender esse código.
Dentro da pasta Services
, temos a classe BankAccount
responsável por gerenciar os dados relacionados às operações bancárias.
BankAccount
import Foundation
enum BankOperation {
case withdraw, deposit
}
protocol BankingServiceDelegate: AnyObject {
func didPerformOperation()
}
protocol AccountServices {
func performOperation(operation: BankOperation, amount: Double) -> Bool
func requestLoan(amount: Double)
func calculateInterestRate()
}
// código omitido
Nessa classe, encontramos uma enumeração chamada BankOperation
, que lista os tipos de operações possíveis, tais como withdraw
(saque) e deposit
(depósito).
Além disso, contamos com o protocolo BankingServiceDelegate
, que utiliza o padrão Delegate para facilitar a comunicação entre diferentes componentes. Por exemplo, quando realizamos um depósito na tela de depósito, essa tela comunica a HomeViewController
, que é a tela inicial, sobre a operação realizada através do método didPerformOperation()
.
Nosso sistema inclui um protocolo denominado AccountServices
, o qual define os serviços disponíveis para uma conta. Esses serviços incluem a realização de operações, especificadas pelo parâmetro Operation
do tipo BankOperation
. Adicionar uma nova operação requer a inclusão de uma nova entrada no Enum
correspondente.
Além disso, a função performOperation()
é responsável por executar as operações e recebe o parâmetro amount
como um valor do tipo Double
, representando o valor.
Outra funcionalidade é o pedido de empréstimo, executado pela função requestLoan
, que também requer o parâmetro amount
para especificar o valor solicitado. Adicionalmente, temos a função CalculateInterestRate
, utilizada para determinar a taxa de juros aplicável.
Os métodos mencionados, RequestLoan
e calculateInterestRate
, ainda não estão implementados no sistema. Ao examinarmos a classe BankAccount
, que segue o protocolo AccountServices
, percebemos que as linhas 56 e 60 não contém implementações para esses métodos.
// código omitido
func requestLoan(amount: Double) {
// Pedir um empréstimo
}
func calculateInterestRate() {
// Calcular taxa de juros
}
// código omitido
Se fossemos realizar essas implementações, seria feito nesse ponto do código.
// código omitido
class BankAccount: AccountServices {
var balance: Double = 0.0
var accountNumber: String
var transactionsHistory: [String] = []
init(accountNumber: String) {
self.accountNumber = accountNumber
}
// código omitido
A classe BankAccount
possui três propriedades principais: balance
, que representa o saldo da conta e é inicializada em zero; accountNumber
, que é o número da conta; e transactionHistory
, um Array de Strings que registra as transações realizadas.
// código omitido
func performOperation(operation: BankOperation, amount: Double) -> Bool {
switch operation {
case .withdraw:
if amount <= balance {
balance -= amount
sendNotification(message: "Saque no valor de \(amount.formatCurrency()) realizado!")
transactionHistory.insert(message: "Saque no valor de \(amount.formatCurrency())", at: 0)
return true
}
return false
case .deposit:
balance += amount
sendNotification(message: "Depósito no valor de \(amount.formatCurrency()) realizado!")
transactionHistory.insert(message: "Depósito no valor de \(amount.formatCurrency())", at: 0)
return true
}
}
// código omitido
Dentro do método performOperation
, definido na linha 33, há uma verificação para determinar o tipo de operação: se é um saque ou um depósito. No caso de ser um saque, são realizadas verificações adicionais para garantir que a transação seja válida, retornando false
se não for possível realizar o saque e true
se for possível.
Também dispomos de um método chamado SendNotification
, o qual está definido na linha 51 do código. Contudo, esse método não executa qualquer ação real. Esta aplicação foi desenvolvida com o propósito de demonstração e ensino, não possui implementação funcional ativa.
Entretanto, a função SendNotification
está presente, sendo capaz de enviar notificações por e-mail, SMS, entre outros meios. Adicionalmente, ela registra no Array de TransactionHistory
a operação realizada e seu respectivo valor.
Dentro da pasta Extensions
, guardamos algumas extensões que apenas gerenciam a interface do usuário, então não vamos entrar em detalhes sobre elas.
Em seguida, na ViewControllers
, temos a HomeViewController
, que representa a tela inicial do aplicativo. Nesta tela, instanciamos o BankAccount
, passando um número de conta fictício que criamos.
Temos elementos como o texto de boas-vindas, o saldo disponível e o accountCardView
, que é a primeira parte da tela com um fundo branco. Nesse accountCardView
, encontramos os botões de depósito e saque, os quais são gerenciados pela View OperationView
, localizada na pasta Views
.
Na tela de Depósito (DepositViewController
), a pessoa usuária aciona o método didTapConfirmDepositButton()
para efetuar o depósito, que é descrito de forma clara e informativa. Esse método notifica sobre o sucesso ou falha do depósito através de alertas, seguindo a lógica fundamental do aplicativo.
É essencial reservar um tempo para analisar e compreender completamente o código, pois nossa próxima etapa é refatorá-lo conforme os princípios SOLID.
Ao examinar a classe BankAccount
no arquivo correspondente, é possível identificar inconsistências e oportunidades de melhoria.
Com esse entendimento do projeto, iniciaremos a refatoração, começando pelo primeiro princípio do SOLID, que é o de Responsabilidade Única.
O curso iOS: escrevendo código de qualidade com SOLID em Swift possui 105 minutos de vídeos, em um total de 47 atividades. Gostou? Conheça nossos outros cursos de iOS em Mobile, ou leia nossos artigos de Mobile.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.