Как реализовать таблицу диспетчеризации для текстовой приключенческой игры?
Я делаю текстовое приключение в C#, и кто-то предложил мне использовать таблицу диспетчеризации вместо оператора switch.
Вот код оператора switch:
#region Public Methods
public static void Do(string aString)
{
if(aString == "")
return;
string verb = "";
string noun = "";
if (aString.IndexOf(" ") > 0)
{
string[] temp = aString.Split(new char[] {' '}, 2);
verb = temp[0].ToLower();
noun = temp[1].ToLower();
}
else
{
verb = aString.ToLower();
}
switch(Program.GameState)
{
case Program.GameStates.Playing:
if (IsValidInput(Commands, verb, true))
{
switch(verb) //this is the switch statement
{
case "help":
case "?":
WriteCommands();
break;
case "exit":
case "quit":
Program.GameState = Program.GameStates.Quit;
break;
case "move":
case "go":
MoveTo(noun);
break;
case "examine":
Examine(noun);
break;
case "take":
case "pickup":
Pickup(noun);
break;
case "drop":
case "place":
Place(noun);
break;
case "use":
Use(noun);
break;
case "items":
case "inventory":
case "inv":
DisplayInventory();
break;
case "attack":
//attack command
break;
}
}
break;
case Program.GameStates.Battle:
if(IsValidInput(BattleCommands, verb, true))
{
switch(verb) //this is the other switch statement
{
case "attack":
//attack command
break;
case "flee":
case "escape":
//flee command
break;
case "use":
//use command
break;
case "items":
case "inventory":
case "inv":
//items command
break;
}
}
break;
}
}
#endregion
Как мне изменить этот факт, чтобы использовать таблицу диспетчеризации?
3 ответа
Простейшим способом было бы использовать словарь делегатов.
Например:
Dictionary<string, Action> dispatch = new Dictionary<string, Action>();
dispatch["help"] = new Action(() => Console.WriteLine("Hello"));
dispatch["dosomething"] = new Action(() =>
{
// Do something else
Console.WriteLine("Do Something");
});
// Call the 'help' command
dispatch["help"]();
Для нескольких различных параметров может быть проще всего использовать базовый делегат и использовать динамический вызов.
Dictionary<string, Delegate> dispatch = new Dictionary<string, Delegate>();
dispatch["help"] = new Action(() => Console.WriteLine("Hello"));
dispatch["dosomething"] = new Action<string>(s => Console.WriteLine(s));
dispatch["help"].DynamicInvoke();
dispatch["dosomething"].DynamicInvoke("World");
И если вы используете.NET 4, вы также можете использовать динамические типы для разрешения во время выполнения, чтобы немного уменьшить беспорядок динамического вызова.
Dictionary<string, dynamic> dispatch = new Dictionary<string, dynamic>();
dispatch["help"] = new Action(() => Console.WriteLine("Hello"));
dispatch["dosomething"] = new Action<string>(s => Console.WriteLine(s));
dispatch["help"]();
dispatch["dosomething"]("World");
Чтобы вернуть представление @Jon Ericson:
Таблица диспетчеризации - это структура данных, которая связывает индексное значение (или ключ, см. Комментарий @ pst ниже) с действием. Это довольно элегантная замена для оператора типа switch.
Что касается части реализации, взгляните на этот вопрос и, в частности, на этот ответ, который ИМХО кажется довольно правильным, но остается легким для понимания.
Возможно, он ссылался на "Двойную рассылку" или " Шаблон посетителя".
Вы можете разделить ваш код, чтобы улучшить стиль диспетчера следующим образом:
public interface IGameState{
void Help();
void Question();
void Attack();
}
public interface ICommand{
bool IsValidFor(PlayingState state);
bool IsValidFor(BattleState state);
void Execute(IGameState state);
}
public class PlayingState : IGameState {
public void Help(){ // Do Nothing }
public void Question() { WriteCommands(); }
private void WriteCommands(){ }
}
public class Battle : IGameState{
public void Help(){ // Do Nothing }
public void Question() { WriteCommands(); }
public void Attack() { Roll(7); }
private void Roll(int numDice){ }
}
public class CommandBuilder{
public ICommand Parse(string verb){
switch(verb){
case "help":
return new HelpCommand();
case "?":
return new QuestionCommand();
case "attack":
return new AttackCommand();
default:
return new UnknownCommand();
}
}
}
public class QuestionCommand(){
bool IsValidFor(PlayingState state){
return true;
}
bool IsValidFor(BattleState state){
return false;
}
void Execute(IGameState state){
state.Question();
}
}
public static void Do(string aString){
var command = CommandBuilder.Parse(aString);
if(command.IsValidFor(Program.GameStates))
command.Execute(Program.Gamestates);
}