Funktionen für einige Benutzer ausblenden / deaktivieren

10

Nehmen wir an, ich habe eine kostenlose und kostenpflichtige Version der App. Die kostenpflichtige Version ist eine Obermenge der kostenlosen Version in Bezug auf die Funktionen, die den Benutzern zur Verfügung stehen. Die kostenpflichtige Version bietet also alle Funktionen der kostenlosen App sowie zusätzliche Funktionen.

Gibt es ein Muster zum Umschalten der Funktionsverfügbarkeit basierend auf einem Flag, das beim Start geladen wird (z. B. kostenlos / kostenpflichtig)?

Ich mag die Idee nicht, überall folgende Codeblöcke zu haben:

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

Es ist keine Option, zwei separate Git-Zweige für jede Version zu haben, da dies bedeuten würde, zwei (oder mehr) Codequellen zu verwalten . Dies erscheint im Allgemeinen unpraktisch und wird hier ausführlicher erläutert: Verwalten von zwei separaten Softwareversionen aus derselben Codebasis in der Versionskontrolle .

Gibt es eine Möglichkeit, dies zu tun, während Sie immer noch eine einzige Codebasis haben und den Code nicht mit bedingten Anweisungen verunreinigen, die das Flag für frei / bezahlt prüfen?

Ich bin mir sicher, dass dies schon oft diskutiert wurde und ich bin mir sicher, dass es einige Muster gibt, um dieses Problem anzugehen, aber ich kann es einfach nicht finden.

Wir verwenden Android / Java.

Tadija Bagarić
quelle
@gnat tnx, schöner Fund. Aber ich möchte Optionen diskutieren, die keine separaten Zweige erfordern und mehrere Codebasen verwalten
Tadija Bagarić
2
Dies ähnelt einer unterschiedlichen Berechtigungsstufe. Möglicherweise untersuchen Sie, wie dieses Problem normalerweise behoben wird, wenn eine Funktion nur für bestimmte Benutzer / Rollen verfügbar ist.
Bart van Ingen Schenau
@BartvanIngenSchenau Ich finde, es handelt sich meistens um ifÜberprüfungen, um Steuerelemente für verbotene Funktionen auszublenden, oder um Popup-Dialoge, wenn der Benutzer versucht, das zu tun, was er nicht darf. Ich hoffe, einen Weg zu finden, um viele Bedingungen im Code zu vermeiden
Tadija Bagarić
2
Verwenden Sie Polyphormismus. Sie müssen sich nie wieder nach dieser if-Anweisung fragen, und die Wartung ist VIEL einfacher!
Steve Chamaillard

Antworten:

14

Ein bedingtes Like if(isFreeVersion)sollte nur einmal im Code vorkommen. Dies ist kein Muster, aber ich bin sicher, dass Sie den Namen dafür bereits kennen: Es wird das DRY-Prinzip genannt . Wenn Sie Code wie " if(isFreeVersion)" an mehr als einer Stelle in Ihrem Code haben, haben Sie diese Zeile / die darin enthaltene Logik wiederholt. Dies bedeutet, dass sie überarbeitet werden sollte, um die Wiederholung zu vermeiden.

" if(isFreeVersion)" sollte verwendet werden, um eine Liste der internen Konfigurationsoptionen für verschiedene Funktionen einzurichten. Der resultierende Code könnte dann folgendermaßen aussehen:

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

Dadurch wird Ihr einzelnes "isFreeVersion" -Flag verschiedenen Funktionen zugeordnet . Beachten Sie, dass Sie hier entscheiden können, ob Sie einzelne Boolesche Flags für einzelne Features oder andere Parameter verwenden möchten, z. B. verschiedene Strategieobjekte mit einer gemeinsamen Schnittstelle, wenn die Feature-Steuerung eine komplexere Parametrisierung erfordert.

Jetzt haben Sie die Kontrolle darüber, was in der kostenlosen Version und was in der kostenpflichtigen Version an einem Ort enthalten ist, was die Wartung dieser Logik recht einfach macht. Sie müssen immer noch darauf achten, dass Ihr Code nicht mit vielen if(feature1Enabled)Anweisungen überfüllt ist (indem Sie dem DRY-Prinzip folgen), aber jetzt ist die Aufrechterhaltung dieser Überprüfungen nicht mehr so ​​schmerzhaft. Sie haben beispielsweise eine viel bessere Kontrolle darüber, was Sie ändern müssen, wenn Sie eine vorhandene kostenpflichtige Funktion kostenlos machen möchten (oder umgekehrt).

Lassen Sie uns zum Schluss einen Blick in Fowlers Blog-Artikel über Feature-Toggles werfen, in dem er über Feature-Einstiegspunkte / Toggle-Punkte spricht. Lassen Sie mich einen zentralen Punkt anführen:

Versuchen Sie nicht, jeden Codepfad im neuen Feature-Code mit einem Umschalter zu schützen, sondern konzentrieren Sie sich nur auf die Einstiegspunkte, die Benutzer dorthin führen würden, und schalten Sie diese Einstiegspunkte um.

Konzentrieren Sie sich als Gesamtstrategie auf die Benutzeroberfläche und beschränken Sie Ihre Überprüfungen auf die minimale Anzahl von Punkten, die erforderlich sind, damit eine bestimmte Funktion angezeigt oder ausgeblendet wird. Das sollte Ihre Codebasis sauber halten, ohne unnötige Unordnung.

