inheritance - protocol - Funções abstratas no Swift Language



how to create protocol in swift 4 (8)

Eu gostaria de criar uma função abstrata em linguagem rápida. É possível?

class BaseClass {
    func abstractFunction() {
        // How do I force this function to be overridden?
    }
}

class SubClass : BaseClass {
    override func abstractFunction() {
        // Override
    }
}

Bem, sei que estou atrasado para o jogo e que posso estar aproveitando as mudanças que aconteceram. Me desculpe por isso.

De qualquer forma, eu gostaria de contribuir com minha resposta, porque eu adoro fazer testes e as soluções com fatalError() são, AFAIK, não testáveis ​​e aquelas com exceções são muito mais difíceis de serem testadas.

Eu sugeriria usar uma abordagem mais ágil . Seu objetivo é definir uma abstração que tenha alguns detalhes comuns, mas que não esteja totalmente definida, ou seja, o (s) método (s) abstrato (s). Use um protocolo que defina todos os métodos esperados na abstração, os que são definidos e os que não são. Em seguida, crie uma extensão de protocolo que implemente os métodos definidos no seu caso. Finalmente, qualquer classe derivada deve implementar o protocolo, o que significa todos os métodos, mas os que fazem parte da extensão de protocolo já têm sua implementação.

Estendendo seu exemplo com uma função concreta:

protocol BaseAbstraction {
    func abstractFunction() {
        // How do I force this function to be overridden?
    }
}

extension BaseAbstraction {
    func definedFunction() {
        print("Hello")
}

class SubClass : BaseAbstraction {
    func abstractFunction() {
        // No need to "Override". Just implement.
    }
}

Observe que, ao fazer isso, o compilador é novamente seu amigo. Se o método não for "substituído", você receberá um erro em tempo de compilação, em vez daqueles que você obteria com fatalError() ou exceções que aconteceriam em tempo de execução.


Este parece ser o modo "oficial" de como a Apple está lidando com métodos abstratos no UIKit. Dê uma olhada no UITableViewController e no modo como ele está trabalhando com o UITableViewDelegate . Uma das primeiras coisas que você faz é adicionar uma linha: delegate = self . Bem, esse é exatamente o truque.

1. Coloque o método abstrato em um protocolo
protocol AbstractMethodsForClassX {
    func abstractMethod() -> String
}
2. Escreva sua classe base
/// It takes an implementation of the protocol as property (same like the delegate in UITableViewController does)
/// And does not implement the protocol as it does not implement the abstract methods. It has the abstract methods available in the `delegate`
class BaseClassX {
    var delegate: AbstractMethodsForClassX!

    func doSomethingWithAbstractMethod() -> String {
        return delegate.abstractMethod() + " - And I believe it"
    }
}
3. Escreva as subclasses.
/// First and only additional thing you have to do, you must set the "delegate" property
class ClassX: BaseClassX, AbstractMethodsForClassX {
    override init() {
        super.init()
        delegate = self
    }

    func abstractMethod() -> String {return "Yes, this works!"}
}
Aqui está, como você usa tudo isso
let x = ClassX()
x.doSomethingWithAbstractMethod()

Verifique com Playground para ver a saída.

Algumas observações

  • Primeiro, muitas respostas já foram dadas. Espero que alguém encontre todo o caminho até este.
  • A questão, na verdade, era encontrar um padrão que implementasse:
    • Uma classe chama um método que precisa ser implementado em uma de suas subclasses derivadas (substituído)
    • Na melhor das hipóteses, se o método não for substituído na subclasse, obtenha um erro durante o tempo de compilação
  • A coisa sobre métodos abstratos é que eles são uma mistura de uma definição de interface e parte de uma implementação real na classe base. Ambos ao mesmo tempo. Como a rapidez é muito nova e muito limpa, ela não tem essa convieniência, mas conceitos "impuros" (ainda).
  • Para mim (um pobre rapaz de Java), esse problema evolui de tempos em tempos. Eu li todas as respostas neste post e desta vez acho que encontrei um padrão, que parece viável - pelo menos para mim.
  • Atualização : Parece que os implementadores do UIKit na Apple usam o mesmo padrão. UITableViewController implementa o UITableViewDelegate mas ainda precisa ser registrado como delegado, definindo explicitamente a propriedade delegate .
  • Tudo isso é testado no Playground do Xcode 7.3.1

Eu entendo o que você está fazendo agora, eu acho que seria melhor você usar um protocolo

protocol BaseProtocol {
    func abstractFunction()
}

Então, você apenas está em conformidade com o protocolo:

class SubClass : BaseProtocol {

