Я слышал и видел, почему приведение типов обычно является признаком плохой иерархии / дизайна классов, если только это не из внешней библиотеки, которую вы не можете контролировать. Однако в этом сценарии я совсем не уверен, как этого избежать.
Я разрабатываю небольшую программу, которая выполняет код на основе абстрактного дерева. Все значения получены из одного базового класса Value
. Теперь я хочу добавить их, поэтому, прежде всего, проверьте тип, если это разрешено - A->GetType()->HasOperator( "+", { A->GetType(), B->GetType() } )
. Возможно, нет необходимости, если я статически анализирую дерево. Но на самом деле проблема заключается в следующем шаге. Скажем A->GetType()->GetOperator( "+" )->Execute( { A, B } )
с подписью virtual Value* Execute( Value* values[] )
. Что теперь?
Сам тип Value
не может содержать значения, потому что производные классы, такие как Number
, знают, как и какой тип информации они имеют (конечно, это относится только к примитивам). типы, а не определенные пользователем, с ними легче иметь дело после того, как вы научитесь обращаться с примитивами). Я действительно не могу определить оператор +
для 2 типов чисел, потому что у меня все еще остается 2 Value*
s, которые я должен передать этому методу, а затем мне все еще нужно преобразовать их в Number
в этом методе.
Одно решение, которое пришло мне в голову, - просто заставить Value
иметь двоичную информацию, работать с примитивами в двоичном формате и ... Прежде всего, это просто приведение типов с дополнительными шагами в меньшем масштабе. Во-вторых, это много работы. В-третьих, можно также построить двоичную машину.
Вторым решением является явное определение всех примитивных операций. Итак, Number* AddNumberToNumber ( Number* A, Number* B )
, Number* GetNumberField ( Object object, string name )
et c. для всех примитивов. Кажется утомительным, и я не уверен, возможно ли вообще хранить его разумным способом. Прямо сейчас выполнение кода работает как стек - каждый Instruction
после выполнения оставляет больше Instruction
s в стеке кода. Так что, если бы я сделал это, мне пришлось бы определять целый производный класс для каждого оператора вместо одного делегата для каждого. (Я думаю, что это работает, но это не обобщает хорошо, и если бы я должен был проанализировать текст в коде, мне нужно было бы статически проанализировать его и иметь помехи if..else для каждой операции вместо, скажем, LUT Operator -> [ List of accepted types, Delegate ]
Другими словами, я думаю, что это решение будет беспорядок). Я предпочел бы иметь это или что-то подобное:
standardOperators = new map< ... > {
{ "+",
{ { types::number, types::number }, [ some delegate ] },
{ ... },
...
},
{ "-",
...
},
...
};
Итак, есть ли лучшее решение? Или приведение типов является лучшим вариантом на этом уровне мастеринга?