We wczorajszym artykule przedstawiłem koncepcję „pociągu zależności”. Tak się dzieje, gdy importujesz funkcję z biblioteki kodu, ale ostatecznie musisz zaimportować kilka dodatkowych modułów, aby spełnić wszystkie zależności. Pozostaje Ci cały „pociąg” modułów kodu, gdy wszystko, czego potrzebujesz, to jedno „miejsce” (funkcja).
Znajdziesz się w takiej sytuacji, gdy Twoje moduły są ściśle powiązane. Więc co możesz z tym zrobić? Istnieje kilka sposobów radzenia sobie z tą sytuacją.
Naruszenie „nie powtarzaj się”
Jednym ze sposobów zachowania luźnego sprzężenia — co skutkuje większą liczbą „samodzielnych” modułów — jest utworzenie prywatnych kopii funkcji z modułu źródłowego w module wywołującym. Eliminuje to zależność między dwoma modułami, ale skutkuje powtarzalnym kodem.
Koncepcja jest prosta. Kopiujesz funkcję z jej podstawowego modułu kodu. Następnie wklejasz go do modułu wywołującego, ale oznaczasz go jako prywatny, aby uniknąć niejasności.
Ma to sens w następujących sytuacjach:
- Proste metody bez skomplikowanej logiki.
- Procedury, które raczej się nie zmienią.
- Gdy procedura jest częścią znacznie większego modułu i nie potrzebujesz żadnych innych funkcji w tym module. Kopiowanie jednej funkcji pozwala uniknąć wzdęcia.
Takie podejście ma następujące wady:
- Jeśli jest błąd w skopiowanej funkcji, będziesz musiał go naprawić w wielu miejscach.
- Masz duplikację kodu, jeśli mimo wszystko zaimportujesz moduł źródłowy.
Wybierz swoje bitwy
Kiedyś bardzo się starałem, aby wszystkie moje standardowe moduły biblioteki kodu były całkowicie niezależne. Problem polegał na tym, że spowodowało to wiele powielania kodu. Powodem jest to, że większość funkcji, które kopiowałem do innych modułów do użytku prywatnego, pochodziła z modułów, które i tak importowałem do mojej aplikacji.
Doskonałym tego przykładem były moje StringFunctions moduł. Ten moduł ma kilka prostych metod, które istnieją głównie po to, aby mój kod był bardziej czytelny. Na przykład mam Conc()
funkcję, którą dołączyłem jako funkcję prywatną w ponad połowie moich modułów biblioteki kodu.
Z biegiem czasu zdałem sobie sprawę, że uwzględniłem te StringFunctions we wszystkich moich projektach. Nigdy nie wprowadzałem nowej zależności, kiedy wywoływałem funkcję z tego modułu. Marnowałem czas i wprowadzałem zduplikowany kod z niewielką lub żadną korzyścią.
Było kilka modułów kodu, które mogłem bezpiecznie założyć, że będą w każdej aplikacji. Były to moduły z funkcjami, z których korzystałem najczęściej. Co oznaczało, że wiele z tych zależności można było zasadniczo zignorować.
Obecnie prowadzę "Standardową Bibliotekę" modułów kodu, które na samym początku importuję do każdego nowego projektu. Swobodnie wywołuję funkcje z tych modułów, teraz bezpiecznie wiedząc, że nie będę wprowadzał nowych zależności.
Użyj unikalnego tokena komentarza
Jednym z modułów w mojej „Bibliotece standardowej” jest moduł klasy (clsApp ), która zawiera właściwości i metody na poziomie aplikacji, takie jak nazwa bieżącego użytkownika i tekst paska tytułu. Udostępniam również inne moduły klas z poziomu clsApp , na przykład clsStatus i clsRejestr , które zapewniają bardziej czytelny dostęp odpowiednio do paska stanu dostępu i rejestru systemu Windows.
Jednak nie potrzebuję dostępu do paska stanu lub rejestru Windows w każdym projekcie. Tak więc, aby uniknąć tworzenia zależności od clsStatus lub clsRejestr klas, skomentowałbym kod odwołujący się do tych klas za pomocą unikalnego „tokenu komentarza”.
Najłatwiej to zademonstrować na przykładzie:
' Notes
' - Find and replace '$$ with blank to enable Status property (requires clsStatus)
' - Find and replace '&& with blank to enable Reg property (requires clsRegistry)
'$$Private m_objStatus As clsStatus
'&&Private m_objReg As clsRegistry
'$$Public Property Get Status() As clsStatus
'$$ Set Status = m_objStatus
'$$End Property
'&&Public Property Get Reg() As clsRegistry
'&& Set Reg = m_objReg
'&&End Property
Private Sub Class_Initialize()
'$$ Set m_objStatus = New clsStatus
'&& Set m_objReg = New clsRegistry
End Sub
Jeśli chcę włączyć Status
właściwość powyższej klasy, mógłbym przeprowadzić globalne wyszukiwanie i zamianę na '$$
.
Działało to przez jakiś czas dobrze, ale zawsze było to dla mnie niezręczne. Pewnie dlatego, że tak było. Inną rzeczą, na którą należy zwrócić uwagę, jest to, że tokeny komentarzy musiałyby być globalnie unikalne w całej mojej bibliotece kodu. Byłby to koszmar związany z konserwacją, gdybym trzymał się tego podejścia przez długi czas.
Użyj kompilacji warunkowej
O wiele czystszym podejściem jest skorzystanie z kompilacji warunkowej. Są to wiersze w VBA, które zaczynają się od znaku funta/hashtagu („#”). Linie zaczynające się tym znakiem podlegają „przetwarzaniu wstępnemu”.
Co to jest wstępne przetwarzanie? Jest to krok, który języki programowania podejmują przed kompilacją. Tak więc, zanim nastąpi jakiekolwiek sprawdzenie czasu kompilacji, wiersze przetwarzania wstępnego są oceniane. To pozwala nam umieścić kod, który w przeciwnym razie nie skompilowałby się do naszych projektów.
Jak możemy to wykorzystać dzięki naszym bibliotekom kodu? Ponownie, najprościej to zademonstrować na przykładzie:
' Notes
' - Replace the '$$ and '&& kludges with conditional compilation
#Const EnableStatusProperty = True 'If True, requires import of clsStatus class
#Const EnableRegProperty = False 'If True, requires import of clsRegistry class
#If EnableStatusProperty Then
Private m_objStatus As clsStatus
#End If
#If EnableRegProperty Then
Private m_objReg As clsRegistry
#End If
#If EnableStatusProperty Then
Public Property Get Status() As clsStatus
Set Status = m_objStatus
End Property
#End If
#If EnableRegProperty Then
Public Property Get Reg() As clsRegistry
Set Reg = m_objReg
End Property
#End If
Private Sub Class_Initialize()
#If EnableStatusProperty Then
Set m_objStatus = New clsStatus
#End If
#If EnableRegProperty Then
Set m_objReg = New clsRegistry
#End If
End Sub
Najlepsze z obu światów
Jak widać, jest to bardzo czysty sposób na uniknięcie problemu „pociągu zależności”.
Pozwala nam tworzyć opcjonalne zależności . Każdy fragment kodu, który opiera się na innym module biblioteki kodu, jest opakowany w kompilację warunkową #If ... Then . Wszystkie stałe warunkowe kompilacji są wymienione w górnej części modułu kodu.
Teraz, gdy ponownie zaimportujemy zaktualizowaną wersję naszego modułu biblioteki kodu, po prostu musimy przejść przez i ustawić flagi kompilacji warunkowej na takie same, jakie były wcześniej. Jeśli nie pamiętamy, w jaki sposób flagi zostały ustawione, powinniśmy być w stanie kontynuować kompilację i dostosowywanie flag do czasu pełnej kompilacji projektu.
A jeśli używamy kontroli wersji, nie musimy się martwić, że zapomnimy o tym, co było wcześniej.