Ocultando / desativando recursos para alguns usuários

10

Digamos que eu tenho uma versão gratuita e paga do aplicativo. A versão paga é um superconjunto da versão gratuita referente aos recursos disponíveis para os usuários, o que significa que a versão paga terá todos os recursos do aplicativo gratuito e mais extras.

Existe um padrão para alternar a disponibilidade do recurso com base em um sinalizador carregado na inicialização (por exemplo, gratuito / pago)?

Não gosto da ideia de ter os seguintes blocos de código em todos os lugares:

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

Ter duas ramificações git separadas para cada versão não é uma opção, porque isso significaria manter 2 (ou mais) fontes de código, parece impraticável em geral e é discutido mais aqui: Manutenção de duas versões de software separadas da mesma base de código no controle de versão .

Existe uma maneira de fazer isso, apesar de ainda ter uma única base de código e não encher o código com instruções condicionais que verificam o sinalizador de pagamento gratuito?

Tenho certeza de que isso foi discutido várias vezes antes e tenho certeza de que existem alguns padrões para abordar esse problema, mas simplesmente não consigo encontrá-lo.

Usamos Android / Java.

Tadija Bagarić
fonte
@gnat tnx, bom achado. Mas eu gostaria de discutir as opções que não requerem ramos separados e manutenção de várias bases de código
Tadija Bagarić
2
Isso é semelhante a ter diferentes níveis de autorização. Você pode analisar como esse problema normalmente é tratado, onde um recurso está disponível apenas para determinados usuários / funções.
Bart van Ingen Schenau
@BartvanIngenSchenau Acho que são principalmente ifverificações para ocultar controles de recursos proibidos ou ter uma caixa de diálogo pop-up para quando o usuário tenta fazer o que não tem permissão. Estou esperando para encontrar uma maneira de evitar muitos condicionais no código
Tadija Bagarić
2
Use poliformismo. Você nunca terá que se perguntar sobre isso novamente, e será MUITO mais fácil de manter!
Steve Chamaillard

Respostas:

14

Um like condicional if(isFreeVersion)deve ocorrer apenas uma vez no código. Este não é um padrão, mas tenho certeza que você já sabe o nome para ele: é chamado de princípio DRY . Ter código como " if(isFreeVersion)" em mais de um lugar no seu código significa que você repetiu essa linha / a lógica nela, o que significa que deve ser refatorado para evitar a repetição.

" if(isFreeVersion)" deve ser usado para configurar uma lista de opções de configuração interna para diferentes recursos. O código resultante pode ficar assim:

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

Isso mapeia seu sinalizador "isFreeVersion" para diferentes recursos . Observe que você pode decidir aqui se preferir usar sinalizadores booleanos individuais para recursos individuais ou usar algum tipo de outros parâmetros, por exemplo, diferentes objetos de estratégia com uma interface comum, se o controle de recurso exigir uma parametrização mais complexa.

Agora você tem o controle do que está na versão gratuita e do que está na versão paga em um só lugar, o que simplifica bastante a manutenção dessa lógica. Você ainda terá que ter cuidado para não ter seu código confuso com muitas if(feature1Enabled)instruções (seguindo o princípio DRY), mas agora a manutenção dessas verificações não é mais tão dolorosa. Por exemplo, você tem um controle muito melhor do que precisa alterar quando deseja liberar um recurso pago existente (ou vice-versa).

Finalmente, vamos dar uma olhada no artigo do blog de Fowler sobre alternância de recursos , onde ele fala sobre pontos de entrada / alternância de recursos. Permitam-me citar um ponto central:

Não tente proteger todos os caminhos de código no novo código de recurso com uma alternância, concentre-se apenas nos pontos de entrada que levariam os usuários até lá e alterne esses pontos de entrada.

Portanto, como estratégia geral, concentre-se na interface do usuário e restrinja suas verificações ao número mínimo de pontos necessários para que um determinado recurso apareça ou desapareça. Isso deve manter sua base de códigos limpa, sem qualquer confusão desnecessária.

