#c# #.net #refactoring #solid-principles
#c# #.net #рефакторинг #solid-принципы
Вопрос:
Описание
Приложение WinForms имеет функцию для экспорта объектов следующего типа в различные форматы:
class Item
{
public int id { get; set; }
public string description { get; set; }
}
При нажатии кнопки в окне отображается SaveFileDialog
, и в настоящее время он предоставляет возможность сохранить данные в формате .txt, .csv или .xlsx. Поскольку иногда существуют сотни или тысячи объектов, и пользовательский интерфейс не должен зависать, для выполнения этой операции используется Task
. Эта реализация работает, но может быть улучшена.
Код
public partial class ExportWindow : Form
{
// objects to be exported
List<Item> items;
// event handler for the "Export" button click
private async void exportButton_click(object sender, System.EventArgs e)
{
SaveFileDialog exportDialog = new SaveFileDialog();
exportDialog.Filter = "Text File (*.txt)|*.txt|Comma-separated values file (*.csv)|*.csv|Excel spreadsheet (*.xlsx)|*.xlsx";
exportDialog.CheckPathExists = true;
DialogResult result = exportDialog.ShowDialog();
if (result == DialogResult.OK)
{
var ext = System.IO.Path.GetExtension(saveExportFileDlg.FileName);
try
{
// update status bar
// (it is a custom control)
statusBar.text("Exporting");
// now export it
await Task.Run(() =>
{
switch (ext.ToLower())
{
case ".txt":
saveAsTxt(exportDialog.FileName);
break;
case ".csv":
saveAsCsv(exportDialog.FileName);
break;
case ".xlsx":
saveAsExcel(exportDialog.FileName);
break;
default:
// shouldn't happen
throw new Exception("Specified export format not supported.");
}
});
}
catch (System.IO.IOException ex)
{
statusBar.text("Export failed");
logger.logError("Export failed" ex.Message "n" ex.StackTrace);
return;
}
}
}
private delegate void updateProgressDelegate(int percentage);
public void updateProgress(int percentage)
{
if (statusBar.InvokeRequired)
{
var d = updateProgressDelegate(updateProgress);
statusBar.Invoke(d, percentage);
}
else
{
_updateProgress(percentage);
}
}
private void saveAsTxt(string filename)
{
IProgress<int> progress = new Progress<int>(updateProgress);
// save the text file, while reporting progress....
}
private void saveAsCsv(string filename)
{
IProgress<int> progress = new Progress<int>(updateProgress);
using (StreamWriter writer = StreamWriter(filename))
{
// write the headers and the data, while reporting progres...
}
}
private void saveAsExcel(string filename)
{
IProgress<int> progress = Progress<int>(updateProgress);
// EPPlus magic to write the data, while reporting progress...
}
}
Вопросы
Как это можно переработать, чтобы сделать его более расширяемым? То есть, если бы я хотел добавить поддержку для большего количества типов файлов, упростить и ускорить изменение. Инструкция switch может стать очень длинной. По сути, как соблюсти принцип Open / Closed?
Ответ №1:
Создание класса для каждого из расширений могло бы быть способом. Вместо простого перебора по какому-либо списку или с помощью отражения, поэтому, если вы захотите добавить поддержку нового расширения, вам придется просто создать новый класс, а не трогать ExportWindow