Masz kilka opcji, z których wszystkie opierają się na zapewnieniu, że Class1 i Class2 używają tego samego wystąpienia "obiektu udostępnionego".
- Wyraźnie przekazuj tę samą instancję do klasy 1 i klasy 2 z zewnątrz, zamiast zezwalać klasie 1 i klasie 2 na tworzenie samej instancji; lub
- Należy podać inicjator singletonu do klasy Functions, a następnie upewnić się, że Class1 i Class2 używają tego; lub
- Użyj wzorca projektowego znanego jako wzorzec rejestru i upewnij się, że klasy 1 i klasa 2 pobierają instancję klasy funkcji z rejestru; lub
- Użyj zależnego wtrysku (złożonego).
Scenariusz (1) jest najprostszym do zrozumienia, ponieważ idzie dokładnie tak:
Functions *funcs = [[Functions alloc] init];
Class1 *obj1 = [[Class1 alloc] initWithFunctions:funcs];
Class2 *obj2 = [[Class2 alloc] initWithFunctions:funcs];
/* or alternatively use setters after initialization */
Scenariusz (2) jest kolejnym najprostszym i jest bardzo powszechne. Kakao zapewnia inicjatory singletonowe dla wielu jego klas. Za każdym razem, gdy widzisz +shared...
jako prefiks dla inicjalizatora, prawdopodobnie zwraca on singleton.
@implementation Functions
+(id)sharedFunctions {
static Functions *sharedFunctions = nil;
@synchronized(self) {
if (sharedFunctions == nil) {
sharedFunctions = [[self alloc] init];
}
return sharedFunctions;
}
}
@end
Zmienna statyczna jest inicjowana tylko raz, co oznacza, że można leniwie załadować instancję obiektu do niego za pomocą czek na jego wartość początkową (który występuje tylko raz). Każde kolejne wywołanie metody zwraca tę samą instancję.
Functions *f1 = [Functions sharedFunctions];
Functions *f2 = [Functions sharedFunctions];
/* f1 == f2; */ // always
Wariant (3) nie są bardzo widoczne w cel-C w porównaniu z innymi językami i realizuje wiele tych samych celów jak pojedyncza. Chodzi o to, że masz słownik obiektów, które można wyszukać za pomocą klucza. Wszystko, co wymaga dostępu do elementów w rejestrze, prosi rejestr o własną instancję. Zazwyczaj rejestr byłby singleton.
Opcja (4), zastrzyk zależności, jest bardzo eleganckim rozwiązaniem z wieloma korzyściami kosztem dodatkowej złożoności. Korzyści wynikają z faktu, że masz pewność, że zależności są zawsze luźno powiązane (co sprawia, że wymienianie implementacji i testowanie zależności jest o wiele prostsze). Złożoność wynika z niestandardowych mechanizmów pobierania instancji tego, czego potrzebujesz.
Wstrzykiwanie zależności dotyczy pomysłu, że żaden obiekt nie tworzy własnych zależności. Zamiast polegać na czymś innym, aby zapewnić te zależności. To "coś innego" jest znane jako pojemnik z wtryskiem zależności. Kontener wstrzykiwania zależności jest faktycznie warstwą na wierzchu wzorca rejestru, ponieważ kontener będzie albo przechowywać wstępnie zbudowane instancje zależności, albo będzie wiedział, jak utworzyć instancje nowych instancji.
W najprostszym przypadku iniekcja zależności jest dokładnie tym, co zademonstrowałem w opcji (1). Do tego celu nie potrzebujesz nawet kontenera do iniekcji zależności.
Przesuwając poziom złożoności, wtrysk zależności wprowadza pojęcie kontenera DI, który obejmuje kilka istniejących wzorców projektowych (rejestr, jak widać w opcji (3), singleton (prawdopodobnie, ale nie ściśle)) i fabryki (aby dowiedzieć się, jak tworzyć nowe instancje obiektów, którymi zarządza).
Demonstrowanie pełnego wdrożenia kontenera DI prawdopodobnie wykraczałoby poza zakres tego pytania, ale z punktu widzenia interfejsu publicznego, jedna realizacja może wyglądać następująco:
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]);
po prostu wpisane, że od szczytu głowy w w środku tego postu, więc nie zakładaj, że jest on w 100% dokładny, ale masz pojęcie. Kontener został poinformowany, że podczas inicjowania wystąpień klasy B musi wywołać -setClassA:
, używając instancji ClassA
, którą również inicjuje zgodnie z regułami zdefiniowanymi w kontenerze (w tym przypadku nie ma zależności od ClassA
, więc po prostu zwraca zwykły przypadek
Zażycie nic więcej od tej odpowiedzi, ale należy pamiętać opcje (1) i (2);).
Pozdrawiam człowieka, to naprawdę przydatne. Poszedłem na opcję 2 i działa dobrze. Opcje 3 i 4 trochę przeszły mi przez głowę, ale ponownie przeczytam je jutro po tym, jak trochę się przespałem. Rano może mieć sens. Dzięki za pomoc, bardzo przydatne. :-D – Baza207
Nie ma problemu, uważam, że wybór opcji (2) jest dobry.Opcja (1) jest "technicznie" najlepsza, ponieważ singleton ma pewną złą reputację wśród wielu programistów, ze względu na trudność, z jaką można ją przetestować (nieczyste środowisko testowe). Jednak poprawnie użyte jest idealnie. W rzeczywistości, ponieważ Objective-C nie ma jednego ekskluzywnego inicjalizatora/konstruktora, jak inne języki, singleton nie jest w 100% wymuszony, tak więc można go wykorzystać w testowaniu w każdym przypadku. Opcja 3 jest dość prosta, ale tak, opcja (4) to temat, który wprawia w zakłopotanie nawet doświadczonych programistów. – d11wtq
Wiem, że to pytanie jest stare, ale pomogło mi to całkowicie. +1 –