Nascondere / disabilitare le funzionalità per alcuni utenti

9

Diciamo che ho una versione gratuita e a pagamento dell'app. La versione a pagamento è un superset della versione gratuita per quanto riguarda le 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 la disponibilità delle funzioni in base a un flag caricato all'avvio (ad es. gratuito / a pagamento)?

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

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

Avere 2 rami git separati per ogni versione non è un'opzione perché significherebbe mantenere 2 (o più) sorgenti di codice, sembra poco pratico in generale ed è discusso di più qui: Manutenzione di due versioni software separate dallo stesso Codebase nel controllo versione .

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

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

Utilizziamo Android / Java.

    
posta Tadija Bagarić 14.05.2018 - 12:03
fonte

4 risposte

13

Un condizionale come if(isFreeVersion) dovrebbe verificarsi solo una volta nel codice. Questo non è uno schema, ma sono sicuro che tu già conosci il nome per questo: si chiama il principio DRY . Avere un codice come " if(isFreeVersion) " in più di una posizione nel codice significa che hai ripetuto questa riga / la logica al suo interno, il che significa che dovrebbe essere refactored 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 essere simile a questo:

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

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

Ora hai il controllo di ciò che è nella versione gratuita e di quello della versione a pagamento in un unico posto, il che rende la manutenzione di questa logica abbastanza semplice. Dovrai comunque fare attenzione a non avere il tuo codice ingombrato da molte dichiarazioni if(feature1Enabled) (seguendo il principio DRY), ma ora il mantenimento di questi controlli non è più doloroso. Ad esempio, hai un controllo molto migliore di ciò che devi cambiare quando vuoi rendere libera una funzionalità a pagamento esistente (o viceversa).

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

Don't try to protect every code path in the new feature code with a toggle, focus on just the entry points that would lead users there and toggle those entry points.

Pertanto, come strategia generale, concentrati sull'interfaccia utente e limita i tuoi assegni al numero minimo di punti necessari per far apparire o scomparire determinate funzionalità. Questo dovrebbe mantenere pulito il tuo codice base, senza inutili confusioni.

    
risposta data 14.05.2018 - 22:54
fonte
9

Se non ti piacciono i blocchi if/else , puoi refactoring per utilizzare l'ereditarietà (vedi Sostituisci condizionale con polimorfismo dal libro Refactoring di Marin Fowler). Questo sarebbe:

  • Semplifica leggermente il tuo codice.

  • È possibile avere due classi, una per la versione gratuita e l'altra per la versione a pagamento, che a sua volta invierà le chiamate ad altre classi, garantendo che la distinzione tra versioni gratuite e a pagamento sia limitata a due classi (tre che contano la classe base).

  • Semplifica, in un secondo momento, l'aggiunta di altre forme del tuo software, ad esempio una variante economica o una versione premium. Dovrai semplicemente aggiungere un'altra classe e dichiararla una sola volta nel tuo codice, e saprai che l'intero codice base funzionerà come previsto.

risposta data 14.05.2018 - 13:33
fonte
6

Mi sembra che la tua domanda possa essere risolta abbastanza bene applicando il Modello di commutazione della funzionalità .

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

Ci sono anche alcune librerie che supportano questo modello. Ho avuto esperienza di lavoro con FF4J in Java, ma immagino che se digiti:

feature toggle <whatever language you prefer>

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

    
risposta data 15.05.2018 - 13:08
fonte
1

C'è più di un modo per farlo. Il modo semplice e diretto è quello di utilizzare il Pattern di commutazione delle funzionalità 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 Mailets e persino plugin IDE, tutti usano il concetto di un'architettura plug-in:

  • Definisci un'interfaccia che la tua app sa come usare. Questa interfaccia deve fornire un modo per iniettarsi nella navigazione dell'applicazione e qualsiasi altra app per i touchpoint del plugin.
  • Configura un percorso che verrà letto all'avvio dell'app (la gestione dei plug-in di runtime è molto più difficile)
  • Se esiste un plug-in (come un file Jar Java), l'app legge il manifest per trovare l'implementazione dell'interfaccia del plugin o esegue la scansione per una classe che implementa l'interfaccia.
  • Una volta trovata la classe, viene istanziata e vengono richiamati i metodi appropriati per integrare le nuove funzionalità.

Ciò che ti consente anche di avere diverse classi di funzioni disponibili per diversi tipi di pubblico. Gli utenti hanno solo le funzioni per cui hanno pagato.

Il codice dell'applicazione viene mantenuto 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 plug-in quando sono presenti e il plugin sa solo come interagire con l'interfaccia.

    
risposta data 15.05.2018 - 15:02
fonte

Leggi altre domande sui tag