Ukrywanie / wyłączanie funkcji dla niektórych użytkowników

11

Powiedzmy, że mam darmową i płatną wersję aplikacji. Wersja płatna jest nadzbiorem bezpłatnej wersji dotyczącej funkcji dostępnych dla użytkowników, co oznacza, że ​​wersja płatna będzie miała wszystkie funkcje darmowej aplikacji plus dodatkowe.

Czy istnieje wzorzec przełączania dostępności funkcji na podstawie flagi, która ładuje się przy starcie (np. Bezpłatny / płatny)?

Nie podoba mi się pomysł posiadania następujących bloków kodu wszędzie:

if(isFreeVersion){
    // ...
} else {
    // ...
}

Posiadanie 2 oddzielnych gałęzi git dla każdej wersji nie jest opcją, ponieważ oznaczałoby to utrzymanie 2 (lub więcej) źródeł kodu, wydaje się ogólnie niepraktyczne i jest omówione bardziej tutaj: Utrzymanie dwóch oddzielnych wersji oprogramowania z tej samej bazy kodów w Kontroli wersji .

Czy istnieje sposób, aby to zrobić, wciąż mając jedną bazę kodu i nie zaśmiecając kodu instrukcjami warunkowymi, które sprawdzają flagę darmową / płatną?

Jestem pewien, że było to omawiane wiele razy wcześniej i jestem pewien, że istnieją pewne wzorce podejścia do tego problemu, ale po prostu nie mogę go znaleźć.

Używamy Androida / Java.

Tadija Bagarić
źródło
@gnat tnx, ładne znalezisko. Chciałbym jednak omówić opcje, które nie wymagają oddzielnych gałęzi i utrzymywania wielu baz kodów
Tadija Bagarić
2
Wygląda to podobnie do różnych poziomów autoryzacji. Możesz spojrzeć na to, jak zazwyczaj rozwiązuje się ten problem, gdy funkcja jest dostępna tylko dla niektórych użytkowników / ról.
Bart van Ingen Schenau
@BartvanIngenSchenau Uważam, że jest to głównie ifsprawdzanie ukrywania elementów sterujących zabronionych funkcji lub wyświetlanie wyskakujących okien dialogowych, gdy użytkownik próbuje zrobić to, czego nie wolno mu robić. Mam nadzieję znaleźć sposób na uniknięcie wielu warunków warunkowych w kodzie
Tadija Bagarić
2
Użyj poliformizmu. Już nigdy nie będziesz musiał zadawać sobie pytania o to, jeśli oświadczenie, i będzie o wiele łatwiejsze do utrzymania!
Steve Chamaillard

Odpowiedzi:

9

Jeśli nie lubisz if/elsebloków, możesz je refaktoryzować, aby użyć dziedziczenia (zobacz Zastąp warunkowe polimorfizmem z książki Refaktoryzacja Marin Fowler ). To by:

  • Uczyń swój kod nieco prostszym.

  • Umożliwiają posiadanie dwóch klas, jednej dla wersji darmowej i drugiej dla wersji płatnej, co z kolei wywoływałoby wywołania do innych klas, zapewniając, że rozróżnienie między wersją bezpłatną i płatną jest ograniczone do dwóch klas (trzy liczą klasa podstawowa).

  • Ułatw później dodanie innych form oprogramowania, takich jak tani wariant lub wersja premium. Po prostu dodasz inną klasę i zadeklarujesz ją raz w kodzie, a będziesz wiedział, że cała baza kodu nadal będzie działać zgodnie z oczekiwaniami.

Arseni Mourzenko
źródło
3
Myślę, że możesz chcieć wyjaśnić, że dziedziczenie implementacji nie jest w tym celu wymagane. Kolejną zaletą jest to, że bezpłatną aplikację można dostarczyć bez funkcji premium. Modyfikacja kodu bajtu Java, aby warunek if zawsze był prawdziwy, nie jest strasznie trudny.
JimmyJames
15

Warunki warunkowe if(isFreeVersion)powinny wystąpić tylko raz w kodzie. To nie jest wzorzec, ale jestem pewien, że już znasz jego nazwę: nazywa się to zasadą OSUSZANIA . Posiadanie kodu typu „ if(isFreeVersion)” w więcej niż jednym miejscu w kodzie oznacza, że ​​powtórzyłeś ten wiersz / logikę w nim, co oznacza, że ​​należy go zrefaktoryzować, aby uniknąć powtórzenia.

if(isFreeVersion)Do ustawienia listy opcji konfiguracji wewnętrznej dla różnych funkcji należy użyć „ ”. Wynikowy kod może wyglądać następująco:

 if(isFreeVersion)
 {
      feature1Enabled=false;
      feature2Enabled=false;
      maxNoOfItems=5;
      advertisingStrategy=new ShowLotsOfAdvertisementsStrategy();
      // ...
 } 
 else
 {
      feature1Enabled=true;
      feature2Enabled=true;
      maxNoOfItems=int.MaxValue; // virtually unlimited
      advertisingStrategy=new ShowMinimalAdvertisementsStrategy();
 }

