#c# #design-patterns
#c# #шаблоны проектирования
Вопрос:
Допустим, у нас есть семейство классов (cards, ради этого), и нам нужно создать их экземпляры на основе некоторого идентификатора. Заводской метод будет выглядеть следующим образом:
public Card GetCard(int cardNumber)
{
switch(cardNumber)
{
case 13: return new King();
case 12: return new Queen();
case 11: return new Jack();
}
//...
}
Чего я хочу, так это избежать этого switch
. Почему? Возможно, я хочу повторно использовать это сравнение в функции.
То, что я придумал, примерно так:
private Dictionary<int, Type> cardTypes =
{
{13, typeof(King)},
{12, typeof(Queen)},
{11, typeof(Jack)}
};
public Card GetCard(int cardNumber)
{
var cardType = cardTypes[cardNumber];
var instance = Activator.CreateInstance(cardType);
return (Card)instance;
}
Однако это решение использует отражение, которое является дорогостоящим, а также проблематичным, когда у вас более одного «идентификатора» (например, 1 и 14 дают Ace
оба — должен ли я добавить 2 ключа в словарь?).
Какова наилучшая практика в этом сценарии?
Комментарии:
1. Я лично думаю, что если коллекция типов фиксирована, четко известна И коротка (в человеческом смысле), фабричный шаблон требует значительных изменений, ваше первое решение может быть достаточно хорошим. Если он должен быть расширяемым Или доступным для чтения в будущих неизвестных типах ИЛИ огромным, может подойти factory. Но это наиболее спорно 😉
Ответ №1:
Вместо сохранения типа в словаре, вы могли бы сохранить Func<Card>
:
private Dictionary<int, Func<Card>> cardFactories =
{
{ 13, () => new King() },
// etc
}
public Card GetCard(int cardNumber)
{
var factory = cardFactories[cardNumber];
return factory();
}
В случае cards я бы, вероятно, сделал их неизменяемыми для начала и просто заполнил словарь самими картами, но это другое дело 🙂
Комментарии:
1. @Caspar: Да, вы, безусловно, могли бы использовать константы вместо этого. На самом деле суть вопроса не в этом, хотя именно это я и пытался решить…
2. это отличное решение, спасибо! a. Что насчет проблемы с Ace? вы бы повторили функцию Ace дважды для 1 и 14? B. Что, если каждой карточке нужно получить параметр, скажем,
Suit
(цветной) объект? Не могли бы вы создать делегатCard delg(Suit)
и затем использовать(suit) => new King(suit)
в словаре?3. @yellowblood: Это будет зависеть от реальной ситуации, которая, предположительно, не для cards. Зачем вам иметь два идентификатора для одного и того же значения? Можете ли вы сначала канонизировать, например?
4. @Caspar: Я думаю, что магические числа имели бы смысл в этой вещи; на самом деле, все cardFactories являются константой. (конечно, я бы это сделал
static reaonly
)