Nascondere / disabilitare le funzionalità per alcuni utenti


10

Diciamo che ho una versione gratuita ea pagamento dell'app. La versione a pagamento è un superset della versione gratuita relativa alle funzionalità disponibili per gli utenti, il che significa che la versione a pagamento avrà tutte le funzionalità dell'app gratuita più extra.

Esiste un modello per attivare o disattivare la disponibilità delle funzionalità in base a un flag che viene caricato all'avvio (ad esempio, gratuito / a pagamento)?

Non mi piace l'idea di avere i seguenti blocchi di codice ovunque:

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

Avere 2 rami git separati per ogni versione non è un'opzione perché significherebbe mantenere 2 (o più) fonti di codice, sembra poco pratico in generale ed è discusso di più qui: Mantenere due versioni separate del software dalla stessa base di codice in Controllo versione .

C'è un modo per farlo, pur avendo una singola base di codice e non sporcare il codice con istruzioni condizionali che controllano il flag gratuito / pagato?

Sono sicuro che questo è stato discusso molte volte prima e sono sicuro che ci sono alcuni modelli per affrontare questo problema, ma non riesco proprio a trovarlo.

Usiamo Android / Java.



@gnat tnx, bella scoperta. Ma vorrei discutere le opzioni che non richiedono rami separati e mantenere più codebase
Tadija Bagarić

2
Sembra simile a livelli di autorizzazione diversi. Potresti esaminare in che modo viene affrontato il problema, in cui una funzione è disponibile solo per determinati utenti / ruoli.
Bart van Ingen Schenau,

@BartvanIngenSchenau Trovo che si tratti principalmente di ifcontrolli per nascondere i controlli per le funzioni vietate o avere una finestra di dialogo popup per quando l'utente tenta di fare ciò che non gli è permesso. Spero di trovare un modo per evitare molti condizionali nel codice
Tadija Bagarić

2
Usa il poliformismo. Non dovrai mai più chiederti questa affermazione se sarà molto più facile da mantenere!
Steve Chamaillard,

Risposte:


14

Un simile condizionale if(isFreeVersion)dovrebbe verificarsi una sola volta nel codice. Questo non è uno schema, ma sono sicuro che già conosci il nome per questo: si chiama principio SECCO . Avere codice come " if(isFreeVersion)" in più di un posto nel tuo codice significa che hai ripetuto questa riga / la logica in essa, il che significa che dovrebbe essere riformulato per evitare la ripetizione.

" if(isFreeVersion)" dovrebbe essere usato per impostare un elenco di opzioni di configurazione interne per diverse funzionalità. Il codice risultante potrebbe quindi apparire così:

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

In questo modo il singolo flag "isFreeVersion" viene mappato su diverse funzionalità . Nota che puoi decidere qui se preferisci utilizzare singoli flag booleani per singole funzionalità o utilizzare qualche tipo di altri parametri, ad esempio diversi oggetti di strategia con un'interfaccia comune, se il controllo delle funzionalità richiede una parametrizzazione più complessa.

Ora hai il controllo di ciò che è nella versione gratuita e di ciò che è nella versione a pagamento in un unico posto, il che rende piuttosto semplice la manutenzione di questa logica. Dovrai comunque stare attento a non avere il tuo codice ingombro di molte if(feature1Enabled)dichiarazioni (seguendo il principio DRY), ma ora il mantenimento di questi controlli non è più così doloroso. Ad esempio, hai un controllo molto migliore di ciò che devi modificare quando vuoi rendere gratuita una funzione a pagamento esistente (o viceversa).

Infine, diamo un'occhiata all'articolo del blog di Fowler sui toggle delle funzionalità , in cui parla dei punti di ingresso delle funzionalità / punti di attivazione / disattivazione. Vorrei citare un punto centrale:

Non cercare di proteggere ogni percorso del codice nel nuovo codice funzione con un interruttore, concentrati solo sui punti di ingresso che porterebbero gli utenti lì e attiva questi punti di ingresso.

Quindi, come strategia generale, concentrati sull'interfaccia utente e limita i tuoi controlli al numero minimo di punti necessari per far apparire o scomparire una determinata funzione. Ciò dovrebbe mantenere pulita la tua base di codice, senza ingombri inutili.


