Пример рефакторинга Delphi с использованием элементов управления с учетом данных и datamodules с прямым доступом к таблицам БД
Я пытаюсь определить лучший способ рефакторинга проекта, над которыми я работаю.
из-за отсутствия хорошего дизайна почти весь проект состоит из:
1) формы, содержащие бизнес-логику
2) огромные datamodules (1 на форму + некоторые дополнительные)
3) некоторые единицы, содержащие общий код (библиотеки)
нет ООП (за исключением некоторых небольших областей), повторное использование кода на минимальном уровне.
одна проблема также в том, что элементы управления dataaware используются, поэтому было очень просто отбросить много наборов данных+источников данных на datamodules и напрямую связать их с БД в высокой степени.
В идеале я хотел бы извлечь классы, такие как TCustomer, TEmployee, чтобы получить преимущество инкапсуляции ОС и сделать возможным создание нового пользовательского интерфейса в будущем без дублирования всего кода.
в любом случае мой вопрос: как я могу продолжать иметь дело с элементами управления dataaware? Должен ли я реализовать функцию, которая возвращает набор данных, и я связываю компонент dataawarecomponent.источник данных для результата функции?
function TCustomer.LoadByID(aCustomerID: integer): TDataset
?
2 ответов
вы привязаны к архитектуре, вокруг которой было разработано ваше приложение. Не пытайтесь бороться с этим. Пусть элементы управления с учетом данных делают то, что они хорошо умеют, синхронизацию данных. Если ваши элементы управления уже привязаны к своим источникам данных с помощью dfm, не должно быть проблем.
что вам нужно для рефакторинга-это любые обработчики событий, которые вы прикрепили к своим элементам управления. Я предлагаю вам взглянуть на Контроль Контроллер узор. Я нашел пример реализации:
- ASP.NET
- богатые клиенты .NET(winforms или wpf)
- Smalltalk(это одно пионерское картина и предшествует ярлыку " контролируя регулятора)
хотя есть несколько примеров архитектурных шаблонов пользовательского интерфейса в Delphi, которые ориентированы на настольные приложения, как правило, касаются Пассивный Вид а не Контролер-Надзиратель. Так вот мой взгляд на это.
вы захотите начать с определения хотя бы одного интерфейса для каждой формы в вашем приложении. Я говорю по крайней мере один потому что некоторые формы сложны и могут быть разбиты на несколько интерфейсов.
IProductView = interface
end;
тогда пусть ваша форма реализует его.
TProductForm = class(TForm, IProductView)
...
end;
Далее вам понадобится ведущий/контроллер. Это будет обрабатывать все, кроме данных синхронизация.
TProductPresenter = class
private
FView: IProductView;
public
constructor Create(AView:IProductView);
end;
создайте частное поле в классе формы и создайте/освободите ведущего, когда форма будет создана/освобождена. Используете ли вы конструктор/деструктор формы или события onCreate/onDestroy, не имеет большого значения.
TProductForm = class(TForm, IProductView)
private
FPresenter: TProductPresenter;
public
constructor Create;
...
end;
implementation
TProductForm.Create
begin
FPresenter := TProductPresenter.Create(self);
end;
теперь, когда вам нужна форма или один из ее элементов управления, чтобы ответить на событие делегировать ответственность ведущему. Предположим, вам нужно проверить, что имя продукта использует proper капитализация.
TProductForm.NameDBEditChange(Sender: TObject);
begin
FPresenter.ValidateName;
end;
вместо передачи элемента управления или его свойства text в качестве аргумента вы предоставляете данные в качестве свойства интерфейса...
IProductView = interface
function GetName:string;
procedure SetName(Value: string);
property Name: string read GetName write SetName;
...и реализовать GetName
и SetName
на форме.
TProductForm.GetName: string;
begin
Result := NameDBEdit.Text;
end;
TProductForm.SetName(Value: string);
begin
NameDBEdit.Text := Value;
end;
важно предоставить данные в максимально простой форме. Вы не хотите, чтобы ведущий зависел от имени продукта, хранящегося в TDBEdit. Ведущий должен видеть только то, что вы явно позволяете ему видеть через взаимодействие. Основное преимущество этого заключается в том, что вы можете изменять форму столько, сколько хотите(или полностью заменить ее), и пока она придерживается интерфейса, никаких изменений не нужно будет вносить в презентер.
теперь, когда вся ваша бизнес-логика была перенесена на вашего докладчика, она будет напоминать класс Бога. Следующим шагом будет рефакторинг этой логики в соответствующие классы, разбитые на ответственность. Когда вы достигнете этой точки, вы будете в гораздо лучшем положении, чтобы попытаться архитектурный редизайн (если вы все еще рассматриваете его).
"Вау! Похоже, много работы!- можно сказать и так. Вы были бы правы (но тогда вы знали, что будет много работы, прежде чем вы начали). Не обязательно делать все сразу. Ни один из этих шагов не изменяет поведение логики только там, где это происходит.
преимущества
- UI теперь легко изменить
- бизнес-логику можно более легко проверить в изоляция
- может быть реализовано постепенно
недостатки
- это больше работы сначала, хотя это в конечном итоге компенсируется более ремонтопригодным кодом позже.
- не подходит для всех приложений. Для небольших проектов дополнительная инфраструктура может не стоить усилий.
другие ссылки
- реализация Delphi представления модели Presenter Рамки (пример-пользовательский элемент управления UI)
- ТДД выставление графического интерфейса
- простой старт с MVP в Delphi для Win32, Часть 1, Часть2
Если в коде нет хорошего дизайна и реального ООП, то, учитывая его сложность, вы должны сначала начать с создания дизайна, описывающего его текущую функциональность. Да, это означает, что сначала вы будете заняты написанием большого количества документации. Но это позволяет разделить весь проект на логические / функциональные части, на которых вы можете сосредоточиться после завершения этой документации. Тогда вы могли бы преобразовать каждую часть отдельно, или, возможно, переписать те части даже.
проекты этот комплекс не всегда практичен для рефакторинга. Вы должны вернуться к исходному дизайну (таким образом, создать его, поскольку у вас его нет), а затем посмотреть на свой код и рассмотреть, что быстрее: рефакторинг или перезапись...