Скрытие / отключение функций для некоторых пользователей

10

Допустим, у меня есть бесплатная и платная версия приложения. Платная версия - расширенный вариант бесплатной версии, касающийся функций, доступных пользователям. Это означает, что платная версия будет иметь все функции бесплатного приложения плюс дополнительные.

Есть ли шаблон для переключения доступности функции на основе флага, который загружается при запуске (например, бесплатно / платно)?

Мне не нравится идея иметь везде следующие блоки кода:

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

Наличие 2-х отдельных веток git для каждой версии не вариант, потому что это означало бы поддержание 2 (или более) источников кода, в общем случае нецелесообразно и обсуждается более подробно здесь: Поддержка двух отдельных версий программного обеспечения из одной и той же базы кода в управлении версиями .

Есть ли способ сделать это, все еще имея единую кодовую базу и не засоряя код условными операторами, которые проверяют флаг free / paid?

Я уверен, что это обсуждалось много раз прежде, и я уверен, что есть некоторые подходы к решению этой проблемы, но я просто не могу найти это.

Мы используем Android / Java.

Тадия багарич
источник
@gnat TNX, хорошая находка. Но я хотел бы обсудить варианты, которые не требуют отдельных веток и поддержки нескольких кодовых баз
Тадия Багарич
2
Это похоже на наличие разных уровней авторизации. Вы можете посмотреть, как обычно решается эта проблема, когда функция доступна только для определенных пользователей / ролей.
Барт ван Инген Шенау
@BartvanIngenSchenau Я считаю, что это в основном ifпроверки, чтобы скрыть элементы управления для запрещенных функций или всплывающее диалоговое окно, когда пользователь пытается сделать то, что ему запрещено. Я надеюсь найти способ избежать многих условных
выражений
2
Используйте полиформизм. Вам никогда не придется снова спрашивать себя об этом заявлении, и это будет ОЧЕНЬ легче поддерживать!
Стив Chamaillard

Ответы:

14

Условное подобное if(isFreeVersion)должно встречаться в коде только один раз . Это не шаблон, но я уверен, что вы уже знаете его название: это называется принцип СУХОЙ . Наличие кода, подобного " if(isFreeVersion)", в нескольких местах в вашем коде означает, что вы повторили эту строку / логику в ней, что означает, что ее следует реорганизовать, чтобы избежать повторения.

« if(isFreeVersion)» следует использовать для настройки списка внутренних параметров конфигурации для различных функций. Полученный код может выглядеть следующим образом:

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

Это сопоставляет ваш единственный флаг isFreeVersion с различными функциями . Обратите внимание, что здесь вы можете решить, предпочитаете ли вы использовать отдельные логические флаги для отдельных функций или использовать какие-то другие параметры, например, различные объекты стратегии с общим интерфейсом, если элемент управления функциями требует более сложной параметризации.

Теперь вы можете контролировать, что находится в бесплатной версии и в платной версии в одном месте, что делает обслуживание этой логики довольно простым. Вам все равно придется быть осторожным, чтобы ваш код не был загроможден множеством if(feature1Enabled)операторов (следуя принципу СУХОЙ), но теперь обслуживание этих проверок теперь не так уж и болезненно. Например, вы гораздо лучше контролируете, что вам нужно изменить, если хотите сделать существующую платную функцию бесплатной (или наоборот).

Наконец, давайте посмотрим на статью Фаулера в блоге о переключателях функций , где он говорит о точках входа / переключателях функций. Позвольте мне привести одну центральную точку:

Не пытайтесь защитить каждый путь кода в новом коде функции с помощью переключателя, сосредоточьтесь только на точках входа, которые могут привести пользователей туда, и переключите эти точки входа.

Таким образом, в качестве общей стратегии сфокусируйтесь на пользовательском интерфейсе и ограничьте количество проверок минимальным количеством точек, необходимых для появления или исчезновения определенной функции. Это должно поддерживать вашу базу кода в чистоте, без каких-либо ненужных помех.

