То, что вы здесь является делегат шаблон. Свойство вашего делегата просто называется class1Obj
, а не более обычным именем delegate
. Ключевая проблема здесь заключается в том, что вы не используете протокол, и в результате эти два класса «тесно связаны», то есть Class2
сильно зависит от деталей реализации Class1
. Кроме того, с этими двумя тесно связанными классами не сразу понятно, какие методы Class1
могут понадобиться Class2
. Это делает техническое обслуживание Class1
более сложным, потому что так легко случайно внести изменения, которые нарушают поведение в Class2
. Это также затрудняет использование Class2
в сочетании с другим классом, отличным от Class1
.
Вместо этого вы обычно объявляете протокол, чтобы точно сформулировать характер договора между Class2
и другими объектами, которые могут потребоваться для его использования. В результате классы менее плотно связаны, т. Е. Class2
ничего не должен знать о другом классе, отличном от его соответствия рассматриваемому протоколу. Кроме того, при редактировании Class1
, если вы заявляете, что он соответствует протоколу, компилятор предупредит вас, когда вы не сможете реализовать какой-либо необходимый метод или свойство.
Таким образом, при незначительном объеме работы, протокол делает код намного проще в обслуживании. Он также обеспечивает дополнительную гибкость, благодаря которой вы можете использовать Class2
в сочетании с чем-то, кроме Class1
в будущем.
В нижней строке, протоколы могут привести к коду, который проще в обслуживании и является более гибким, без каких-либо скрытых предположений.
Если вы не хотите использовать шаблон делегата протокола, другой альтернативой является использование закрытия, где Class1
поставляет блок кода, который может вызывать Class2
. Таким образом, вы можете сделать что-то вроде:
class Class1 {
var class2Obj = Class2()
init() {
class2Obj.handler = { [weak self] in // note `weak` reference which avoids strong reference cycle
self?.class1Method()
}
}
func class1Method() {
print("Parent")
}
}
class Class2 {
var handler: (() -> Void)?
func class2Method() {
handler?()
}
}
Хотя шаблон делегата протокола является полезным, когда у вас есть богатый интерфейс между этими двумя классами, это закрывающий шаблон, когда у вас есть очень простой интерфейс между этими двумя классами.
Откровенно говоря, более распространенная перестановка вышеопределяется тем, что замыкание более непосредственно связано с каким-либо конкретным запросом, что Class1
начинается с Class2
. Таким образом, вы можете просто сделать параметр закрытием соответствующего метода в Class2
. Кроме того, вы часто передавая данные обратно, так что представьте, что мы передаем назад дополнительный String
:
class Class1 {
var class2Obj = Class2()
func performClass2Method() {
class2Obj.class2Method { string in
guard let string = string else { return }
self.class1Method()
}
}
func class1Method() {
print("Parent")
}
}
class Class2 {
func class2Method(completionHandler: @escaping (String?) -> Void) {
// do something which creates `string`
// when done, call the closure, passing that `string` value back
completionHandler(string)
}
}
Эти закрывающие шаблоны отличный способ сделать простые интерфейсы между объектами, но и держит два класса очень слабо (т.е. Class2
не имеет зависимостей от Class1
), что очень похоже на использование протокола в шаблоне делегата. Но, надеюсь, приведенные выше примеры закрытия иллюстрируют простые альтернативы шаблону богатого делегата-протокола.
Да, вы можете общаться так.Но делегат, блок и закрытие более понятны для других людей, чтобы понять, что Class2 перекликается через «метод». Это вопрос «что такое лучшая практика». :) –
@YUNCHEN - Да, вы также можете использовать шаблон закрытия. Но я бы квалифицировал утверждение о том, что «наилучшей практикой» является использование закрытий. Как и большинство вариантов дизайна , это сложнее, чем это.Это действительно зависит: в простых случаях закрытие может быть полезно, но в более богатых интерфейсах шаблон делегат-протокола часто превосходит его, он зависит только от характера интерфейса. Но я согласен, что картина Аньшу двух плотно связанных классов субоптимальна. – Rob
@Rob, согласитесь с вами. –