Как мне улучшить этот код на C++

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


// i'm receiving a string containing : id operation arguments
data    = read(socket);
tokens  = tokenize(data," "); // tokenize the string based on spaces
if (tokens[0] == "A") {
   if (tokens[1] == "some_operation") {
      // here goes code for some_operation , will use the remaining tokens as arguments for function calls
   }
   else if (tokens[1] == "some_other_operation") {
     // here goes code for some_other_operation , will use the remaining tokens
   }
   ...
   else {
     // unknown operation
   }
}
else if (tokens[0] == "B") {
   if (tokens[1] == "some_operation_for_B") {
     // do some operation for B
   }
   else if (tokens[1] == "yet_another_operation") {
     // do yet_another_operation for B
   }
   ...
   else {
     // unknown operation
   } 
}

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
0
751
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

Создайте класс для каждого идентификатора, который реализует общий интерфейс. В основном Шаблон стратегии IIRC.

Итак, вы бы назвали (псевдо) код вроде:

StrategyFactory.GetStrategy(tokens[0]).parse(tokens[1..n])

Я успешно использовал этот шаблон, чтобы превратить программу из 1000+ строк в цикл из 10 строк (+1)

ThatBloke 27.10.2008 12:01

Вы, вероятно, имеете в виду 10-строчный цикл в основной и 2000+ строк во вспомогательных классах :) Нет никакого волшебства, которое вам все еще нужно для работы ...

Ilya 27.10.2008 12:12

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

Dave Van den Eynde 27.10.2008 14:31

Вы хотите разделить это на несколько функций, по одной для каждого идентификатора и по одной для каждой операции.

Обычно я использую высоту экрана. Если у меня нет функции, полностью умещающейся на моем экране, я начинаю думать о разделении ее на части. Таким образом, вам не нужно прокручивать, чтобы увидеть, куда идет функция. Как я уже сказал, это рекомендация, а не правило, но я считаю более практичным контролировать структуру.

Если вы затем захотите применить объектно-ориентированный подход и превратить его в группу классов, вы можете сделать это, если видите преимущество. Однако помните обо всей сантехнике, которая идет с ним. Возможно, вы захотите сделать это простым.

Дэйв

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

Ilya 27.10.2008 11:55

Создайте карту функций. Тогда у вас будет такой код:

consumed_count = token_mapper[tokens[0]](tokens)
remove amount of consumed tokens according to the return value and repeat.

Хотя я все равно не понимаю вашего подхода, вы собираетесь написать язык, с которым трудно справиться и который негибкий. Подумайте об этом: небольшая разница в количестве аргументов вызывает настоящий хаос в этом языке. Поэтому вы всегда ограничены 1-3 аргументами на команду.

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

Это важно, даже если ваш язык будет сгенерирован машиной, что, если в вашем генераторе будет ошибка? Неудачи рано, неудачи часто.

Не все мои функции имеют одинаковое количество параметров.

Geo 27.10.2008 11:54

Вы можете заглянуть в «Табличные методы» (как описано в «Завершенном коде», 2-е издание, глава 18). Думаю это что описывает Cheery. Преимущество этого - легкая расширяемость. Вам просто нужно добавить несколько записей в таблицу. Эта таблица может быть жестко запрограммирована или даже загружена во время выполнения.

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

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

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

Сначала запишите синтаксис того, что вы поддерживаете, а затем напишите код для его поддержки.

Для этого отлично подходит использование нотации BNF. А использовать библиотеку Spirit для кодовой части довольно просто.

Command := ACommand | BCommand

ACommand := 'A' AOperation
AOperation := 'some_operation' | 'some_other_operation'

BCommand := 'B' BOperation
BOperation := 'some_operation_for_B' | 'some_other_operation_for_B'

Это легко переводится в парсер Spirit. Каждое производственное правило стало бы однострочным, каждый конечный символ переводился бы в функцию.

#include "stdafx.h"
#include <boost/spirit/core.hpp>
#include <iostream>
#include <string>

using namespace std;
using namespace boost::spirit;

namespace {
    void    AOperation(char const*, char const*)    { cout << "AOperation\n"; }
    void    AOtherOperation(char const*, char const*)    { cout << "AOtherOperation\n"; }

    void    BOperation(char const*, char const*)    { cout << "BOperation\n"; }
    void    BOtherOperation(char const*, char const*)    { cout << "BOtherOperation\n"; }
}

struct arguments : public grammar<arguments>
{
    template <typename ScannerT>
    struct definition
    {
        definition(arguments const& /*self*/)
        {
            command
                =   acommand | bcommand;

            acommand = chlit<char>('A') 
              >> ( a_someoperation | a_someotheroperation );

            a_someoperation = str_p( "some_operation" )           [ &AOperation ];
            a_someotheroperation = str_p( "some_other_operation" )[ &AOtherOperation ];

            bcommand = chlit<char>('B') 
              >> ( b_someoperation | b_someotheroperation );

            b_someoperation = str_p( "some_operation_for_B" )           [ &BOperation ];
            b_someotheroperation = str_p( "some_other_operation_for_B" )[ &BOtherOperation ];

        }

        rule<ScannerT> command;
        rule<ScannerT> acommand, bcommand;
        rule<ScannerT> a_someoperation, a_someotheroperation;
        rule<ScannerT> b_someoperation, b_someotheroperation;

        rule<ScannerT> const&
        start() const { return command; }
    };
};

template<typename parse_info >
bool test( parse_info pi ) {
  if ( pi.full ) { 
    cout << "success" << endl; 
    return true;
  } else { 
    cout << "fail" << endl; 
    return false;
  }
}

int _tmain(int argc, _TCHAR* argv[])
{

  arguments args;
  test( parse( "A some_operation", args, space_p ) );
  test( parse( "A some_other_operation", args, space_p ) );
  test( parse( "B some_operation_for_B", args, space_p ) );
  test( parse( "B some_other_operation_for_B", args, space_p ) );
  test( parse( "A some_other_operation_for_B", args, space_p ) );

    return 0;
}

Я видел решение этой проблемы, которое сработало: хеш-таблица функций.

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

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

Заставляет код работать как конечный автомат, что (IMHO) является самым простым способом приблизиться к сетевому коду.

Другие вопросы по теме