2017-01-22 3 views
1

Можем ли мы общаться между классами, как показано ниже, вместо использования шаблона делегирования/протокола и уведомления. Я не видел подобного кода. Но просто любопытно узнать, почему это должно работать или не работать.Слабая ссылка между родительским и дочерним классами

class Class1 { 

    var class2Obj: Class2 = Class2() 
    init() { 
     class2Obj.class1Obj = self 
    } 

    func class1Method() { 
     print("Parent") 
    } 
} 

class Class2 { 

    weak var class1Obj: Class1? 

    func class2Method() { 
     class1Obj.class1Method() 
    } 

} 
+0

Да, вы можете общаться так.Но делегат, блок и закрытие более понятны для других людей, чтобы понять, что Class2 перекликается через «метод». Это вопрос «что такое лучшая практика». :) –

+0

@YUNCHEN - Да, вы также можете использовать шаблон закрытия. Но я бы квалифицировал утверждение о том, что «наилучшей практикой» является использование закрытий. Как и большинство вариантов дизайна , это сложнее, чем это.Это действительно зависит: в простых случаях закрытие может быть полезно, но в более богатых интерфейсах шаблон делегат-протокола часто превосходит его, он зависит только от характера интерфейса. Но я согласен, что картина Аньшу двух плотно связанных классов субоптимальна. – Rob

+0

@Rob, согласитесь с вами. –

ответ

3

То, что вы здесь является делегат шаблон. Свойство вашего делегата просто называется 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), что очень похоже на использование протокола в шаблоне делегата. Но, надеюсь, приведенные выше примеры закрытия иллюстрируют простые альтернативы шаблону богатого делегата-протокола.

Смежные вопросы