Что вы должны заменить перечислением, если значения должны быть предоставлены плагинами?

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

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

Переименование выглядит примерно так:

enum class HardwareVersion
{
    Stage1_Base,           // corresponds to version 5.11.37
    Stage1_Extensions,     // corresponds to version 5.12.01
    Stage2_Base,           // corresponds to version 8.00.00
    Stage2_ABC_Extensions, // corresponds to version 8.03.20
    Stage2_DEF_Extensions  // corresponds to version 9.01.00
}

Примечание. Имена типа «Base 1 Base» - это термины, которые обычно используются при обсуждении с руководством проекта или клиентом, поэтому они также использовались в коде.

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

// This would be in a plugin
void addContent( HardwareVersion currentStage ) /* override */
{
    if (currentStage < Stage2_Base) return;

    // add stage 2 content
    ...
}

и

// This would be in a plugin as well
void initialize() /* override */
{
    _someFactory->register( HardwareVersion::Stage2_Base, new Stage2SubcontrolFactory() );
}

// This would be somewhere in the core application
std::shared_ptr<Subcontrol> createSubcontrol( HardwareVersion currentStage )
{
    return getFactory( currentStage )->createSubcontrol();
}

Теперь мы могли бы просто заменить enum на typedef int и терять безопасность всех типов и т. д. Мы также можем использовать GUID и потерять всю удобочитаемость и сделать отладку очень тяжелой.

Оба решения чувствуют себя очень плохо. Это (наконец) приводит к вопросу:

Что вы должны заменить перечислением, если значения должны быть предоставлены плагинами?

[Edit:] Чтобы сделать это менее субъективным: два основных критерия для решений - высокая ремонтопригодность (этот вид подразумевает читаемость для меня) и высокую стабильность при добавлении новых плагинов.

2 голоса | спросил Tim Meyer 13 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 13 Sep 2016 18:40:34 +0300 2016, 18:40:34

2 ответа


2

После спящего вопроса на ночь и размышления о различных комментариях, я нашел следующее решение, подходящее для наших нужд:

Создайте HardwareVersion C-style POD (*) struct следующим образом:

struct IMPORT_EXPORT HardwareVersion
{
public:
    /// The first part of the version
    unsigned int Major;
    /// The second part of the version
    unsigned int Minor;
    /// The third part of the version
    unsigned int Release;
    /// A readable name mainly used for logging and debugging
    char* ReadableName;
}

Кроме того, A Cpp Wrapper с функциями, такими как сравнение, поддержка хэша, строка кортежа версии ("XX.YY.ZZ", где XX = Major, YY = Minor и ZZ = Release; обратите внимание, что по определению ни одна из частей версии не может превышать 99 в этом случае) будет добавлена, но не будет отображаться через интерфейс плагина.

Это имеет следующие преимущества:

  • Исполняемый файл можно реализовать, чтобы не разрешать плагины, которые предоставляют объекты HardwareVersion, которые уже известны ИЛИ он может быть реализован, чтобы всегда использовать новейший, поэтому патчи могут быть реализованы через плагины (это, скорее всего, не будет решено мной).
  • Плагины могут указать, с какими плагинами они зависят от номера версии (например, если Stage2_ABC_Extensions имеет фабрику, которая предоставляет подкласс объект Stage2_Base, плагины могут быть инициализированы в соответствии с их цепочкой зависимостей).
  • Код, который должен выполняться только выше или ниже определенного этапа, по-прежнему доступен для чтения.
  • Приложение может быть расширено только добавлением плагина без дополнительной настройки, требуемой в пользовательской среде.

(*) POD, чтобы увеличить шансы написать плагин с другим компилятором (хотя я знаю, что это не гарантируется)

ответил Tim Meyer 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2016 10:05:36 +0300 2016, 10:05:36
0

Я бы сказал, что вместо использования фактического перечисления было бы желательно, чтобы каждый плагин использовал набор смещений от некоторого базового значения. Базовое значение для каждого плагина может быть предоставлено при загрузке плагина.

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

ответил Jerry Coffin 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 14 Sep 2016 03:33:26 +0300 2016, 03:33:26

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132