Выбор правильного вида для типа объекта

У меня была эта проблема много раз раньше, и у меня никогда не было решения, которое меня устраивало.

Допустим, у меня есть базовый класс Transaction и два производных класса AdjustmentTransaction и IssueTransaction.

У меня есть список транзакций в пользовательском интерфейсе, и каждая транзакция имеет конкретный тип AdjustmentTransaction или IssueTransaction.

Когда я выбираю транзакцию и нажимаю кнопку «Изменить», мне нужно решить, отображать ли AdjustmentTransactionEditorForm или IssueTransactionEditorForm.

Вопрос в том, как мне сделать это объектно-ориентированным способом без использования оператора switch для типа выбранной транзакции? Оператор switch работает, но кажется неуклюжим. Я чувствую, что должен иметь возможность каким-то образом использовать иерархию параллельного наследования между транзакциями и редакторами транзакций.

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

Заранее спасибо.

В PHP
В PHP
В большой кодовой базе с множеством различных компонентов классы, функции и константы могут иметь одинаковые имена. Это может привести к путанице и...
Принцип подстановки Лискова
Принцип подстановки Лискова
Принцип подстановки Лискова (LSP) - это принцип объектно-ориентированного программирования, который гласит, что объекты суперкласса должны иметь...
3
0
313
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

В какой-то момент вам нужно сопоставить вашу "EditorForm" с транзакцией. У вас есть несколько вариантов:

  • Оператор переключения ... как и вы, я думаю, что это воняет и плохо масштабируется.
  • Абстрактное свойство EditorForm в базовом классе Transaction лучше масштабируется, но плохо разделяет проблемы.
  • Тип -> преобразователь форм в вашем интерфейсе. Это довольно хорошо масштабируется и сохраняет хорошее разделение.

В 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);
}

Я думаю, это отлично подойдет мне, а доп. config, предложенный вами и Биллом К., действительно сделает это приятным. Спасибо!

James Thigpen 20.11.2008 22:54

Я что-то упускаю в вопросе? Я просто спрашиваю, потому что очевидным ОО-ответом было бы: Превращение

Просто выполните Transaction.editWindow () (или как вы хотите его назвать) и перезапишите метод в AdjustmentTransaction и IssueTrasaction с требуемой функциональностью. Вызов element.editWindow () затем открывает для вас нужный диалог.

Он хочет отделить пользовательский интерфейс от класса состояния транзакции.

FlySwat 20.11.2008 22:35

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

Связь должна быть указана где-нибудь во внешнем файле. Что-то, что описывает отношения:

Editing AdujustmentTransaction = AdjustmentTransactionEditorForm
Editing IssueTransaction = IssueTransactionEditorForm

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

(Возможно, вы захотите, чтобы пользователи с именем «Джо» использовали вместо этого «JoeIssueTransactionEditorForm», это может быть довольно легко преобразовано в ваш «язык»)

По сути, это внедрение зависимостей - вы, вероятно, можете использовать Spring для решения проблемы в более общих чертах.

Мне очень нравится ваш комментарий о том, что я не привязываю его к inh. tree, что имеет большой смысл в описываемом вами сценарии.

James Thigpen 20.11.2008 22:51

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

Bill K 20.11.2008 23:12

Альтернативой подходу Dictionary / Config File будет

1) определить интерфейс для каждого редактора транзакций.

2) В сборке EXE или пользовательского интерфейса каждая из форм должна регистрироваться в сборке, которая создает отдельную транзакцию.

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

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

4) Когда вызывается метод Edit, он просто использует метод Show внутреннего метода для запуска цепочки вызовов, которые приведут к отображению этого редактора транзакций.

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

Обратной стороной является необходимость писать интерфейс для каждой формы в дополнение к самой форме.

Если у вас много разных типов редакторов (десятки), то в этом случае я рекомендую вам использовать Command Pattern.

У вас есть основная команда, содержащая словарь, рекомендованный Джонатаном. Эти команды по очереди будут использовать этот словарь для выполнения одной из ряда других команд, которые вызывают правильную форму с правильным объектом. Формы по-прежнему отделены от самого объекта. Формы находятся в сборке Command. Кроме того, вам не нужно обновлять EXE, чтобы добавить еще один редактор, только сборку Command. Наконец, поместив что-то в Command, вы можете намного проще реализовать Undo / Redo. (Реализуйте Unexecute, а также Execute)

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