У вас есть несколько опций, которые сводятся к тому, чтобы Class1 и Class2 использовали один и тот же экземпляр «общего объекта».
- Явно передавайте один и тот же экземпляр Class1 и Class2 извневместо того, чтобы позволить Class1 и Class2 самим создавать экземпляр;или
- Предоставьте одноэлементный инициализатор для класса Functions, затем убедитесь, что Class1 и Class2 используют это;или
- Используйте шаблон проектирования, известный как шаблон реестра, и убедитесь, что Class1 и Class2 получают свои экземпляры класса функций из реестра;или
- Использовать внедрение зависимостей (сложное).
Сценарий (1) проще всего понять, поскольку он выглядит именно так:
Functions *funcs = [[Functions alloc] init];
Class1 *obj1 = [[Class1 alloc] initWithFunctions:funcs];
Class2 *obj2 = [[Class2 alloc] initWithFunctions:funcs];
/* or alternatively use setters after initialization */
Сценарий (2) является следующим самым простым и очень распространенным.Какао предоставляет одноэлементные инициализаторы для многих своих классов.Всякий раз, когда вы видите +shared...
в качестве префикса для инициализатора, он, вероятно, возвращает синглтон.
@implementation Functions
+(id)sharedFunctions {
static Functions *sharedFunctions = nil;
@synchronized(self) {
if (sharedFunctions == nil) {
sharedFunctions = [[self alloc] init];
}
return sharedFunctions;
}
}
@end
Статическая переменная инициализируется только один раз, что означает, что вы можете лениво загрузить в нее экземпляр объекта, используяпроверьте его начальное значение (что происходит только один раз).Каждый последующий вызов метода возвращает один и тот же экземпляр.
Functions *f1 = [Functions sharedFunctions];
Functions *f2 = [Functions sharedFunctions];
/* f1 == f2; */ // always
Опция (3) не очень хорошо видна в target-C по сравнению с другими языками и достигает тех же целей, что и singleton.Идея в том, что у вас есть словарь объектов, которые можно искать по ключу.Все, что требует доступа к элементам в реестре, просто запрашивает у реестра собственный экземпляр.Обычно сам реестр представляет собой одноэлементный.
Опция (4), внедрение зависимостей, на самом деле является очень элегантным решением с рядом преимуществ за счет дополнительной сложности.Преимущества заключаются в том, что вы гарантировали, что зависимости всегда слабо связаны (что делает замену реализаций и независимое тестирование зависимостей гораздо проще).Сложность проистекает из нестандартных механизмов получения нужных вам экземпляров.
Внедрение зависимостей вращается вокруг идеи, что ни один объект не создает свои собственные зависимости.Вместо того, чтобы полагаться на что-то еще, чтобы обеспечить эти зависимости.Это «что-то еще», известное как контейнер для внедрения зависимостей.Контейнер внедрения зависимостей фактически является слоем поверх шаблона реестра, поскольку контейнер будет содержать предварительно созданные экземпляры зависимостей или знать, как создавать новые экземпляры.
В простейшем случаеВнедрение зависимостей - именно то, что я продемонстрировал в варианте (1).Вам даже не нужен контейнер для внедрения зависимостей.
Повышая уровень сложности, внедрение зависимостей вводит концепцию DI-контейнера, который инкапсулирует ряд существующих шаблонов проектирования (реестрКак видно в варианте (3), синглтон (вероятно, но не строго) и фабрика (чтобы знать, как создавать новые экземпляры объектов, которыми он управляет).
Демонстрация полной реализации контейнера DIвероятно, выходит за рамки этого вопроса, но с точки зрения общедоступного интерфейса одна из реализаций может выглядеть так:
DIContainer *container = [DIContainer sharedContainer];
[container registerClass:[ClassA class]];
[container registerClass:[ClassB class]];
[container registerDependentProperty:@selector(setClassA:)
withInstanceOf:[ClassA class]
forClass:[ClassB class]];
ClassB *obj = [container makeInstanceOfClass:[ClassB class]];
NSLog(@"ClassB's -classA type = %@", [[obj classA] class]);
Я просто набрал это в верхней части головы в середине этогоpost, так что не думайте, что он на 100% точен, но вы поняли концепцию. Контейнеру было поручено, что, когда он инициализирует экземпляры ClassB, он должен затем вызвать -setClassA:
, используя экземпляр ClassA
, который онтакже инициализируется в соответствии с правилами, определенными в контейнере (в этом случае нет никаких зависимостей ClassA
так что он просто возвращает простой экземпляр.
Если вы больше ничего не забираете из этого ответа, просто запомните опции (1) и (2);)