O melhor pedaço da Maçã.

Quaddro + MacMagazine: Swift na prática #10 — Orientação a Objetos

por Leandro Cissoto

Publicidade

Chegamos, enfim, ao nosso último artigo — já com aquela dor no coração de despedida — sobre Swift na parceria Quaddro + MacMagazine. Vamos fechar esta série de tutoriais com Orientação a Objetos na Swift. Veremos seus principais conceitos e vamos praticar um pouco com um exercício guiado.

Ícone da Swift

Lembramos que, para testar os conceitos aqui apresentados, recomendamos o uso do Xcode 6 ou superior. Se você não possui um Mac ou não quer instalar o Xcode, é possível utilizar uma ferramenta online que permite escrever em Swift diretamente pelo navegador.


Ícone do app Xcode
Xcode de Apple
Compatível com Macs
Versão 15.3 (3.4 GB)
Requer o macOS 14.0 ou superior
GrátisBadge - Baixar na Mac App Store Código QR Código QR

Teoria

A Programação Orientada a Objetos foi uma grande mudança de paradigma no desenvolvimento de aplicações, trazendo estruturas semelhantes às do mundo real para dentro da programação. Utilizando esse paradigma, podemos construir aplicações robustas e mais eficientes, gerando menos esforço para manutenção e mais reutilização de códigos.

Publicidade

CLASSES E OBJETOS

O primeiro e um dos mais importantes conceitos a serem entendidos é o de Classes, que podem ser compreendidas como abstrações de objetos que possuem características semelhantes. Um objeto, por sua vez, é quando instanciamos, ou seja, damos uma identidade/especificidade para uma classe.

Publicidade

Objetos podem armazenar estados através das suas propriedades, executar ações através de métodos e se relacionar além de enviar mensagens a outros objetos. Podemos dizer que criar um objeto é dar vida a uma classe.

A sintaxe para a criação de uma classe é a seguinte:

class NomeDaClasse [: NomeDaSuperclasse] {

}

Só para explicar, a parte que está entre colchetes é opcional e explicaremos mais sobre superclasse quando falarmos de herança.

Publicidade

A primeira vez que me deparei com esses termos — classes, objetos, propriedades, herança, etc. — admito que fiquei algum tempo fritando os neurônios para achar algum sentido, e na verdade é muito mais simples do que eu imaginava.

Vamos analisar a frase abaixo do ponto de vista da POO:

Gervásio é uma pessoa que possui o RG 12345.

Se formos levar a frase acima para o paradigma da orientação a objetos, podemos dizer que Gervásio é um objeto da classe pessoa, ou seja, estou dando características específicas — o nome Gervásio e o RG — para para algo abstrato (pessoa).

Agora, transportando para Swift, ficaria assim:

class Pessoa{
    var nome: String = ""
    var rg: String = ""
}

Essas variáveis dentro da classe são chamadas de propriedades, que veremos adiante. Vamos agora criar um objeto da classe Pessoa:

var gervasio: Pessoa = Pessoa()

Agora a variável gervasio é um objeto da classe Pessoa, e por esta razão tem acesso a suas propriedades e métodos.

PROPRIEDADES E MÉTODOS

Agora que temos um entendimento mais claro do que é uma classe, podemos falar sobre o que são propriedades e métodos.

Propriedades, também chamadas de atributos, compõem o conjunto de características que uma classe tem, e métodos constituem o que uma classe faz. Do ponto de vista técnico, ao declararmos variáveis ou constantes dentro do escopo de uma classe, estamos definindo as suas propriedades. Ao declararmos funções dentro do escopo da classe, estamos definindo métodos.

Para acessar propriedades e métodos de uma classe, utilizamos . depois de termos definido o objeto.

No exemplo abaixo vamos dar uma atualizada na nossa classe Pessoa, adicionando novas propriedades a ela, e daremos um pouco mais de funcionalidades através dos métodos.

class Pessoa{
    var nome: String = ""
    var rg: String = ""
    var idade: Int = 0
    var peso: Float = 0.0
    
    func andar(){
        print("Começou a Andar!")
    }
}

var gervasio: Pessoa = Pessoa()