To odwzorowuje twoją pojedynczą flagę „isFreeVersion” na różne funkcje . Pamiętaj, że możesz zdecydować, czy wolisz używać pojedynczych flag boolowskich dla poszczególnych funkcji, czy też użyć innych parametrów, na przykład różnych obiektów strategii ze wspólnym interfejsem, jeśli kontrola funkcji wymaga bardziej złożonej parametryzacji.

Teraz masz kontrolę nad tym, co jest w wersji darmowej, a co w wersji płatnej w jednym miejscu, co sprawia, że ​​utrzymanie tej logiki jest bardzo proste. Nadal będziesz musiał uważać, aby nie zaśmiecać kodu dużą ilością if(feature1Enabled)instrukcji (zgodnie z zasadą DRY), ale teraz utrzymanie tych kontroli nie jest już tak bolesne. Na przykład masz znacznie lepszą kontrolę nad tym, co musisz zmienić, aby uwolnić istniejącą płatną funkcję (lub odwrotnie).

Na koniec rzućmy okiem na artykuł Fowlera na temat przełączania funkcji , w którym mówi on o punktach wejścia / przełączania funkcji. Przytoczę jeden centralny punkt:

Nie próbuj chronić każdej ścieżki kodu w nowym kodzie funkcji za pomocą przełącznika, skup się tylko na punktach wejścia, które doprowadziłyby użytkowników tam i przełączaj te punkty wejścia.

Tak więc, jako ogólna strategia, skup się na interfejsie użytkownika i ogranicz swoje kontrole do minimalnej liczby punktów wymaganych do pojawienia się lub zniknięcia określonej funkcji. To powinno utrzymywać bazę kodu w czystości, bez zbędnego bałaganu.

Doktor Brown
źródło
5
Zasadniczo zastąpiłeś IsFreeVersion FeaturexEnabled, nie zmniejszyłeś liczby połączeń. Chociaż nie miałem dokładnie takiej sprawy, zawsze zajmowałem się podobnymi rzeczami przy tworzeniu menu wyłączając te opcje, których użytkownik nie powinien widzieć. Najczęściej dotyczy to tworzenia formularzy, ale czasami musiałem to zrobić, przygotowując wyskakujące menu.
Loren Pechtel
1
@LorenPechtel: powinieneś przeczytać uważniej moją odpowiedź. Właściwie wspomniałem o dwóch rzeczach, aby zmniejszyć liczbę testów warunkowych, jedną z nich była zasada DRY, a jedna koncentrowała się na testach w interfejsie użytkownika. Co ważniejsze, mapowanie niespecyficzne flagę jak isFreeVersiondo konkretnych parametrów funkcja usuwa większość bólu tych testów - będą rzeczywiście zaczynają sensu i nie powodują bałagan konserwacyjnych więcej.
Doc Brown
6

Wydaje mi się, że twoje pytanie można rozwiązać całkiem dobrze, stosując wzorzec przełączania cech .

Jak to często bywa, Pete Hodgson wyjaśnił w jednym artykule wszystkie scenariusze, z którymi możesz się spotkać stosując ten wzór, znacznie lepiej niż ja.

Istnieją również biblioteki obsługujące ten wzorzec. Miałem doświadczenie w pracy z FF4J w Javie, ale zgaduję, jeśli wpiszesz:

feature toggle <whatever language you prefer>

... w dowolnej wyszukiwarce otrzymasz kilka rozwiązań.

danidemi
źródło
2

Jest na to więcej niż jeden sposób. Prostym i bezpośrednim sposobem jest użycie Wzorca Przełączania Funkcji, który był dostępny w tak wielu artykułach. Kolejne podejście dotyczy projektowania funkcji, które można podłączyć. Zarówno Android, jak i IOS mają płatności w aplikacji. Wraz z tą płatnością istnieje możliwość pobrania.

Kiedy patrzysz na Servlety, JAMES Mailets, a nawet wtyczki IDE, wszystkie one wykorzystują koncepcję architektury wtyczek:

  • Zdefiniuj interfejs, z którego będzie korzystać Twoja aplikacja. Ten interfejs musi umożliwiać samodzielne wprowadzenie się w nawigację aplikacji i dowolną inną aplikację do podłączania punktów kontaktowych.
  • Ustaw ścieżkę, którą aplikacja będzie czytać podczas uruchamiania (zarządzanie wtyczkami w czasie wykonywania jest znacznie trudniejsze)
  • Jeśli istnieje wtyczka (np. Plik Java Jar), ​​aplikacja albo czyta manifest, aby znaleźć implementację interfejsu wtyczki, lub szuka klasy, która implementuje interfejs.
  • Po znalezieniu tej klasy tworzona jest instancja i wywoływane są odpowiednie metody w celu zintegrowania nowych funkcji.

Pozwala to również na posiadanie różnych klas funkcji dostępnych dla różnych odbiorców. Użytkownicy mają tylko funkcje, za które zapłacili.

Kod aplikacji jest utrzymywany jako jedna podstawa kodu, a wtyczka stanowi oddzielną podstawę kodu - ale zawiera tylko części istotne dla wtyczki. Aplikacja wie, jak radzić sobie z wtyczkami, gdy są one obecne, a wtyczka wie tylko, jak współpracować z interfejsem.

Berin Loritsch
źródło