Существует много возможных ответов на этот вопрос в зависимости от того, как используется MyClass. Как написано, конечно, нет никакой причины для someProperty
быть в базовом классе вообще, и определенно нет причин для initProperties()
метода (который должен быть в init
).
Я понимаю, что это «просто пример», но он демонстрирует общую проблему создания ненужных иерархий. Есть способы написания этого кода с использованием полуабстрактного базового класса, но, как правило, этого следует избегать, поэтому первый вопрос - для чего вы его используете, и можем ли мы полностью избежать этой проблемы?
Чтобы ответить на заданный вопрос, вы, вероятно, начнете с установки по умолчанию SomeBaseClass
, чтобы абстрактный класс мог просто присвоить someProperty = SomeBaseClass()
.
Если это невозможно, обычно вы используете тип !
:
let someProperty: SomeBaseClass!
И вы реализуете initProperties()
с помощью fatalError
:
func initProperties() { fatalError("Implement in subclass") }
С другой стороны, может быть удобно реализовать someProperty
как вычисляемую переменную и реализовать ее на основе некоторого другого свойства в подклассах
var someProperty: SomeBaseClass { fatalError() }
Но это действительно последнее средство. Каждый раз, когда вам приходится писать fatalError
, вы, вероятно, ошибаетесь, и вам не нужен трюк, чтобы обойти это; вам нужно пересмотреть проблему.
Сначала вы должны подумать о том, как используется *1029*, используется , и подумать, может ли это быть типом значения. Отдельно вы должны подумать о том, может ли это быть протокол, соответствующий сценарию использования. Протоколы - это не просто абстрактные интерфейсы, которые скрывают реализации. Они представляют собой соответствующий тип для решения конкретной проблемы. Вот почему существует протокол Collection, который предоставляет доступ к десяткам алгоритмов для многочисленных, иначе не связанных типов, а не ArrayProtocol
просто для того, чтобы скрыть реализацию Array. Не превращайте MyClass
в MyClassProtocol
. Спросите, какие алгоритмы хотят использовать такие типы, как этот.
Когда вы создаете взаимосвязанные иерархии типов (подклассы чего-то, что требует подклассов чего-то другого), вы часто решаете проблему в неправильном направлении. Вы должны переосмыслить, можете ли вы решить проблему так, чтобы различные части SomeBaseClass
фактически были частью MyClass
(часто это упрощает SomeBaseClass
; например, это чистые данные, а не логика).
Здесь нет одного правильного ответа. Это зависит от природы MyClass
, поэтому мы не можем обсуждать это в абстрактных терминах. Как абстрактные классы, решение абстрактных задач часто ведет вас по неверным путям. Часто лучше начать с конкретных типов, а затем найти их сходства и извлечь их.
Даже с учетом вышесказанного, стоит показать, как простой, наивный протокол будет выглядеть здесь. (Возможно, это даже правильный протокол.)
public protocol MyProtocol {
var someProperty: SomeBaseClass { get }
}
Вот и все. Это все, что вы на самом деле выражаете в своем текущем абстрактном классе (и на самом деле неясно, является ли someProperty
публичным; если он приватный, этот протокол будет пустым).
Реализующая структура будет выглядеть так:
struct MyStruct: MyProtocol {
var someProperty: SomeBaseClass
}
Или, если вы хотите ссылочный тип, вы можете использовать последний класс:
final class MyClass: MyProtocol {
var someProperty: SomeBaseClass
init() {
someProperty = ...
}
}
Или, если вы хотите наследовать, вы можете использовать не финальный класс:
class MyClass: MyProtocol {
var someProperty: SomeBaseClass
init() {
someProperty = ...
}
}