//Defino as propriedades
gervasio.nome = "Gervásio da Silva"
gervasio.rg = "430287039"
gervasio.idade = 70
gervasio.peso = 72.0

//Utilizo os métodos
gervasio.andar()

ENCAPSULAMENTO

Através do encapsulamento podemos definir diferentes níveis de acesso para as classes, propriedades e métodos.

Utilizamos este conceito quando queremos definir como nossas classes, propriedades e métodos são acessados por outras classes ou objetos dentro da aplicação. Este conceito traz mais segurança e controle durante o desenvolvimento.

Existem três níveis de encapsulamento na Swift:

  • public – Permite acesso a qualquer outro elemento.
  • internal – Permite acesso apenas dentro da própria classe e nas classes herdeiras.
  • private – Permite acesso apenas dentro da classe na qual foi declarada.

Um exemplo de encapsulamento é a variável saldoBancário de um cliente de banco. Ela não pode ter um acesso público, senão qualquer parte do programa poderia mudar o seu valor. Neste caso, definimos a variável como privada para que seu valor seja alterado usando métodos que irão conter mecanismos de travas dependendo da operação.

Nota: por padrão, o nível de encapsulamento é internal.

class Pessoa {
    
    var nome: String = "Leandro"
    //Criamos a propriedade privada
    private var idade: Int = 28
    
    //Criamos o método que altera a idade
    func mudarIdade(novaIdade: Int)->Void{
        
        idade = novaIdade
    }
    func imprimeIdade()->Void{
        println(idade)
    }
}

//Criamos o objeto da classe
var humano : Pessoa = Pessoa()
       
humano.idade = 35
//Resultado: erro
   
//Faz a alteração da idade, usando o método da classe
humano.mudarIdade(35)
humano.imprimeIdade()
//Resultado: 35

Nota: o encapsulamento do Swift funciona apenas se a classe e sua instância estiverem em arquivos separados.

MÉTODOS INICIALIZADORES

Podemos utilizar métodos inicializadores para serem executados no momento em que instanciamos nossa classe. Através deles, podemos atribuir valores às propriedades, executar métodos da classe ou da superclasse e realizar qualquer tipo de rotina necessária no momento da criação do objeto.

Esses métodos também são chamados de construtores, e em Swift utilizamos a palavra reservada init para serem criados no contexto da classe.

//Criamos um classe com o métodos inicializador
class Empresa{
    
    var cnpj: String = String()
    var nomeFantasia: String = String()
    var faturamentoAnual: Double = Double()
    
    
    init(cnpj: String, nomeFantasia: String, faturamentoAnual: Double){
        self.cnpj = cnpj
        self.nomeFantasia = nomeFantasia
        self.faturamentoAnual = faturamentoAnual
        
        println("Iniciando a classe Empresa: Nome: \(self.nomeFantasia)")
    }
}

//Crio um objeto e utilizo seu inicializador
var petrobras: Empresa? = Empresa(cnpj: "3299420424-24", nomeFantasia: "BR", faturamentoAnual: 2_000_000)

willSet e didSet

Os observadores didSet e willSet provêm uma maneira de responder corretamente quando uma propriedade tem seu valor definido/alterado. O observador willSet é chamado antes de o valor ser atribuído a uma propriedade, já o observador didSet é chamado depois de uma propriedade ter recebido um valor.

Você pode estar se perguntando: “Onde vou usar esse negócio estranho?” Eu lhe respondo com um exemplo: vamos supor que toda vez que você usa um Array para preencher uma tableview. Você pode utilizar o didSet por exemplo para, toda vez que houver alteração no conteúdo do Array, a tableview executar o método reloadData, responsável por atualizar seu conteúdo.

class Abastecer{
    var contador: Int = Int(){
        willSet(novaContagem){
            print("Abastecer \(novaContagem) litros")
        }
        didSet{
            var novoValor = oldValue
            
            if contador > novoValor{
                print("Abasteceu \(contador + novoValor) litros")
            }
        }
    }
}

let abastecer = Abastecer()
abastecer.contador = 30
abastecer.contador = 50
abastecer.contador = 45

HERANÇA E POLIFORMISMO

