Быстро, как определить абстрактный класс и почему Apple изобрела связанный тип, но не использует общий протокол

Я быстрый новичок. Что-то озадачило меня при обучении. Теперь я хочу определить абстрактный класс или определить какой-то чисто виртуальный метод, но я не могу найти способ сделать это. У меня есть протокол со связанным типом (это также озадачило меня, почему бы не использовать общий протокол), и некоторые методы должны быть реализованы в базовом классе, а другие классы, унаследованные от базового класса, должны реализовывать другие методы в протоколе, Как я могу сделать? например:

Ptotocol P{
    typealias TypeParam
    func A()
    func B()
}

class BaseClass<TypeParam> : P {
    abstract func A()
    func B(){
        if someCondition {
            A()
        }
    }
}

class ChildClass : BaseClass<Int> {
    func A(){}
}

Это кажется очень странным, и я до сих пор не могу найти метод решения абстрактной проблемы.

1 ответ

У Swift есть нечто похожее: расширения протокола

Они могут определять реализации по умолчанию, поэтому вам не нужно объявлять метод в базовом классе, но это также не заставляет делать это в любом классе, структуре или перечислении.

protocol P {
    associatedtype TypeParameter
    func A()
    func B()
}

extension P {
    func A(){}
}

class BaseClass<TypeParam> : P {
    typealias TypeParameter = TypeParam
    func B(){
        if someCondition {
            A()
        }
    }
}

class ChildClass : BaseClass<Int> {
    // implementation of A() is not forced since it has a default implementation
    func A(){}
}

Другой подход заключается в использовании протокола вместо BaseClass что больше соответствует протоколно-ориентированному программированию:

protocol Base {
    associatedtype TypeParameter
    func A()
    func B()
}

extension Base {
    func B(){
        if someCondition {
            A()
        }
    }
}

class ChildClass : Base {
    typealias TypeParameter = Int

    // implementation of A() is forced but B() is not forced
    func A(){}
}

Однако одним из больших недостатков было бы то, что переменная типа протокола может использоваться только в общем коде (как общее ограничение):

var base: Base = ChildClass() // DISALLOWED in every scope

В качестве обходного пути для этого ограничения вы можете сделать тип оболочки:

// wrapper type
struct AnyBase<T>: Base {
    typealias TypeParameter = T
    let a: () -> ()
    let b: () -> ()
    init<B: Base>(_ base: B) where B.TypeParameter == T {
        // methods are passed by reference and could lead to reference cycles
        // below is a more sophisticated way to solve also this problem
        a = base.A
        b = base.B
    }
    func A() { a() }
    func B() { b() }
}

// using the wrapper:
var base = AnyBase(ChildClass()) // is of type AnyBase<Int>

Что касается использования "настоящих" общих протоколов, команда Swift решила использовать associatedtype потому что вы можете использовать много общих типов без необходимости записывать все в скобках <>,

Например Collection где у вас есть связанный Iterator а также Index тип. Это позволяет вам иметь конкретные итераторы (например, для Dictionary а также Array).

В общем, универсальные / ассоциированные типы хороши для оптимизации кода во время компиляции, но в то же время они иногда слишком статичны, когда вам придется использовать универсальный тип-обертку.

Полезная ссылка на некоторые шаблоны для работы со связанными типами.


(См. Также выше)

Более сложный способ решить проблему передачи методов по ссылке.

// same as `Base` but without any associated types
protocol _Base {
    func A()
    func B()
}

// used to store the concrete type
// or if possible let `Base` inherit from `_Base`
// (Note: `extension Base: _Base {}` is currently not possible)
struct BaseBox<B: Base>: _Base {
    var base: B
    init(_ b: B) { base = b}
    func A() { base.A() }
    func B() { base.B() }
}

struct AnyBase2<T>: Base {
    typealias TypeParameter = T
    var base: _Base
    init<B: Base>(_ base: B) where B.TypeParameter == T {
        self.base = BaseBox(base)
    }
    func A() { base.A() }
    func B() { base.B() }
}

// using the wrapper:
var base2 = AnyBase2(ChildClass()) // is of type AnyBase2<Int>
Другие вопросы по тегам