Док Браун
источник
5
Вы в основном заменили IsFreeVersion на FeaturexEnabled, вы не сократили количество вызовов. Хотя у меня не было точно такого случая, я всегда обрабатывал подобные вещи при создании меню, отключая те опции, которые пользователь не должен видеть. В основном это при создании формы, но мне иногда приходилось делать это при подготовке всплывающего меню.
Лорен Печтел
1
@LorenPechtel: вы должны прочитать мой ответ еще раз, более внимательно. Я фактически упомянул две вещи, чтобы уменьшить количество условных тестов, один из них принцип DRY, один из которых фокусируется на тестах в пользовательском интерфейсе. Что еще более важно, сопоставление неопределенного флага, например, isFreeVersionс параметрами конкретной функции, устраняет большинство трудностей этих тестов - они действительно начнут иметь смысл и больше не приведут к беспорядку обслуживания.
Док Браун
9

Если вам не нравятся if/elseблоки, то вы можете реорганизовать их, чтобы использовать наследование (см. « Заменить условный полиморфизмом» из книги « Рефакторинг Марина Фаулера» ). Это будет:

  • Сделайте немного проще рассуждать о вашем коде.

  • Сделайте возможным иметь два класса, один для бесплатной версии, а другой для платной версии, которые, в свою очередь, будут передавать вызовы другим классам, гарантируя, что различие между бесплатной и платной версиями ограничено двумя классами (три считая базовый класс).

  • Позже упростите добавление других форм вашего программного обеспечения, таких как дешевый вариант или премиум-версия. Вы просто добавите еще один класс и объявите его один раз в своем коде, и вы будете знать, что вся база кода будет работать, как и ожидалось.

Арсений Мурзенко
источник
3
Я думаю, что вы могли бы хотеть быть более ясным, что наследование реализации не требуется для этого. Еще одним преимуществом является то, что бесплатное приложение может быть доставлено без дополнительных функций. Изменение байтового кода Java, чтобы сделать условие if всегда истинным, не очень сложно.
JimmyJames
6

Мне кажется, ваш вопрос мог бы быть решен довольно хорошо с помощью шаблона Feature Toggle .

Как часто бывает, в одной статье Пит Ходжсон объяснил все сценарии, с которыми вы можете столкнуться, применяя этот шаблон, гораздо лучше, чем я мог бы сделать.

Есть также некоторые библиотеки, которые поддерживают этот шаблон. У меня был опыт работы с FF4J на Java, но я думаю, если вы введете :

feature toggle <whatever language you prefer>

... в любой поисковой системе вы получите несколько решений.

danidemi
источник
1

Есть несколько способов сделать это. Простой и прямой способ - использовать шаблон переключения функций, который был представлен во многих статьях. Следующий подход связан с проектированием подключаемых функций. И Android, и IOS имеют платежи в приложении. Наряду с этим возможна загрузка.

Когда вы смотрите на Servlets, JAMES Mailets и даже на плагины IDE, все они используют концепцию архитектуры плагинов:

  • Определите интерфейс, который ваше приложение знает, как использовать. Этот интерфейс должен предоставить способ внедрить себя в навигацию вашего приложения и любого другого приложения для добавления точек соприкосновения.
  • Задайте путь, который будет читать ваше приложение при запуске (управление плагинами во время выполнения намного сложнее)
  • Если плагин существует (например, файл Jar Java), приложение либо читает манифест, чтобы найти реализацию интерфейса плагина, либо ищет класс, который реализует интерфейс.
  • Как только этот класс найден, он создается и соответствующие методы вызываются для интеграции новых функций.

Это также позволяет вам иметь различные классы функций, доступные для разных аудиторий. У пользователей есть только те функции, за которые они заплатили.

Код вашего приложения поддерживается как одна кодовая база, а ваш плагин - это отдельная кодовая база - но включает только те части, которые имеют отношение к плагину. Приложение знает, как обращаться с плагинами, когда они присутствуют, а плагин знает, как взаимодействовать с интерфейсом.

Берин Лорич
источник