Я нахожусь в ситуации, когда я думаю, что две реализации являются основными, и я не знаю, какую из них выбрать.
У меня есть приложение, имитирующее устройства чтения карт. Он имеет графический интерфейс, в котором вы можете выбрать, какой последовательный порт и скорость использовать, а также кнопку воспроизведения и остановки.
Я ищу лучшую реализацию для построения читателей.
у меня есть SimulatorCore
класс, который живет так долго, как мое заявление
SimulatorCore инстанцирует Reader
учебный класс. И можно будет смоделировать несколько считывателей на нескольких последовательных портах.
Две возможности:
мой Reader
является указателем (динамическое создание), я создаю его при нажатии кнопки воспроизведения, удаляю при нажатии кнопки остановки.
мой Reader
это объект (статическое воплощение), я воплощаю его в SimulatorCore
конструктор затем создать и вызвать Reader.init()
а также Reader.cleanup()
в мой класс Reader и вызывать их, когда ударили play и stop
Я лично вижу функциональную сторону, и я явно хочу использовать указатель, и у меня нет экземпляра какого-либо читателя, если он не имитируется.
Кто-то говорит мне, что я должен использовать статическое создание экземпляров (Причина: для безопасности, и потому что «плохо использовать указатель, когда у вас есть выбор не использовать их»)
Я не знаком с ними, но я думаю, что я также могу использовать умный указатель.
Примеры кода: 1-е решение:
class SimulatorCore
{
play(){reader = new Reader();};
stop(){delete reader; reader = nullptr;};
private:
Reader *reader;
}
Примеры кода: 2-е решение:
class SimulatorCore
{
play(){reader.init();};
stop(){reader.cleanup();};
private:
Reader reader;
}
Код не тестируется, я приведу его в качестве иллюстрации.
Какое лучшее решение? Зачем ?
Как правило, следует избегать динамического выделения с new
сами, поэтому, если вы собираетесь использовать 1-е решение, вы должны вместо этого использовать умные указатели.
Однако главный вопрос здесь — это вопрос логики. Реальный кард-ридер существует в режиме ожидания, пока он не используется. Во втором решении, что делать init
а также cleanup
делать? Они просто переводят устройство чтения карт в состояние ожидания или начинают имитировать фактическое считывание карты? Если это первый случай, я предлагаю, чтобы это поведение было в конструкторе и деструкторе Reader
, а затем создание Reader
Объект означает создание кард-ридера. Если это второй случай, то я бы сказал, что 2-е решение в значительной степени правильно, просто что функции имеют неправильные имена.
Что кажется мне наиболее логичным, так это что-то вроде этого:
class SimulatorCore
{
play(){reader.start();};
stop(){reader.stop();};
private:
Reader reader;
}
Да, все, что я сделал, это изменил имена функций для Reader
, Однако функции теперь не несут ответственности за инициализацию или очистку считывателя — эта ответственность находится в руках Reader
конструктор и деструктор. Вместо, start
а также stop
начало и конец симуляции Reader
, Один Reader
Затем экземпляр может входить и выходить из этого режима симуляции несколько раз за время своего существования.
Если позже вы захотите распространить эту идею на несколько Reader
s, вы можете просто изменить члена на:
std::vector<Reader> readers;
Тем не менее, я не могу знать наверняка, что это то, что вы хотите, потому что я не знаю логику вашей программы. Надеюсь, это даст вам некоторые идеи.
Опять же, что бы вы ни решили сделать, вам следует избегать использования new
выделить свой Reader
s, а затем также избегать использования сырых указателей для ссылки на эти Reader
s. Используйте умные указатели и их соответствующие make_...
функции для динамического размещения этих объектов.
Вы можете легко использовать shared_ptr / unique_ptr:
class SimulatorCore
{
play(){_reader = make_shared<Reader>();};
stop(){_reader = nullptr};
private:
shared_ptr<Reader> _reader;
}
Я думаю, это решит вашу проблему правильно.
Динамическое выделение создает некоторые проблемы, например, с исключением исключений (может произойти потеря памяти, если между play () и stop (), например, возникнет исключение, и stop () никогда не будет вызываться). Или вы можете просто забыть где-нибудь вызвать stop () до уничтожения SimulatorCore, это возможно, если программа тяжелая.
Если вы никогда не пробовали умные указатели, это хороший шанс начать это делать.
Это явно зависит от того, как организована вся ваша программа, но в целом, я думаю, я бы предпочел статический подход, потому что ОТВЕТСТВЕННОСТЬ соображения:
Предположим, у вас есть отдельный класс, который обрабатывает последовательную связь. Этот класс будет отправлять и получать сообщения и отправлять их классу чтения. Сообщение может прибыть в любое время. Разница между динамическим и статическим подходами заключается в:
Поэтому я думаю, что статический подход немного проще и понятнее.
Однако, если есть шанс, что вам придется реализовать другие, разные читательские классы в будущем динамический подход облегчит это расширение, поскольку соответствующий класс можно легко создать во время выполнения.
Таким образом, динамический подход предлагает больше гибкости.