Doc Brown
quelle
5
Sie haben IsFreeVersion grundsätzlich durch FeaturexEnabled ersetzt und die Anzahl der Anrufe nicht reduziert. Obwohl ich nicht genau diesen Fall hatte, habe ich bei der Menüerstellung immer ähnliche Dinge behandelt, indem ich die Optionen deaktiviert habe, die der Benutzer nicht sehen sollte. Meistens ist dies bei der Formularerstellung, aber ich musste es manchmal tun, wenn ich ein Popup-Menü vorbereitete.
Loren Pechtel
1
@ LorenPechtel: Du solltest meine Antwort noch einmal genauer lesen. Ich habe tatsächlich zwei Dinge erwähnt, um die Anzahl der bedingten Tests zu reduzieren, eines davon das DRY-Prinzip, eines davon konzentriert sich auf Tests in der Benutzeroberfläche. Noch wichtiger ist, dass die Zuordnung eines unspezifischen Flags isFreeVersionzu bestimmten Funktionsparametern den größten Teil der Schmerzen dieser Tests beseitigt - sie werden tatsächlich sinnvoll und verursachen kein Wartungsproblem mehr.
Doc Brown
9

Wenn Sie if/elseBlöcke nicht mögen , können Sie sie umgestalten, um die Vererbung zu verwenden (siehe Bedingtes Ersetzen durch Polymorphismus aus Marin Fowlers Refactoring- Buch). Das würde:

  • Machen Sie es sich etwas einfacher, über Ihren Code nachzudenken.

  • Ermöglichen Sie zwei Klassen, eine für die kostenlose Version und eine für die kostenpflichtige Version, die wiederum die Anrufe an andere Klassen weiterleiten und sicherstellen, dass die Unterscheidung zwischen kostenlosen und kostenpflichtigen Versionen auf zwei Klassen beschränkt ist (drei zählen die Basisklasse).

  • Machen Sie es später einfach, andere Formen Ihrer Software hinzuzufügen, z. B. eine billige Variante oder eine Premium-Version. Sie fügen einfach eine weitere Klasse hinzu und deklarieren sie einmal in Ihrem Code. Sie wissen dann, dass die gesamte Codebasis weiterhin wie erwartet funktioniert.

Arseni Mourzenko
quelle
3
Ich denke, Sie möchten vielleicht klarer machen, dass hierfür keine Vererbung der Implementierung erforderlich ist. Ein weiterer Vorteil ist, dass die kostenlose App ohne die Premium-Funktionen geliefert werden kann. Das Ändern des Java-Bytecodes, um eine if-Bedingung immer wahr zu machen, ist nicht besonders schwierig.
JimmyJames
6

Es scheint mir, dass Ihre Frage mit dem Feature Toggle Pattern ziemlich gut gelöst werden könnte .

Wie so oft erklärte Pete Hodgson in einem Artikel alle Szenarien, in denen Sie dieses Muster anwenden könnten, viel besser als ich.

Es gibt auch einige Bibliotheken, die dieses Muster unterstützen. Ich hatte Erfahrung mit FF4J in Java, aber ich würde raten, wenn Sie Folgendes eingeben :

feature toggle <whatever language you prefer>

... in jeder Suchmaschine erhalten Sie mehrere Lösungen.

danidemi
quelle
1

Es gibt mehr als einen Weg, dies zu erreichen. Der einfache und unkomplizierte Weg besteht darin, das Feature-Toggle-Muster zu verwenden , das in so vielen Artikeln bereitgestellt wurde. Der nächste Ansatz hat mit dem Entwerfen für steckbare Funktionen zu tun. Sowohl Android als auch IOS haben In-App-Zahlungen. Zusammen mit dieser Zahlung besteht das Potenzial für einen Download.

Wenn Sie sich Servlets, JAMES Mailets und sogar IDE-Plugins ansehen, verwenden alle das Konzept einer Plug-In-Architektur:

  • Definieren Sie eine Schnittstelle, die Ihre App verwenden kann. Diese Schnittstelle muss eine Möglichkeit bieten, sich in die Navigation Ihrer Anwendung und in jede andere App einzufügen, um Touchpoints zu integrieren.
  • Richten Sie einen Pfad ein, den Ihre App beim Start liest (die Verwaltung von Laufzeit-Plugins ist viel schwieriger).
  • Wenn ein Plugin vorhanden ist (wie eine Java Jar-Datei), liest die App entweder das Manifest, um die Implementierung der Plugin-Schnittstelle zu finden, oder sucht nach einer Klasse, die die Schnittstelle implementiert.
  • Sobald diese Klasse gefunden wurde, wird sie instanziiert und die entsprechenden Methoden aufgerufen, um die neuen Funktionen zu integrieren.

Dies ermöglicht Ihnen auch die Möglichkeit, verschiedene Funktionsklassen für verschiedene Zielgruppen verfügbar zu machen. Die Benutzer haben nur die Funktionen, für die sie bezahlt haben.

Ihr Anwendungscode wird als eine Codebasis verwaltet, und Ihr Plug-In ist eine separate Codebasis - enthält jedoch nur die Teile, die für das Plugin relevant sind. Die App weiß, wie man mit Plugins umgeht, wenn sie vorhanden sind, und das Plugin weiß nur, wie man mit der Schnittstelle interagiert.

Berin Loritsch
quelle