У меня была эта проблема много раз раньше, и у меня никогда не было решения, которое меня устраивало.
Допустим, у меня есть базовый класс Transaction и два производных класса AdjustmentTransaction и IssueTransaction.
У меня есть список транзакций в пользовательском интерфейсе, и каждая транзакция имеет конкретный тип AdjustmentTransaction или IssueTransaction.
Когда я выбираю транзакцию и нажимаю кнопку «Изменить», мне нужно решить, отображать ли AdjustmentTransactionEditorForm или IssueTransactionEditorForm.
Вопрос в том, как мне сделать это объектно-ориентированным способом без использования оператора switch для типа выбранной транзакции? Оператор switch работает, но кажется неуклюжим. Я чувствую, что должен иметь возможность каким-то образом использовать иерархию параллельного наследования между транзакциями и редакторами транзакций.
У меня могло быть свойство EditorForm в моей транзакции, но это ужасное смешение арахисового масла пользовательского интерфейса с моим шоколадом Model.
Заранее спасибо.


В какой-то момент вам нужно сопоставить вашу "EditorForm" с транзакцией. У вас есть несколько вариантов:
В C# я бы реализовал преобразователь Type -> Form следующим образом:
Dictionary <Type,Type> typeMapper = new Dictionary<Type,Type>();
typeMapper.Add(typeof(AdjustTransaction), typeof(AdjustTransactionForm));
// etc, in this example, I'm populating it by hand,
// in real life, I'd use a key/value pair mapping config file,
// and populate it at runtime.
затем при нажатии кнопки редактирования:
Type formToGet;
if (typeMapper.TryGetValue(CurrentTransaction.GetType(), out formToGet))
{
Form newForm = (Form)Activator.CreateInstance(formToGet);
}
Я что-то упускаю в вопросе? Я просто спрашиваю, потому что очевидным ОО-ответом было бы: Превращение
Просто выполните Transaction.editWindow () (или как вы хотите его назвать) и перезапишите метод в AdjustmentTransaction и IssueTrasaction с требуемой функциональностью. Вызов element.editWindow () затем открывает для вас нужный диалог.
Он хочет отделить пользовательский интерфейс от класса состояния транзакции.
Вы, вероятно, не захотите привязывать его к дереву наследования - это вас довольно хорошо связывает позже, когда вы получите небольшое изменение требований.
Связь должна быть указана где-нибудь во внешнем файле. Что-то, что описывает отношения:
Editing AdujustmentTransaction = AdjustmentTransactionEditorForm
Editing IssueTransaction = IssueTransactionEditorForm
После небольшого разбора и лучшего языка, чем я использовал здесь, этот файл может стать очень обобщенным и многоразовым - вы можете повторно использовать формы для разных объектов, если это необходимо, или изменить форму, используемую для редактирования объекта, без особых усилий. усилие.
(Возможно, вы захотите, чтобы пользователи с именем «Джо» использовали вместо этого «JoeIssueTransactionEditorForm», это может быть довольно легко преобразовано в ваш «язык»)
По сути, это внедрение зависимостей - вы, вероятно, можете использовать Spring для решения проблемы в более общих чертах.
Мне очень нравится ваш комментарий о том, что я не привязываю его к inh. tree, что имеет большой смысл в описываемом вами сценарии.
В прошлом у меня были большие проблемы с подобными вещами - использование отражения для «выяснения» того, какие вещи идут вместе, но это делает материал действительно негибким. Подходит и Ruby «сделать обычное простым, а необычное - возможным» - привязка имен классов и тому подобное нарушает вторую часть этого правила.
Альтернативой подходу Dictionary / Config File будет
1) определить интерфейс для каждого редактора транзакций.
2) В сборке EXE или пользовательского интерфейса каждая из форм должна регистрироваться в сборке, которая создает отдельную транзакцию.
3) Класс, контролирующий регистрацию, должен быть одноэлементным, чтобы у вас не было нескольких экземпляров формы, плавающих вокруг.
3) Когда создается отдельная транзакция, она извлекает правильную переменную формы из объекта регистрации и назначает ей внутреннюю переменную.
4) Когда вызывается метод Edit, он просто использует метод Show внутреннего метода для запуска цепочки вызовов, которые приведут к отображению этого редактора транзакций.
Это устраняет необходимость в файлах конфигурации и словарях. Он продолжает отделять пользовательский интерфейс от объекта. Кроме того, вам не нужен оператор переключения
Обратной стороной является необходимость писать интерфейс для каждой формы в дополнение к самой форме.
Если у вас много разных типов редакторов (десятки), то в этом случае я рекомендую вам использовать Command Pattern.
У вас есть основная команда, содержащая словарь, рекомендованный Джонатаном. Эти команды по очереди будут использовать этот словарь для выполнения одной из ряда других команд, которые вызывают правильную форму с правильным объектом. Формы по-прежнему отделены от самого объекта. Формы находятся в сборке Command. Кроме того, вам не нужно обновлять EXE, чтобы добавить еще один редактор, только сборку Command. Наконец, поместив что-то в Command, вы можете намного проще реализовать Undo / Redo. (Реализуйте Unexecute, а также Execute)
Я думаю, это отлично подойдет мне, а доп. config, предложенный вами и Биллом К., действительно сделает это приятным. Спасибо!