A capacidade de uma classe de herdar as propriedades e métodos de outra classe é chamada de herança. Essa é uma das ferramentas mais poderosas da orientação a objetos, pois através dela podemos criar hierarquias e aumentamos o nível de abstração.

A herança nos permite muitas possibilidades durante o desenvolvimento através da transferência de propriedades e métodos da superclasse para suas subclasses. Veja o exemplo abaixo:

//Classe Pai / SuperClasse (Humano)
class Humano {
    var nome: String = ""
    var idade: Int = 0
    
    func andar(){
        println("O humano está andando")
    }
    
}

//Classe Filha / SubClasse (Filha)
class Atleta : Humano {
    
}

//Outra classe que herda as funcionalidade de Humano
class Funcionario : Humano{
    
}

//Crio um objeto da classe Humano
let pessoa: Humano = Humano()
pessoa.nome = "Roberto"
println("O Nome da pessoa é \(pessoa.nome)")
//Resultado: imprime "O Nome da pessoa é Danilo"

//Crio um objeto da classe Atleta
let maratonista: Atleta = Atleta()
maratonista.nome = "Leandro"
println("O Nome do atleta é \(maratonista.nome)")
//Resultado: imprime "O Nome do atleta é Leandro"

//Crio um objeto do tipo Funcionario
let vendedor: Funcionario = Funcionario()
vendedor.nome = "Gustavo"
println("O nome do funcionário é \(vendedor.nome)")
//Resultado: imprime "O Nome do funcionário é Gustavo"

A subclasse pode ter suas próprias propriedades e métodos, e estes não podem ser acessados pela superclasse, já que o fluxo da herança é sempre da superclasse para a subclasse. Veja o exemplo abaixo:

class Animal {
    var peso: Double = 0.0
    var altura: Double = 0.0
    
    func comer(){
        println("O Animal está comendo")
    }
    
}

class Cachorro: Animal {
    var raca: String = ""
    
    func latir(){
        println("O cachorro está latindo")
    }
}

//Crio um objeto da superclasse animal
let gato: Animal = Animal()
gato.peso = 15.5
gato.altura = 0.30
gato.comer()
gato.latir()
//Resultado: erro ao tentar executar o método latir, que é da subclasse

//Crio um objeto da subclasse cachorro
let boxer: Cachorro = Cachorro()
boxer.peso = 45.2
boxer.altura = 1.00
boxer.raca = "Boxer"
boxer.comer()
boxer.latir()
//Resultado: executa todos os métodos, tanto da classe "Cachorro" quanto da classe "Animal"

POLIFORMISMO

Outro conceito importante dentro do paradigma de orientação a objetos é o polimorfismo, que é a capacidade de uma subclasse sobrescrever métodos e propriedades de uma superclasse. Em Swift, utilizamos a palavra reservada override antes do nome do método para dizermos que este está sendo alterando. A palavra override irá verificar pelo compilador se existe na superclasse o respectivo método. Este processo é altamente importante para definição correta de um método herdado.

Veja o exemplo abaixo:

//Classe Pai / SuperClasse (Humano)
class Humano {
    
    func andar() {
        println("Método andar na classe Humano")
    }
}
let humano : Humano = Humano()
humano.andar()
//Resultado: imprime "Método andar na classe Humano"

//Classe Filha / SubClasse (Filha)
class Atleta : Humano {
    override func andar() {
        println("Método andar na classe Atleta")
    }
}

let atleta: Atleta = Atleta()
atleta.andar()
//Resultado: imprime "Método andar da classe Atleta"

Para evitar que a herança substitua a rotina de programação de um método, subscripts ou propriedade, podemos utilizar a palavra reservada final na declaração do método.

Veja o exemplo abaixo:

//Classe Pai / SuperClasse (Humano)
class Humano {
       final func andar() {
           println("Método andar na classe Humano")
       }
}

//Classe Filha / SubClasse (Filha)
class Atleta : Humano {
       
       override func andar() {
           println("Método andar na classe Atleta")
       }
}

let atleta: Atleta = Atleta()
atleta.andar()
//Resultado: erro

Veja que no exemplo acima não conseguiremos alterar o método andar(), pois ele contém a palavra final em sua estrutura e o compilador irá apresentar um erro quando tentarmos fazer a reescrita do método.