Doc Brown
fonte
5
Você basicamente substituiu IsFreeVersion por FeaturexEnabled, não reduziu o número de chamadas. Embora eu não tenha tido exatamente esse caso, sempre lidei com coisas semelhantes ao criar o menu, desativando as opções que o usuário não deveria ver. Principalmente, isso ocorre na criação de formulários, mas às vezes eu tenho que fazer isso ao preparar um menu pop-up.
Loren Pechtel
11
@ LorenPechtel: você deve ler minha resposta novamente, com mais cuidado. Na verdade, mencionei duas coisas para reduzir o número de testes condicionais, um deles o princípio DRY, um deles focado em testes na interface do usuário. Mais importante, mapeando uma bandeira inespecíficos como isFreeVersiona específicas parâmetros recurso elimina a maioria da dor desses testes - eles vão realmente começar a fazer sentido e não produzem uma bagunça manutenção mais.
Doc Brown
9

Se você não gosta de if/elseblocos, pode refatorá-los para usar herança (consulte Substituir condicional por polimorfismo no livro Refatoração de Marin Fowler ). Isso seria:

  • Simplifique um pouco o raciocínio sobre seu código.

  • Torne possível ter duas classes, uma para a versão gratuita e a outra para a versão paga, que, por sua vez, despachariam as chamadas para outras classes, garantindo que a distinção entre versões gratuitas e pagas seja limitada a duas classes (três contando o classe base).

  • Facilite, posteriormente, adicionar outras formas do seu software, como uma variante barata ou uma versão premium. Você apenas adicionará outra classe e a declarará uma vez no seu código, e saberá que toda a base de códigos ainda funcionaria conforme o esperado.

Arseni Mourzenko
fonte
3
Eu acho que você pode querer deixar mais claro que a herança da implementação não é necessária para isso. Outro benefício é que o aplicativo gratuito pode ser entregue sem os recursos premium. Modificar o código de bytes Java para criar uma condição if sempre verdadeira não é muito difícil.
21418 JimmyJames
6

Parece-me que sua pergunta poderia ser resolvida muito bem aplicando o Padrão de alternância de recursos .

Como é frequentemente o caso, Pete Hodgson explicou em um artigo todos os cenários que você poderia enfrentar ao aplicar esse padrão, muito melhor do que eu poderia fazer.

Existem também algumas bibliotecas que suportam esse padrão. Eu tinha experiência em trabalhar com FF4J em Java, mas acho que se você digitar:

feature toggle <whatever language you prefer>

... em qualquer mecanismo de pesquisa, você terá várias soluções.

danidemi
fonte
1

Há mais de uma maneira de conseguir isso. A maneira simples e direta é usar o Padrão de alternância de recursos, fornecido em muitos artigos. A próxima abordagem tem a ver com o design de recursos plugáveis. O Android e o IOS têm pagamentos no aplicativo. Junto com esse pagamento está o potencial para um download.

Quando você olha Servlets, JAMES Mailets e até plugins IDE, todos usam o conceito de arquitetura de plug-in:

  • Defina uma interface que seu aplicativo saiba usar. Essa interface precisa fornecer uma maneira de se injetar na navegação do aplicativo e em qualquer outro aplicativo para conectar pontos de contato.
  • Configure um caminho que seu aplicativo lerá na inicialização (o gerenciamento de plug-in em tempo de execução é muito mais difícil)
  • Se existir um plug-in (como um arquivo Java Jar), ​​o aplicativo lê o manifesto para encontrar a implementação da interface do plug-in ou procura uma classe que implemente a interface.
  • Depois que essa classe é encontrada, ela é instanciada e os métodos apropriados são chamados para integrar os novos recursos.

O que isso também permite é a oportunidade de ter diferentes classes de recursos disponíveis para diferentes públicos. Os usuários têm apenas os recursos pelos quais pagaram.

O código do aplicativo é mantido como uma base de código e o plug-in é uma base de código separada - mas inclui apenas as partes relevantes para o plug-in. O aplicativo sabe como lidar com plug-ins quando eles estão presentes, e o plug-in sabe apenas como interagir com a interface.

Berin Loritsch
fonte