El concepto de control de acceso nos permite restringir cómo se puede acceder a tipos, funciones y otras declaraciones por otro código. Swift ofrece cinco niveles diferentes de control de acceso, y hacer un uso completo de ellos puede ser crucial para escribir programas que tengan preocupaciones claramente separadas y una estructura sólida.

Cuando definamos cualquier tipo, propiedad o función nuevos en Swift, tendrá el nivel de acceso internal por defecto. Esto significa que será visible para todos los demás códigos que se encuentren dentro del mismo módulo, como una aplicación, una extensión de sistema, un marco de trabajo o un paquete Swift.

Como ejemplo, digamos que estamos creando una aplicación de compras y que hemos definido una clase llamada PriceCalculator que nos permite calcular el precio total de una variedad de productos:

Como actualmente no estamos especificando ningún nivel de acceso explícito, nuestra clase PriceCalculator (y su método calculatePrice) serán accesibles desde cualquier lugar dentro de nuestra aplicación. Sin embargo, si estamos buscando compartir nuestra nueva clase con otros módulos (por ejemplo, podríamos implementarla dentro de un marco que compartamos entre nuestra aplicación principal y una extensión, o una aplicación complementaria de Apple Watch), entonces necesitaremos hacerla public para que sea visible dentro de esos contextos externos:

Sin embargo, el cambio anterior no es suficiente. Aunque ahora podemos encontrar nuestra clase fuera del módulo en el que está definida, no podemos crear ninguna instancia de ella, ya que su inicializador (implícito) es, al igual que cualquier otro código, internal por defecto. Para solucionarlo, definamos un inicializador public, que dejaremos vacío ya que no hay trabajo real que hacer dentro de él:

public class PriceCalculator { public init() {} ...}

Ahora podemos encontrar, inicializar y llamar a nuestro PriceCalculator tanto dentro como fuera de su módulo, fantástico. Pero ahora digamos que también estamos buscando subclasificarlo para modificarlo, o para agregarle una nueva funcionalidad. Si bien eso es posible actualmente dentro de su propio módulo, de nuevo es algo que se evita fuera de él.

Para cambiar eso, tendremos que usar el nivel de control de acceso más abierto de Swift, que se denomina apropiadamente open:

open class PriceCalculator { ...}

Con el cambio anterior en su lugar, ahora podemos crear subclases personalizadas de PriceCalculator en cualquier lugar, que pueden tener nuevos inicializadores, nuevas propiedades y nuevos métodos. Así es como podríamos usarlo para implementar un DiscountedPriceCalculator, que nos permite aplicar un discount dado a todos los cálculos de precios:

Arriba estamos definiendo un nuevo método de cálculo de precios, pero podría decirse que sería mucho más apropiado anular y modificar el método calculatePrice existente que heredamos de nuestra clase base. De esa manera, no habría confusión en torno a qué método llamar, y podríamos mantener nuestras dos clases consistentes.

Para poder hacer eso, nuevamente tenemos que marcar la declaración original, esta vez nuestra declaración de método calculatePrice, como open:

open class PriceCalculator { public init() {} open func calculatePrice(for products: ) -> Int { ... }}

Con lo anterior en su lugar, ahora podemos reemplazar libremente calculatePrice, en lugar de tener que crear un método separado:

Para que internal, public y open — que se utilizan para abrir gradualmente una declaración pública de uso y modificación. Pero, por supuesto, también podemos ir por el otro lado, y ocultar partes de nuestro código para que no sean descubiertas y utilizadas. Al principio, puede parecer cuestionable cuál es el valor de hacer eso, pero realmente puede ayudarnos a hacer que nuestra API sea mucho más estrecha y enfocada, lo que a su vez puede facilitar su comprensión, prueba y uso.

Así que ahora vamos al otro lado del espectro de nivel de acceso, y echemos un vistazo al nivel más restrictivo: private. Cualquier tipo, propiedad o método marcado como private solo será visible dentro de su propio tipo (que también incluye extensiones en ese tipo definido dentro del mismo archivo).

Cualquier cosa que deba considerarse un detalle de implementación privada de un tipo dado debe marcarse como private. Por ejemplo, la propiedad discount de nuestra calculadora de precios de antes solo estaba destinada a usarse dentro de su propia clase, así que sigamos adelante y hagamos que esa propiedad sea privada:

class DiscountedPriceCalculator: PriceCalculator { private let discount: Int ...}

Nuestra implementación anterior continuará funcionando exactamente de la misma manera que antes, ya que discount permanecerá completamente visible dentro de nuestra clase DiscountedPriceCalculator. Sin embargo, si quisiéramos ampliar ligeramente esa visibilidad para incluir también otros tipos definidos dentro del mismo archivo, tendríamos que usar fileprivate, que hace exactamente lo que parece, mantiene una declaración privada dentro del archivo en el que está definido:

class DiscountedPriceCalculator: PriceCalculator { fileprivate let discount: Int ...}

Con el cambio anterior en su lugar, ahora podemos acceder a nuestra propiedad discount desde el código relacionado definido en el mismo archivo, como esta extensión en UIAlertController que nos permite mostrar fácilmente una descripción de precio para una variedad de productos dentro de una alerta:

Cuando se trata de funciones, tipos y extensiones libres, private y fileprivate actúan exactamente igual. Solo son diferentes cuando se aplican a declaraciones definidas dentro de un tipo.

En resumen, estos son los cinco niveles de control de acceso que Swift ofrece actualmente:

  • private mantiene una propiedad o función privada dentro de su tipo envolvente, incluyendo cualquier extensión en que tipo definida en el mismo archivo. Cuando se aplica a un tipo, función o extensión de nivel superior, actúa de la misma manera que fileprivate.
  • fileprivate hace visible una declaración dentro de todo el archivo en el que está definida, mientras la oculta del resto del código.
  • internal es el nivel de acceso predeterminado y hace visible una declaración dentro de todo el módulo en el que está definido.
  • public revela una función, tipo, extensión o propiedad fuera de su módulo.
  • open permite que una clase sea subclase, y que una función o propiedad sea sobrescrita, fuera de su módulo.

En general, a menudo es mejor comenzar con el nivel de acceso más restrictivo que una declaración dada puede tener prácticamente, y luego abrir las cosas más tarde si es necesario. De esa manera, estamos limitando las vías de interacción entre nuestros diversos tipos y funciones, lo que al principio puede parecer algo malo, pero a menudo es verdaderamente esencial para construir sistemas bien estructurados y mantenibles.

Gracias por leer!

(Tenga en cuenta que este artículo no entró en modificadores de acceso específicos para mutaciones, como private(set). Estos serán cubiertos por otro artículo de Lo Basico en el futuro.)

Apoye Swift de Sundell echando un vistazo a este patrocinador:

Instabug: Resuelva errores, bloqueos y otros problemas mucho más rápido utilizando los rastros detallados de la pila, los registros de red y los eventos de interfaz de usuario que Instabug adjunta automáticamente a cada informe de errores. Utilizado tanto por mí como por miles de equipos de desarrollo de iOS de todo el mundo. Pruébalo gratis e intégrelo con una sola línea de código.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.