    func abstractFunction() {
        // Override
        println("Override")
    }
}

Se a sua classe também é uma subclasse, os protocolos seguem a Superclasse:

class SubClass: SuperClass, ProtocolOne, ProtocolTwo {}

Eu não sei se vai ser útil, mas eu tive um problema semelhante com o método abstraído ao tentar construir o jogo do SpritKit. O que eu queria era uma classe Animal abstrata que tivesse métodos como move (), run () etc, mas os nomes dos sprites (e outras funcionalidades) deveriam ser fornecidos pelos filhos da classe. Então acabei fazendo algo assim (testado para o Swift 2):

import SpriteKit

// --- Functions that must be implemented by child of Animal
public protocol InheritedAnimal
{
    func walkSpriteNames() -> [String]
    func runSpriteNames() -> [String]
}


// --- Abstract animal
public class Animal: SKNode
{
    private let inheritedAnimal: InheritedAnimal

    public init(inheritedAnimal: InheritedAnimal)
    {
        self.inheritedAnimal = inheritedAnimal
        super.init()
    }

    public required init?(coder aDecoder: NSCoder)
    {
        fatalError("NSCoding not supported")
    }

    public func walk()
    {
        let sprites = inheritedAnimal.walkSpriteNames()
        // create animation with walking sprites...
    }

    public func run()
    {
        let sprites = inheritedAnimal.runSpriteNames()
        // create animation with running sprites
    }
}


// --- Sheep
public class SheepAnimal: Animal
{
    public required init?(coder aDecoder: NSCoder)
    {
        fatalError("NSCoding not supported")
    }

    public required init()
    {
        super.init(inheritedAnimal: InheritedAnimalImpl())
    }

    private class InheritedAnimalImpl: InheritedAnimal
    {
        init() {}

        func walkSpriteNames() -> [String]
        {
            return ["sheep_step_01", "sheep_step_02", "sheep_step_03", "sheep_step_04"]
        }

        func runSpriteNames() -> [String]
        {
            return ["sheep_run_01", "sheep_run_02"]
        }
    }
}

Há outra alternativa para essa questão, embora ainda haja uma desvantagem quando comparada à proposta do @jaumard; requer uma declaração de retorno. Embora eu perca o ponto de exigi-lo, porque consiste em lançar diretamente uma exceção:

class AbstractMethodException : NSException {

    init() {
        super.init(
            name: "Called an abstract method",
            reason: "All abstract methods must be overriden by subclasses",
            userInfo: nil
        );
    }
}

E depois:

class BaseClass {
    func abstractFunction() {
        AbstractMethodException.raise();
    }
}

O que quer que venha depois disso é inacessível, então não vejo por que forçar o retorno.


Não há conceito de resumo no Swift (como o Objective-C), mas você pode fazer isso:

class BaseClass {
    func abstractFunction() {
        preconditionFailure("This method must be overridden") 
    } 
}

class SubClass : BaseClass {
     override func abstractFunction() {
         // Override
     } 
}

Uma maneira de fazer isso é usar um fechamento opcional definido na classe base, e os filhos podem optar por implementá-lo ou não.

class BaseClass {
    var abstractClosure?:(()->())?
    func someFunc()
    {
        if let abstractClosure=abstractClosure
        {
            abstractClosure()
        }
    } 
}

class SubClass : BaseClass {
    init()
    {
        super.init()
        abstractClosure={ ..... }
    }
}

Usando assert keyword para impor métodos abstratos:

class Abstract
{
    func doWork()
    {
        assert(false, "This method must be overriden by the subclass")
    }
}

class Concrete : Abstract
{
    override func doWork()
    {
        println("Did some work!")
    }
}

let abstract = Abstract()
let concrete = Concrete()

abstract.doWork()    // fails
concrete.doWork()    // OK

No entanto, como Steve Waddicor mencionou, você provavelmente quer um protocol .





swift