5
Fondamentalmente hai sostituito IsFreeVersion con FeaturexEnabled, non hai ridotto il numero di chiamate. Anche se non ho avuto esattamente quel caso, ho sempre gestito cose simili al momento della creazione del menu disabilitando quelle opzioni che l'utente non dovrebbe vedere. Principalmente questo è alla creazione del modulo, ma a volte ho dovuto farlo durante la preparazione di un menu a comparsa.
Loren Pechtel,

1
@LorenPechtel: dovresti leggere di nuovo la mia risposta, più attentamente. In realtà ho menzionato due cose per ridurre il numero di test condizionali, uno dei quali il principio DRY, uno focalizzato sui test nell'interfaccia utente. Più importante, la mappatura una bandiera non specifico come isFreeVersionper specifiche funzionalità parametri rimuove la maggior parte del dolore di quei test - saranno effettivamente iniziare ad avere un senso e non producono un pasticcio di manutenzione più.
Doc Brown,

9

Se non ti piacciono i if/elseblocchi, puoi riformattarli in modo da usare l'ereditarietà (vedi Sostituisci condizionale con polimorfismo dal libro Refactoring di Marin Fowler ). Questo avrebbe:

  • Rendi leggermente più semplice ragionare sul tuo codice.

  • Consentire di avere due classi, una per la versione gratuita e l'altra per la versione a pagamento, che a loro volta spedirebbero le chiamate ad altre classi, garantendo che la distinzione tra versioni gratuite e a pagamento sia limitata a due classi (tre contando il classe di base).

  • Semplifica, in seguito, l'aggiunta di altre forme del tuo software, come una variante economica o una versione premium. Aggiungerai semplicemente un'altra classe e la dichiarerai una volta nel tuo codice, e saprai che l'intera base di codice funzionerà comunque come previsto.


3
Penso che potresti voler essere più chiaro che l'ereditarietà dell'implementazione non è richiesta per questo. Un altro vantaggio è che l'app gratuita può essere fornita senza le funzionalità premium. Modificare il codice byte Java per rendere una condizione if sempre vera non è tremendamente difficile.
JimmyJames,

6

Mi sembra che la tua domanda possa essere risolta abbastanza bene applicando il modello di attivazione / disattivazione funzioni .

Come spesso accade, Pete Hodgson ha spiegato in un articolo tutti gli scenari che potresti affrontare applicando questo schema, molto meglio che io possa fare.

Ci sono anche alcune librerie che supportano questo modello. Ho avuto esperienza di lavoro con FF4J in Java ma indovinerei se si digita:

feature toggle <whatever language you prefer>

... in qualsiasi motore di ricerca otterrai diverse soluzioni.


1

C'è più di un modo per ottenere questo risultato. Il modo semplice e diretto è quello di utilizzare il modello di commutazione delle funzioni fornito in così tanti articoli. Il prossimo approccio ha a che fare con la progettazione di funzionalità plug-in. Sia Android che IOS hanno pagamenti in-app. Insieme a quel pagamento è il potenziale per un download.

Quando guardi Servlet, JAMES Mailet e persino plugin IDE, tutti usano il concetto di un'architettura plug-in:

  • Definisci un'interfaccia che la tua app sa come usare. Tale interfaccia deve fornire un modo per inserirsi nella navigazione della tua applicazione e in qualsiasi altra app per collegare i touchpoint.
  • Imposta un percorso che la tua app leggerà all'avvio (la gestione dei plug-in di runtime è molto più difficile)
  • Se esiste un plug-in (come un file Java Jar), ​​l'app legge il manifest per trovare l'implementazione dell'interfaccia del plug-in o cerca una classe che implementa l'interfaccia.
  • Una volta trovata quella classe, viene istanziata e vengono chiamati i metodi appropriati per integrare le nuove funzionalità.

Ciò che ti consente anche è l'opportunità di avere diverse classi di funzionalità disponibili per un pubblico diverso. Gli utenti hanno solo le funzionalità per cui hanno pagato.

Il codice dell'applicazione viene gestito come una base di codice e il plug-in è una base di codice separata, ma include solo le parti rilevanti per il plug-in. L'app sa come gestire i plugin quando sono presenti e il plugin sa solo come interagire con l'interfaccia.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.