mamy wielu klientów o różnych potrzebach. Chociaż nasze oprogramowanie jest do pewnego stopnia zmodularyzowane, prawie pewne jest, że musimy dostosować logikę biznesową każdego modułu tu i tam trochę dla każdego klienta. Zmiany są prawdopodobnie zbyt małe, aby uzasadnić podział modułu na odrębny (fizyczny) moduł dla każdego klienta, obawiam się problemów z kompilacją, chaosu łączącego. Jednak zmiany te są zbyt duże i zbyt wiele, aby skonfigurować je za pomocą przełączników w pliku konfiguracyjnym, ponieważ prowadziłoby to do problemów podczas wdrażania i prawdopodobnie wielu problemów z obsługą, szczególnie w przypadku administratorów typu majsterkowiczów.
Chciałbym, aby system kompilacji tworzył wiele kompilacji, po jednej dla każdego klienta, gdzie zmiany są zawarte w specjalnej wersji pojedynczego modułu fizycznego, o którym mowa. Mam więc kilka pytań:
Czy doradziłbyś, aby system kompilacji tworzył wiele kompilacji? Jak powinienem przechowywać różne dostosowania w kontroli źródła, w szczególności svn?
#ifdef
działa dla ciebie?Odpowiedzi:
Potrzebna jest organizacja kodu przypominająca rozgałęzienie funkcji . W twoim konkretnym scenariuszu powinno to być nazywane rozgałęzieniem łącza specyficznym dla klienta, ponieważ prawdopodobnie będziesz używać rozgałęzienia funkcji również podczas opracowywania nowych rzeczy (lub rozwiązywania błędów).
http://svnbook.red-bean.com/en/1.5/svn.branchmerge.commonpatterns.html
Chodzi o połączenie pnia kodu z połączonymi gałęziami nowych funkcji. Mam na myśli wszystkie funkcje niespecyficzne dla klienta.
Następnie masz również oddziały specyficzne dla klienta, w których również scalasz oddziały tych samych funkcji, jeśli jest to wymagane (wybieranie wiśni, jeśli chcesz).
Te gałęzie klientów wyglądają podobnie do gałęzi obiektów, chociaż nie są tymczasowe ani krótkotrwałe. Są one utrzymywane przez dłuższy czas i są głównie połączone. Rozwój gałęzi funkcji specyficznych dla klienta powinien być jak najmniejszy.
Gałęzie specyficzne dla klienta są gałęziami równoległymi do pnia i są aktywne tak długo, jak sam pień, a nawet nie łączą się w całość.
źródło
Nie rób tego z oddziałami SCM. Uczyń wspólny kod osobnym projektem, który tworzy bibliotekę lub artefakt szkielet projektu. Każdy projekt klienta jest osobnym projektem, który następnie zależy od wspólnego jako zależności.
Jest to najłatwiejsze, jeśli twoja wspólna aplikacja bazowa używa struktury wstrzykiwania zależności, takiej jak Spring, dzięki czemu możesz łatwo wstrzykiwać różne warianty zastępowania obiektów w każdym projekcie klienta, ponieważ wymagane są niestandardowe funkcje. Nawet jeśli nie masz już środowiska DI, dodanie go i zrobienie tego w ten sposób może być najmniej bolesnym wyborem.
źródło
Przechowuj oprogramowanie dla wszystkich klientów w jednym oddziale. Nie ma potrzeby rozróżniania zmian wprowadzonych dla różnych klientów. Najprościej rzecz biorąc, będziesz chciał, aby twoje oprogramowanie było najlepszej jakości dla wszystkich klientów, a poprawka błędów w podstawowej infrastrukturze powinna wpływać na wszystkich bez niepotrzebnego nakładania się na siebie, co może również powodować więcej błędów.
Zmodularyzuj wspólny kod i zaimplementuj kod różniący się w różnych plikach lub strzeż go za pomocą różnych definicji. Spraw, aby system kompilacji miał określone cele dla każdego klienta, przy czym każdy cel kompiluje wersję zawierającą tylko kod związany z jednym klientem. Jeśli na przykład masz kod C, możesz chcieć zabezpieczyć funkcje dla różnych klientów za pomocą „
#ifdef
” lub innego mechanizmu, który ma Twój język do zarządzania konfiguracją kompilacji i przygotować zestaw definicji odpowiadających ilości funkcji, za które klient zapłacił.Jeśli nie lubisz
ifdef
s, użyj „interfejsów i implementacji”, „funktorów”, „plików obiektowych” lub innych narzędzi dostępnych w twoim języku do przechowywania różnych rzeczy w jednym miejscu.Jeśli dystrybuujesz źródła do swoich klientów, najlepiej jest, aby skrypty kompilacji miały specjalne „docelowe źródła dystrybucji”. Gdy wywołasz taki cel, tworzy on specjalną wersję źródeł twojego oprogramowania, kopiuje je do osobnego folderu, abyś mógł je wysłać, i nie kompiluje ich.
źródło
Jak wielu stwierdziło: poprawny kod i dostosuj go na podstawie wspólnego kodu - znacznie poprawi to łatwość konserwacji. Bez względu na to, czy używasz języka / systemu zorientowanego obiektowo, czy nie, jest to możliwe (chociaż w C jest to nieco trudniejsze niż w przypadku obiektów zorientowanych obiektowo). Jest to dokładnie ten rodzaj problemu, który pomaga rozwiązać dziedziczenie i hermetyzacja!
źródło
Bardzo ostrożnie
Rozgałęzianie funkcji jest opcją, ale uważam, że jest nieco ciężkie. Ułatwia także głębokie modyfikacje, które mogą prowadzić do rozwidlenia aplikacji, jeśli nie są pod kontrolą. Idealnie chcesz w jak największym stopniu ulepszyć dostosowania, starając się, aby Twój podstawowy kod był tak powszechny i ogólny, jak to możliwe.
Oto jak bym to zrobił, chociaż nie wiem, czy ma on zastosowanie do twojej bazy kodu bez ciężkich modyfikacji i ponownego faktoryzacji. Miałem podobny projekt, w którym podstawowa funkcjonalność była taka sama, ale każdy klient wymagał bardzo określonego zestawu funkcji. Stworzyłem zestaw modułów i kontenerów, które następnie zestawiam przez konfigurację (à la IoC).
następnie dla każdego klienta stworzyłem projekt, który zasadniczo zawiera konfiguracje i skrypt kompilacji, aby utworzyć w pełni skonfigurowaną instalację dla ich witryny. Czasami umieszczam tam również komponenty wykonane na zamówienie dla tego klienta. Jest to jednak rzadkie i ilekroć jest to możliwe, staram się zrobić to w bardziej ogólnej formie i spycham w dół, aby inne projekty mogły z nich korzystać.
W rezultacie otrzymałem poziom dostosowywania, którego potrzebowałem, otrzymałem spersonalizowane skrypty instalacyjne, aby po przejściu na stronę klienta nie wyglądało na to, że cały czas aktualizuję system, a jako bardzo znaczącą premię otrzymuję aby móc tworzyć testy regresji zaczepione bezpośrednio na kompilacji. W ten sposób za każdym razem, gdy dostaję błąd, który jest specyficzny dla klienta, mogę napisać test, który zapewni, że system jest wdrażany, a zatem może wykonać TDD nawet na tym poziomie.
w skrócie:
Jeśli zostanie to wykonane poprawnie, zestaw produktu powinien zawierać wszystkie pliki konfiguracyjne oprócz kilku.
Po pewnym czasie korzystania z tego skończyłem, tworząc meta-pakiety, które składają najczęściej używane lub niezbędne systemy jako jednostkę podstawową i używają tego meta-pakietu do zestawów klientów. Po kilku latach otrzymałem duży zestaw narzędzi, który mogłem bardzo szybko zmontować, aby stworzyć rozwiązania dla klientów. Obecnie patrzę na Spring Roo i sprawdzam, czy nie mogę rozwinąć tego pomysłu trochę dalej, mając nadzieję, że pewnego dnia uda mi się stworzyć pierwszy szkic systemu bezpośrednio z klientem w naszym pierwszym wywiadzie ... Wydaje mi się, że można to nazwać kierowanym przez użytkownika Rozwój ;-).
Mam nadzieję, że to pomogło
źródło
IMO, nie mogą być zbyt małe. Jeśli to możliwe, rozróżniam kod specyficzny dla klienta przy użyciu wzorca strategii praktycznie wszędzie. Zmniejszy to ilość kodu, który musi być rozgałęziony, i zmniejszy scalanie wymagane do synchronizacji ogólnego kodu dla wszystkich klientów. Uprości to również testowanie ... możesz przetestować ogólny kod przy użyciu domyślnych strategii i osobno przetestować klasy specyficzne dla klienta.
Jeśli Twoje moduły są zakodowane w taki sposób, że użycie zasady X1 w module A wymaga użycia zasady X2 w module B, pomyśl o refaktoryzacji, aby X1 i X2 można było połączyć w jedną klasę zasad.
źródło
Możesz użyć SCM do utrzymania oddziałów. Zachowaj nienaruszoną / czystą gałąź master od czystego kodu klienta. Dokonaj głównego rozwoju w tej branży. Dla każdej dostosowanej wersji aplikacji utrzymuj osobne gałęzie. Każde dobre narzędzie SCM poradzi sobie naprawdę dobrze z łączeniem gałęzi (przychodzi na myśl Git). Wszelkie aktualizacje w gałęzi głównej powinny zostać scalone z niestandardowymi gałęziami, ale kod specyficzny dla klienta może pozostać we własnym oddziale.
Chociaż, jeśli to w ogóle możliwe, spróbuj zaprojektować system w sposób modułowy i konfigurowalny. Minusem tych niestandardowych gałęzi może być to, że przesuwa się zbyt daleko od rdzenia / wzorca.
źródło
Jeśli piszesz zwykłym C, tutaj jest raczej brzydki sposób na zrobienie tego.
Wspólny kod (np. Jednostka „frangulator.c”)
kod specyficzny dla klienta, małe elementy, które są używane tylko dla każdego klienta.
w kodzie jednostki głównej użyj #ifdef i #include, aby zrobić coś takiego
Używaj tego jako wzorca w kółko we wszystkich jednostkach kodu, które wymagają dostosowania do konkretnego klienta.
Jest to BARDZO brzydkie i prowadzi do innych problemów, ale jest również proste i dość łatwo można porównywać pliki specyficzne dla klienta.
Oznacza to również, że wszystkie elementy specyficzne dla klienta są przez cały czas wyraźnie widoczne (każdy we własnym pliku) i istnieje wyraźny związek między głównym plikiem kodu a częścią pliku specyficzną dla klienta.
Jeśli staniesz się naprawdę sprytny, możesz skonfigurować pliki makefile, aby utworzyć poprawną definicję klienta, a więc coś takiego:
zrobić klientę
zbuduje dla client_a, a „make clientb” zrobi dla client_b i tak dalej.
(i „make” bez podanego celu może wydać ostrzeżenie lub opis użytkowania).
Użyłem już podobnego pomysłu, konfiguracja zajmuje trochę czasu, ale może być bardzo skuteczna. W moim przypadku jedno drzewo źródłowe zbudowało około 120 różnych produktów.
źródło
W git chciałbym to zrobić, aby mieć gałąź master z całym wspólnym kodem i gałęzie dla każdego klienta. Za każdym razem, gdy wprowadzana jest zmiana w kodzie podstawowym, po prostu rozłóż wszystkie gałęzie specyficzne dla klienta na master, tak abyś miał zestaw ruchomych łat dla klientów, które są nakładane na bieżącą linię bazową.
Za każdym razem, gdy wprowadzasz zmiany dla klienta i zauważysz błąd, który powinien być zawarty w innych gałęziach, możesz wybrać go w jednym z nich lub w innych gałęziach, które wymagają poprawki (chociaż różne gałęzie klienta współużytkują kod , prawdopodobnie powinieneś mieć je obie rozgałęzione ze wspólnej gałęzi od master).
źródło