Mam problem ze zrozumieniem, dlaczego powinieneś tworzyć biblioteki wewnętrzne, które będą używane wyłącznie do tworzenia aplikacji wewnętrznych. Doceniam to, że jeśli chcę korzystać z oprogramowania napisanego przez osobę spoza organizacji, może on wysłać mi swoje pliki nagłówkowe oraz pliki .a lub .so i mogę po prostu połączyć je z moim projektem (zakładając, że są one skompilowane w tym samym środowisku) .
Ale dlaczego należy opracować bibliotekę wewnętrzną, aby była połączona z aplikacją wewnętrzną, gdy mam dostęp do plików nagłówka i plików implementacyjnych i mogę po prostu dołączyć je do mojego drzewa źródłowego i skompilować je wszystkie razem?
Innymi słowy: jeśli jakiś kod źródłowy zostanie napisany, jak zdecydować, czy należy go skompilować do biblioteki binarnej i połączyć z aplikacją, czy po prostu włączyć do plików źródłowych projektu i regularnie kompilować?
Kiedy mówię „dołącz” pliki do każdego projektu, nie mam na myśli kopiowania i wklejania każdego pliku do drzewa źródłowego aktualnie opracowywanego projektu. Mam na myśli opracowanie jakiegoś katalogu / biblioteki (oddzielnej dla każdego projektu) zawierającego wspólny kod źródłowy, który można włączyć do plików projektu w zwykły sposób, tj. #Include.
ps Mówię tutaj o rozwoju c / c ++ dla wielu aplikacji komputerowych.
Odpowiedzi:
Istnieje wiele powodów, aby tworzyć biblioteki i biblioteki współdzielone (w plikach .dll lub .so) nawet do użytku wewnętrznego:
Niektóre firmy mają nawet praktykę księgową, w ramach której projekty tworzące biblioteki otrzymują zwrot kosztów za każde ponowne użycie.
źródło
Niektóre inne możliwe przyczyny, które mogą mieć zastosowanie do większych, nietrywialnych projektów:
Czasy kompilacji: Ogromne monolityczne projekty C ++ z tysiącami plików, tysiącami klas, funkcji itp. Mogą zająć bardzo dużo czasu (co obniża wydajność, jeśli chcesz rekompilować za każdym razem, gdy zmieniasz kilka wierszy kodu). Biblioteki połączone statycznie i dynamicznie są kompilowane niezależnie i nie trzeba ich ponownie kompilować, jeśli ich źródło się nie zmieniło.
Logiczne rozdzielenie odrębnych modułów lub podsystemów : Dużymi systemami zazwyczaj łatwiej zarządzać, jeśli oddzielne obszary funkcjonalności są umieszczone w osobnych modułach, a programiści nie muszą przeszukiwać ogromnych folderów / projektów zawierających tysiące plików / klas.
Granice między programistami / zespołami : programiści budujący osobne nowe funkcje w tym samym czasie mogą zmniejszyć ryzyko konfliktów scalania, jeśli każdy programista może pracować w różnych modułach.
Kod, którego nie wolno wypuszczać do środowiska na żywo : Na przykład biblioteki testów jednostkowych lub biblioteki „pozorne”, które są używane do testowania programistów w celu zastąpienia składnika systemu na żywo (sprzęt, interfejsy API, systemy zdalne, bazy danych itp.)
Flagi kompilatora : Jeśli znajdziesz się w bardzo niefortunnej sytuacji integracji z interfejsem API innej firmy, który spodziewa się dziwnej flagi kompilatora, biblioteka może być „warstwą odkażającą” znajdującą się między interfejsem API innej firmy a resztą aplikacji.
Funkcje opcjonalne / Optymalizacja : W dużych systemach aplikacja może czekać przed załadowaniem niektórych dynamicznie połączonych modułów do pamięci w czasie wykonywania, jeśli nie mają one decydującego znaczenia dla podstawowej funkcjonalności aplikacji.
Zasadniczo wiele wewnętrznych projektów to często małe mikro-aplikacje, które nie korzystają z podziału na osobne biblioteki. Jeśli pracujesz nad małym projektem jako samotny programista, „nie musisz” martwić się o podział kodu na biblioteki (jeszcze ...). Nie zapomnij o zasadzie YAGNI .
źródło
Twoje pierwotne pytanie mogło spowodować nieporozumienie w przypadku większości innych odpowiedzi. Ponieważ nie zamierzasz kopiować istniejącego kodu między projektami, ale dołączyć te same pliki źródłowe z różnych projektów jako odniesienia , każdy argument „duplikatu kodu” staje się bezcelowy, podobnie jak wiele innych przedstawionych argumentów.
Zauważ, że jest to czasem (- nie zawsze -) rozsądna technika . W rzeczywistości, gdy umieścisz wszystkie pliki źródłowe, które chcesz ponownie wykorzystać w różnych projektach, w jednym osobnym folderze dołączania, już zbudowałeś bibliotekę - bibliotekę kodów źródłowych, a nie bibliotekę binarną. Zwłaszcza w C ++, podczas tworzenia ogólnych bibliotek z szablonami, nie jest niczym niezwykłym posiadanie bibliotek tylko z nagłówkami, które wymagają jedynie prostego dołączenia i żadnych oddzielnych przygotowań do łączenia.
Sądzę więc, że Twoim prawdziwym pytaniem jest - kiedy budować biblioteki kodu źródłowego, a kiedy preferować binarne biblioteki prekompilowane? W tej starszej odpowiedzi na tej stronie omówiłem zalety i wady bibliotek tylko nagłówkowych, być może to ci pomoże. Główną zaletą bibliotek kodu źródłowego jest to, że nie wymagają one kompilacji z tymi samymi flagami wykonawczymi i / lub kompatybilnymi kompilatorem / linkerem jak aplikacja, która ich używa. Wadami są dodatkowe czasy kompilacji i wymóg zapewnienia dostępu do kodu źródłowego (co oczywiście nie stanowi problemu dla tego rodzaju „wewnętrznych” projektów, o których myślisz).
źródło
Zgadzam się z innymi komentatorami, którzy piszą, że nie powinieneś powielać kodu. W twoim przypadku wydaje się jednak, że ty (lub osoby, z którymi pracujesz) tworzysz biblioteki dla kodu, który nie jest duplikowany w innym miejscu.
W tym przypadku przestrzegam przed przedwczesnym uogólnieniem . Często zdarza się, że wydaje się, że fragment kodu będzie można ponownie wykorzystać. Jednak bez znajomości szczegółowych informacji na temat tego, jak drugi przypadek użycia będzie wykorzystywał taki kod, bardzo łatwo jest poświęcić dodatkowy czas na funkcje „wielokrotnego użytku”, które w rzeczywistości nie będą przydatne w dodatkowych przypadkach, lub poczynić założenia, które okażą się błędne w druga sprawa.
Pisanie „biblioteki” dla jednego przypadku użycia może stać się bardzo kosztownym ćwiczeniem bez żadnej wypłaty - ugryzło mnie to kilka razy.
Przykładowe koszty:
Moja ogólna zasada brzmi: nie przekształcaj kodu w bibliotekę, chyba że mam co najmniej 2 oddzielne miejsca, w których kod jest potrzebny.
źródło
Ponieważ jeśli „po prostu umieścisz je w moim drzewie źródłowym”, skopiujesz kod .
Problem polega na tym, że nie skorzystasz z żadnych ulepszeń (w tym krytycznych poprawek) wprowadzonych przez projekt, z którego skopiowałeś kod, ani nie skorzystasz z żadnych ulepszeń, które wprowadzisz.
Możesz pomyśleć, że możesz rozwiązać ten problem, po prostu regularnie kopiując najnowszą wersję kodu do drzewa źródłowego, a może nawet zautomatyzowany za pomocą submodułu w git lub czegoś podobnego. Ale wtedy ciągle będziesz mieć przerwę kompilacji z powodu niezgodnych zmian API. Z drugiej strony biblioteka ma „oficjalny” publiczny interfejs API, o którym programiści wiedzą, że nie można go zmienić bez uzgadniania z klientami.
Wreszcie mogą istnieć powody techniczne - czy utrzymanie części kodu jako biblioteki może być konieczne, aby można go było opcjonalnie załadować, a nawet załadować i rozładować na żądanie, a tym samym zmniejszyć zużycie pamięci, gdy funkcjonalność nie jest potrzebna?
źródło
Chciałbym rozwinąć koszty, jakie Twoje rozwiązanie ma w dłuższej perspektywie.
Oczywiste jest, że dodanie biblioteki do projektu wiąże się z pewnym obciążeniem, a wszystko powyżej, jeśli jest to pierwsze: przepływy pracy muszą zostać zmienione, czasem nawet infrastruktura, a niektórym członkom zespołu może się to nie podobać (na początku). Więc zalety rozwiązania są oczywiste, jako że nakłada pomniejszonej o koszty teraz .
Jednak wraz ze wzrostem projektu wzrosną koszty „pseudo-biblioteki”. Załóżmy, że masz „pseudo-bibliotekę”, z
A
której korzysta aplikacja i tester jednostek. Za każdym razem, gdy dodajesz cppA
, musisz dodać go do obu projektów, w przeciwnym razie nie zostaną połączone.Co jeśli Twoja „pseudo-biblioteka” jest używana przez inną „pseudo-bibliotekę”
B
? Musisz dodać nowy procesor do wielu projektów. A jeśliB
przełączysz się na inną bibliotekę? Będziesz musiał usunąć cpps zeA
wszystkich projektów, w zależności tylko odB
.To wszystko byłoby za darmo, gdyby wykorzystano prawdziwą bibliotekę. Pytanie brzmi: ile procesorów jest potrzebnych do uzasadnienia przejścia do prawdziwej biblioteki?
Ale poczekaj, jest jeszcze więcej zabezpieczeń: Deweloperowi nie podoba się ta głupia praca polegająca na polowaniu na wszystkie projekty wymagające nowego CPP i doda gdzieś swój kod / klasy do już istniejących plików, co nie jest dobre w długi bieg.
Zatem użycie „pseudo-biblioteki” może być pierwszym krokiem do rozbicia monolitycznego projektu, ale nie powinieneś zbyt długo czekać, aby stała się prawdziwą biblioteką, aby móc korzystać z jej zalet.
źródło
Jeśli biblioteka jest używana tylko przez jedną aplikację, prawdopodobnie nie potrzebujesz jej jako oddzielnej biblioteki.
Jeśli biblioteka jest używana przez 3500 aplikacjach wtedy absolutnie zrobić trzeba go jako oddzielnej bibliotece.
Co jeśli w bibliotece jest błąd i trzeba go naprawić? Czy może nastąpią jakieś zmiany ustawowe lub wykonawcze, co oznacza, że musisz zmienić sposób działania biblioteki?
Jeśli jest w osobnej bibliotece, możesz (potencjalnie) naprawić bibliotekę, ponownie ją przetestować i wdrożyć, a każda aplikacja korzysta z tej poprawki.
Jeśli jest tylko w kodzie źródłowym, który jest „lokalny” dla każdej aplikacji, musisz zmienić, przebudować, ponownie przetestować i wdrożyć każdą aplikację osobno . To znacznie większe (tj. Droższe) ćwiczenie.
źródło