Pesquisar

terça-feira, 2 de outubro de 2018

Tipos diferentes e com o mesmo nome, como resolver?


Quando trabalhamos em projetos grandes e/ou em uma equipe de desenvolvimento é muito comum encontrarmos nomes iguais para tipos diferentes. Esse cenário também pode acontecer quando utilizamos framerworks de terceiros.

Vamos exemplificar o problema citado sobre nomenclaturas iguais e as três formas de resolvermos.



Exemplificando o problema


Digamos que a classe A possui uma classe B e C. A classe C possui uma estrutura com mesmo nome B e um método que recebe como parâmetro um tipo B. Quando olharmos para o código, sabemos que o parâmetro do método myMethod da classe C, refere-se a estrutura B.

import Foundation
class A {
class B {
let number: Int
init(n: Int) {
self.number = n
}
}
class C {
struct B {
let str: String
}
static func myMethod(_ b: B) {
}
}
}


Porém o que devemos fazer quando precisamos que o parâmetro do myMethod faça referência a classe B externa a C?

Opção 1


A primeira opção e acredito que a melhor é renomear o tipo duplicado e que faz menos sentido. Para isso o Xcode lançou desde a versão 9 uma ferramenta que particularmente eu gosto, mesmo com seus "pequenos" probleminhas - refatoração baseada no cursor.

Xcode
Fonte: https://swift.org/blog/swift-local-refactoring/

Com ela você pode alterar de uma vez todos que estão utilizando o tipo escolhido para refatorar.

Opção 2

Outra forma de resolvermos é inserindo o módulo em que faz parte. Por exemplo, sabemos que o framework UIKit da Apple possui um tipo chamado de UIColor, mas digamos que queremos criar o nosso próprio como value type e não reference type. Isso é permitido? Utilizando o mesmo nome? Sim, veja como ficaria:

import UIKit
struct UIColor {
let value: UIKit.UIColor
init(_ color: UIKit.UIColor) {
self.value = color
}
}
let color = UIColor(UIKit.UIColor.black)
color.value.cgColor


Observe que no código acima quando refere-se ao UIColor do framework UIKIt, inserimos com a indicação do módulo, no caso fica UIKit.UIColor. A mesma estratégia podemos utilizar no código apresentando no início do artigo:

import Foundation
class A {
class B {
let number: Int
init(n: Int) {
self.number = n
}
}
class C {
struct B {
let str: String
}
static func myMethod(_ b: A.B) {
print("\(b.number)")
}
}
}


Essa opção só é válida se seu projeto possuir módulos separados, se os códigos não estiverem em um único target.

Opção 3

A terceira e última opção é declarar um type alias. Essa opção acho que só faz sentido se a renomeação for um problema ou não fizer sentido. Utilizando o código apresentado no início do artigo, podemos aplicar da seguinte maneira:

import Foundation
typealias BClass = A.B
class A {
class B {
let number: Int
init(n: Int) {
self.number = n
}
}
class C {
struct B {
let str: String
}
static func myMethod(_ b: BClass) {
print("\(b.number)")
}
}
}


A opção 2 já foi apresentada no Twitter pelo criador do Swift - Chris Lattner:

Fonte: https://twitter.com/clattner_llvm/status/474772713739792384