koncepcja kontroli dostępu pozwala nam ograniczyć dostęp do typów, funkcji i innych deklaracji za pomocą innego kodu. Swift oferuje pięć różnych poziomów Kontroli dostępu, a pełne ich wykorzystanie może mieć kluczowe znaczenie w pisaniu programów, które mają wyraźnie oddzielone obawy i solidną strukturę.
kiedy zdefiniujemy dowolny nowy typ, właściwość lub funkcję w Swift, będzie ona miała domyślnie poziom dostępu internal
. Oznacza to, że będzie on widoczny dla wszystkich innych kodów znajdujących się w tym samym module — takich jak aplikacja, rozszerzenie systemu, framework lub pakiet Swift.
jako przykład, powiedzmy, że budujemy aplikację zakupową i że zdefiniowaliśmy klasę o nazwie PriceCalculator
, która pozwala nam obliczyć całkowitą cenę dla tablicy produktów:
ponieważ obecnie nie określamy żadnego jawnego poziomu dostępu, Nasza klasa PriceCalculator
(i jej metoda calculatePrice
) będzie dostępna z dowolnego miejsca w naszej aplikacji. Jeśli jednak chcemy udostępnić naszą nową klasę innym modułom (możemy, na przykład, zaimplementować ją w ramach, które dzielimy między naszą główną aplikacją a rozszerzeniem lub towarzyszącą aplikacją Apple Watch), będziemy musieli uczynić ją public
, aby była widoczna w tych zewnętrznych kontekstach:
Jednak powyższa zmiana nie wystarczy. Chociaż jesteśmy teraz w stanie znaleźć naszą klasę poza modułem, w którym jest zdefiniowana, nie możemy utworzyć jej instancji — ponieważ jej (niejawny) inicjalizator jest, jak każdy inny kod, domyślnie internal
. Aby to naprawić, zdefiniujmy inicjalizator public
, który zostawimy pusty, ponieważ nie ma w nim żadnej rzeczywistej pracy do zrobienia:
public class PriceCalculator { public init() {} ...}
jesteśmy teraz w stanie znaleźć, zainicjować i wywołać nasz PriceCalculator
zarówno wewnątrz, jak i na zewnątrz jego modułu — fantastycznie. Ale powiedzmy teraz, że chcemy go podklasować, aby go zmodyfikować lub dodać do niego nową funkcjonalność. Chociaż jest to obecnie możliwe w ramach własnego modułu, jest to ponownie coś, co jest zabronione poza nim.
aby to zmienić, będziemy musieli użyć obecnie najbardziej otwartego poziomu kontroli dostępu Swift, który jest odpowiednio nazwany open
:
open class PriceCalculator { ...}
Dzięki powyższej zmianie możemy teraz tworzyć niestandardowe podklasy PriceCalculator
w dowolnym miejscu – które mogą mieć nowe inicjalizatory, nowe właściwości i nowe metody. Oto jak możemy użyć tego do zaimplementowania DiscountedPriceCalculator
, co pozwala nam zastosować daną discount
do wszystkich obliczeń cenowych:
powyżej definiujemy zupełnie nową metodę obliczania ceny, ale prawdopodobnie bardziej odpowiednie byłoby nadpisanie i zmodyfikowanie istniejącej metody calculatePrice
, którą odziedziczyliśmy z naszej klasy bazowej. W ten sposób nie byłoby zamieszania wokół metody wywołania, i moglibyśmy utrzymać nasze dwie klasy spójne.
aby móc to zrobić, ponownie musimy oznaczyć pierwotną deklarację-tym razem naszą calculatePrice
deklarację metody-jako open
:
open class PriceCalculator { public init() {} open func calculatePrice(for products: ) -> Int { ... }}
mając powyższe na miejscu, możemy teraz dowolnie nadpisać calculatePrice
, zamiast tworzyć osobną metodę:
więc to jest internal
, public
i open
— które są używane do stopniowego otwierania deklaracji do użytku publicznego i modyfikacji. Ale możemy oczywiście pójść w drugą stronę i ukryć części naszego kodu przed odkryciem i użyciem. Na początku może wydawać się wątpliwe, jaka jest wartość w robieniu tego, ale może to naprawdę pomóc nam uczynić nasze API znacznie bardziej wąskim i skoncentrowanym — co z kolei może ułatwić zrozumienie, Testowanie i używanie.
przejdźmy teraz do drugiej strony spektrum poziomu dostępu i przyjrzyjmy się najbardziej restrykcyjnemu poziomowi — private
. Każdy typ, właściwość lub metoda oznaczona jako private
będzie widoczna tylko w obrębie własnego typu (który zawiera również rozszerzenia dla tego typu zdefiniowanego w tym samym pliku).
Wszystko, co powinno być uważane za prywatny szczegół implementacji danego typu, powinno być prawdopodobnie oznaczone jako private
. Na przykład, nasza nieruchomość discount
z naszego kalkulatora cenowego była tak naprawdę przeznaczona tylko do użytku w swojej klasie — więc zróbmy to, aby ta nieruchomość była prywatna:
class DiscountedPriceCalculator: PriceCalculator { private let discount: Int ...}
nasza poprzednia implementacja będzie nadal działać dokładnie tak samo jak poprzednio, ponieważ discount
pozostanie całkowicie widoczna w naszej klasie DiscountedPriceCalculator
. Jeśli jednak chcemy nieco rozszerzyć tę widoczność, aby uwzględnić również inne typy zdefiniowane w tym samym pliku, musielibyśmy użyć
class DiscountedPriceCalculator: PriceCalculator { fileprivate let discount: Int ...}
Dzięki powyższej zmianie możemy teraz uzyskać dostęp do naszej właściwości discount
z powiązanego kodu zdefiniowanego w tym samym pliku — takiego jak to rozszerzenie na UIAlertController
, które pozwala nam łatwo pokazać opis ceny dla tablicy produktów w alercie:
jeśli chodzi o wolne funkcje, typy i rozszerzenia, private
i fileprivate
działają dokładnie tak samo. Różnią się one tylko wtedy, gdy są stosowane do deklaracji zdefiniowanych w obrębie typu.
podsumowując, są to pięć poziomów Kontroli dostępu, które obecnie oferuje Swift:
-
private
zachowuje własność lub funkcję prywatną w obrębie typu zamykającego, w tym wszelkie rozszerzenia tego typu zdefiniowane w tym samym pliku. Po zastosowaniu do typu, funkcji lub rozszerzenia najwyższego poziomu, działa tak samo jakfileprivate
. -
fileprivate
sprawia, że deklaracja jest widoczna w całym pliku, w którym jest zdefiniowana, ukrywając ją przed wszystkimi innymi kodami. -
internal
jest domyślnym poziomem dostępu i sprawia, że deklaracja jest widoczna w całym module, w którym jest zdefiniowana. -
public
ujawnia funkcję, Typ, rozszerzenie lub właściwość poza modułem. -
open
umożliwia podklasę klasy i nadpisanie funkcji lub właściwości poza modułem.
ogólnie rzecz biorąc, często najlepiej zacząć od najbardziej restrykcyjnego poziomu dostępu, jaki dana deklaracja może mieć praktycznie, a następnie otworzyć rzeczy później, jeśli zajdzie taka potrzeba. W ten sposób ograniczamy możliwości interakcji między naszymi różnymi typami i funkcjami, co z początku może wydawać się złą rzeczą, ale często jest naprawdę niezbędne do budowy systemów, które można utrzymać i dobrze ustrukturyzować.
Dzięki za przeczytanie! 🚀
(zauważ, że ten artykuł nie wszedł w modyfikatory dostępu specyficzne dla mutacji, takie jak private(set)
. Te zostaną omówione w kolejnym artykule podstawy w przyszłości.
Wspieraj Swift by Sundell sprawdzając tego sponsora:
Instabug: Rozwiązuj błędy, awarie i inne problemy znacznie szybciej, korzystając ze szczegółowych śladów stosu, dzienników sieciowych i zdarzeń interfejsu użytkownika, które Instabug automatycznie dołącza do każdego zgłoszenia błędu. Używany zarówno przeze mnie, jak i tysiące zespołów programistycznych iOS na całym świecie. Wypróbuj go za darmo i Zintegruj z jednym wierszem kodu.