Exercício guiado

Agora que já vimos os principais conceitos para trabalharmos com Orientação a Objetos em Swift, vamos colocar a mão na massa em um exercício prático.

  1. Crie um novo arquivo chamado “POO” e deixe-o em branco.

Exercício sobre Swift

  1. Aproveitando que no ano que vem teremos as Olimpíadas aqui no Brasil, e como sou um grande fã de artes marciais, vamos criar uma classe Lutador e duas classes herdeiras — Karateca e Judoca — para vermos melhor o conceito de herança e polimorfismo.
class Lutador{

}


class Judoca : Lutador {

}

class Karateca: Lutador {
  
}

Exercício sobre Swift

  1. Vamos criar alguns métodos na classe Lutador. Lembrando que, como as classes Judoca e Karateca são herdeiras, podem fazer uso das propriedades e métodos de sua superclasse. Começaremos pelas propriedades. Por ser uma classe abstrata, devemos dar ao Lutador propriedades que também façam sentido para suas classes herdeiras. Podemos dizer, com certeza, que um lutador tem um nome, um sexo e que, para justificar ser um lutador, possui uma série de movimentos específicos de luta. Como são varios movimentos, podemos dizer que essa propriedade será um Array de String, para que o nosso lutador possa escolher entre uma coleção de golpes. Um lutador que se preze também cumprimenta seus adversários e luta. Então vamos criar estes métodos.
class Lutador{
    
    var nome:String = String()
    var sexo: Character = " "
    var movimentos: Array<String> = Array()
    
    func cumprimentar(){
        println("Cumprimento geral")
    }
    
    func lutar(){
        println("Luta sem regras")
    }
}

Exercício sobre Swift

  1. Agora que nossa classe Lutador está pronta, vamos nos concentrar nas suas subclasses, começando pela classe Judoca. Um judoca possui as mesmas características que um lutador, mais alguns diferenciais. Podemos dizer que nem todo tipo de luta possui um sistema de graduação baseado em faixas, mas o judô sim. Vamos criar essa propriedade, então. Outro detalhe relevante é que um judoca também possui um cumprimento específico — na verdade mais de um, mas para o exemplo usaremos só um, mesmo — e o judô tem algumas regras de luta que precisam ser respeitadas. Neste caso, não poderemos utilizar os métodos da superclasse porque se mostram inadequados para a realidade do judoca. Teremos, então, que utilizar o conceito de polimorfismo para alterar esses métodos. Para isso, utilizamos a palavra override do método, e assim podemos sobrescrevê-lo. A classe Judoca ficou assim:
class Judoca : Lutador {
    
    var faixa: String = String()
    
    override func cumprimentar(){
        println("Tachi-rei")
    }
    
    override func lutar(){
        println("Lutando nas regras do Judô")
    }
}

Exercício sobre Swift

  1. O mesmo acontece com a classe Karateca: ele possui um sistema de graduação específico, um cumprimento diferente e luta com regras diferentes. Como adicional, podemos criar a propriedade estilos, pois o caratê possui muitos estilos diferentes, como o Shotokan e o Wado-ryu por exemplo. Vamos criar essa propriedade como um Array de String, pois um carateca pode conhecer vários estilos. Nossa classe Karateca fica assim:
class Karateca: Lutador {
    
    var faixa: String = String()
    var estilos: Array<String> = Array()
    
    override func cumprimentar(){
        println("Oss")
    }
    
    override func lutar(){
        println("Lutar nas regras do Karate")
    }
}

Exercício sobre Swift

Tudo junto fica assim:

class Lutador{
    
    var nome:String = String()
    var sexo: Character = " "
    var movimentos: Array<String> = Array()
    
    func cumprimentar(){
        println("Cumprimento geral")
    }
    
    func lutar(){
        println("Luta sem regras")
    }

}

class Judoca : Lutador {
    
    var faixa: String = String()
    
    override func cumprimentar(){
        println("Tachi-rei")
    }
    
    override func lutar(){
        println("Lutando nas regras do Judô")
    }
}

class Karateca: Lutador {
    
