inheritance - Clases abstractas en Swift Language
abstract-class subclass (8)
Creo que este es el más cercano al abstract
de Java o al abstract
C #:
class AbstractClass {
private init() {
}
}
Tenga en cuenta que, para que los modificadores private
funcionen, debe definir esta clase en un archivo Swift diferente.
EDITAR: Aún así, este código no permite declarar un método abstracto y así forzar su implementación.
¿Hay alguna manera de crear una clase abstracta en Swift Language, o es una limitación como Objective-C? Me gustaría crear una clase abstracta comparable a lo que Java define como una clase abstracta.
Después de luchar durante varias semanas, finalmente me di cuenta de cómo traducir una clase abstracta de Java / PHP a Swift:
public class AbstractClass: NSObject {
internal override init(){}
public func getFoodToEat()->String
{
if(self._iAmHungry())
{
return self._myFavoriteFood();
}else{
return "";
}
}
private func _myFavoriteFood()->String
{
return "Sandwich";
}
internal func _iAmHungry()->Bool
{
fatalError(__FUNCTION__ + "Must be overridden");
return false;
}
}
public class ConcreteClass: AbstractClass, IConcreteClass {
private var _hungry: Bool = false;
public override init() {
super.init();
}
public func starve()->Void
{
self._hungry = true;
}
public override func _iAmHungry()->Bool
{
return self._hungry;
}
}
public protocol IConcreteClass
{
func _iAmHungry()->Bool;
}
class ConcreteClassTest: XCTestCase {
func testExample() {
var concreteClass: ConcreteClass = ConcreteClass();
XCTAssertEqual("", concreteClass.getFoodToEat());
concreteClass.starve();
XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
}
}
Sin embargo, creo que Apple no implementó clases abstractas porque generalmente usa el patrón delegado + protocolo. Por ejemplo, el mismo patrón anterior se haría mejor de esta manera:
import UIKit
public class GoldenSpoonChild
{
private var delegate: IStomach!;
internal init(){}
internal func setup(delegate: IStomach)
{
self.delegate = delegate;
}
public func getFoodToEat()->String
{
if(self.delegate.iAmHungry())
{
return self._myFavoriteFood();
}else{
return "";
}
}
private func _myFavoriteFood()->String
{
return "Sandwich";
}
}
public class Mother: GoldenSpoonChild, IStomach
{
private var _hungry: Bool = false;
public override init()
{
super.init();
super.setup(self);
}
public func makeFamilyHungry()->Void
{
self._hungry = true;
}
public func iAmHungry()->Bool
{
return self._hungry;
}
}
protocol IStomach
{
func iAmHungry()->Bool;
}
class DelegateTest: XCTestCase {
func testGetFood() {
var concreteClass: Mother = Mother();
XCTAssertEqual("", concreteClass.getFoodToEat());
concreteClass.makeFamilyHungry();
XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
}
}
Necesitaba este tipo de patrón porque quería utilizar algunos métodos en UITableViewController, como viewWillAppear, etc. ¿Fue útil?
Hay una forma de simular clases abstractas usando Protocolos. Esto es un ejemplo:
protocol MyProtocol {
func doIt()
}
class BaseClass {
var myDelegate: MyProtocol!
init() {
...
}
func myFunc() {
...
self.myDelegate.doIt()
...
}
}
class ChildClass: BaseClass, MyProtocol {
override init(){
super.init()
self.myDelegate = self
}
func doIt() {
// Custom implementation
}
}
Intentaba crear una clase abstracta de Weather
, pero el uso de protocolos no era ideal, ya que tenía que escribir los mismos métodos de init
una y otra vez. Extendí el protocolo y escribí un método init
con sus problemas, especialmente porque estaba usando NSObject
conforme a NSCoding
.
Así que se me ocurrió esto para la conformidad de NSCoding
:
required init?(coder aDecoder: NSCoder) {
guard type(of: self) != Weather.self else {
fatalError("<Weather> This is an abstract class. Use a subclass of `Weather`.")
}
// Initialize...
}
En cuanto a init
:
fileprivate init(param: Any...) {
// Initialize
}
La forma más simple es usar una llamada a fatalError("Not Implemented")
en el método abstracto (no variable) en la extensión del protocolo.
protocol MyInterface {
func myMethod() -> String
}
extension MyInterface {
func myMethod() -> String {
fatalError("Not Implemented")
}
}
class MyConcreteClass: MyInterface {
func myMethod() -> String {
return "The output"
}
}
MyConcreteClass().myMethod()
No hay clases abstractas en Swift (al igual que Objective-C). Su mejor opción será usar un Protocol , que es como una interfaz Java.
Con Swift 2.0, puede agregar implementaciones de métodos e implementaciones de propiedades calculadas mediante extensiones de protocolo. Sus únicas restricciones son que no puede proporcionar variables o constantes de miembro y no hay despacho dinámico .
Un ejemplo de esta técnica sería:
protocol Employee {
var annualSalary: Int {get}
}
extension Employee {
var biweeklySalary: Int {
return self.annualSalary / 26
}
func logSalary() {
print("$/(self.annualSalary) per year or $/(self.biweeklySalary) biweekly")
}
}
struct SoftwareEngineer: Employee {
var annualSalary: Int
func logSalary() {
print("overridden")
}
}
let sarah = SoftwareEngineer(annualSalary: 100000)
sarah.logSalary() // prints: overridden
(sarah as Employee).logSalary() // prints: $100000 per year or $3846 biweekly
Tenga en cuenta que esto proporciona funciones similares a la "clase abstracta" incluso para las estructuras, pero las clases también pueden implementar el mismo protocolo.
También tenga en cuenta que cada clase o estructura que implementa el protocolo de Empleado tendrá que declarar nuevamente la propiedad anual de Salario.
Lo más importante es darse cuenta de que no hay un despacho dinámico . Cuando se llama a logSalary
en la instancia que está almacenada como un SoftwareEngineer
, llama a la versión anulada del método. Cuando se llama a logSalary
en la instancia una vez que se ha enviado a un Employee
, llama a la implementación original (no se distribuye dinámicamente a la versión reemplazada aunque la instancia sea en realidad un Software Engineer
.
Para obtener más información, consulte el excelente video de WWDC sobre esta característica: Creación de mejores aplicaciones con tipos de valores en Swift
Una forma más de cómo puede implementar la clase abstracta es bloquear el inicializador. Lo hice de esta manera:
class Element:CALayer { // IT''S ABSTRACT CLASS
override init(){
super.init()
if self.dynamicType === Element.self {
fatalError("Element is abstract class, do not try to create instance of this class")
}
}
}
Tenga en cuenta que esta respuesta está dirigida a Swift 2.0 y superior
Puede lograr el mismo comportamiento con protocolos y extensiones de protocolo.
Primero, escribe un protocolo que actúa como interfaz para todos los métodos que deben implementarse en todos los tipos que se ajustan a él.
protocol Drivable {
var speed: Float { get set }
}
Luego puede agregar un comportamiento predeterminado a todos los tipos que se ajusten a él
extension Drivable {
func accelerate(by: Float) {
speed += by
}
}
Ahora puede crear nuevos tipos implementando Drivable
.
struct Car: Drivable {
var speed: Float = 0.0
init() {}
}
let c = Car()
c.accelerate(10)
Entonces, básicamente, obtienes:
- Realice comprobaciones de tiempo que garanticen que todos los
Drivable
s implementen laspeed
- Puede implementar el comportamiento predeterminado para todos los tipos que se ajusten a
Drivable
(accelerate
) -
Drivable
está garantizado para no ser instanciado ya que es solo un protocolo
Este modelo realmente se comporta mucho más como rasgos, lo que significa que puede ajustarse a múltiples protocolos y asumir implementaciones predeterminadas de cualquiera de ellos, mientras que con una superclase abstracta está limitado a una jerarquía de clases simple.