    var faixa: String = String()
    var estilos: Array<String> = Array()
    
    override func cumprimentar(){
        println("Oss")
    }
    
    override func lutar(){
        println("Lutar nas regras do Karate")
    }
}

Exercício sobre Swift

  1. Vamos criar alguns objetos para testarmos as nossas classes.
//Criando um karateca 👊
let oyama: Karateca = Karateca()
oyama.nome = "Masutatsu Oyama"
oyama.sexo = "M"
oyama.faixa = "Preta"
oyama.estilos = ["Shotokan", "Goju-ryu", "Kyokushinkai"]
oyama.movimentos = ["Ague-zuki", "Haito-uti", "Shuto-uti"]

//Hora de o karateca lutar
oyama.cumprimentar()
oyama.lutar()

//Criando um judoca 👊
let koga : Judoca = Judoca()
koga.nome = "Toshihiko Koga"
koga.sexo = "M"
koga.faixa = "Preta"
koga.movimentos = ["Nage-waza", "Te-waza", "Mae-sutemi-waza", "Osaekomi-waza"]

//Hora de o judoca lutar
koga.cumprimentar()
koga.lutar()

//Criando um lutador 👊
let mummy: Lutador = Lutador()
mummy.nome = "Amanda Mummy"
mummy.sexo = "F"
mummy.movimentos = ["Dedo no olho", "Chute na canela", "Cruzado de direita"]

//Hora de o lutador lutar
mummy.cumprimentar()
mummy.lutar()

Exercício sobre Swift

Olhando para este exemplo, consigo pensar em um jogo de luta baseado em turnos em que cada movimento poderia receber um valor de ataque, e cada lutador poderia ter um nível de energia.
Deixo esta sugestão para vocês quebrarem a cabeça em cima dessa ideia. 😉

Desafio

Pedra, papel e tesoura

  • Criar uma classe chamada de Jogo.
  • Criar uma variável que será uma String contendo a escolha do usuário.
  • Criar uma variável que será uma String contendo a escolha do computador.
  • Criar uma variável que será uma String contendo o resultado.
  • Criar um willSet/didSet na variável que contém a escolha do usuário.
  • No willSet, definir a escolha do computador.
  • No didSet, verificar o vencedor.
  • Criar um método chamado regra, que irá dizer o vencedor, lembrando que:
    • papel »» pedra
    • pedra »» tesoura
    • tesoura »» papel

Dica: pesquise por “arc4random_uniform” para realizar a escolha do computador.

É um desafio bem bacana que certamente abrirá a sua mente em relação ao uso de didSet e willSet.

·   ·   ·

Lembramos que no nosso GitHub você encontra os exercícios de todos os artigos, bem como as soluções dos desafios. Lá também você encontrará outros exemplos com classes sobre temas que não abordei neste artigo, mas que certamente valem o estudo.

Ainda há muitas coisas sobre a Swift, como protocolos, generics, subscripts e outros temas que certamente valem a pena estudar. Qualquer dificuldade, dúvida ou sugestão, podem enviar um email para mm@quaddro.com.br ou escrever nos comentários abaixo. Terei o maior prazer em ajudá-los nessa jornada.

Gostaria de agradecer ao MacMagazine por permitir que estes artigos fossem realizados e por abrir novamente a oportunidade para que possamos compartilhar nosso conhecimento com seus leitores, fortalecendo ainda mais essa parceria que ainda vai gerar muitos outros frutos. 🙂

Um obrigado especial a vocês, leitores, que acompanharam as matérias e participaram nos comentários. Vocês foram a nossa grande motivação para esta série de artigos!

A Swift ainda vai dar muito o que falar, e as novidades não param por aqui: a Swift 2 está chegado e, com ela, mais um artigo com as principais novidades para vocês. Estude, faça exercícios, quebre a cabeça, mas principalmente, divirta-se com a Swift. Garanto que cada linha de código valerá a pena.

Até breve!

Ver comentários do post

Compartilhe este artigo
URL compartilhável
Post Ant.

“Apple Car”: empresa continua recrutando especialistas do mercado automotivo

Próx. Post

Skype 6.0 é lançado para iOS, totalmente redesenhado

Posts Relacionados