[.NET, Проектирование и рефакторинг, C#] Организация кода для работы с ftp средствами Fluent interface
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Мне очень нравится паттерн Fluent interface, за то, что он делает сложный и длинный код максимально читабельным. В статье хочу показать пример реализации этого паттерна при работе с ftp. Задача, что требуется сделать:
- Получать имена файлов в определенном каталоге;
- Скачивать файлы в поток/файл;
- Загружать файлы из потока/файла;
- Удалять файлы;
- Настройки данных авторизации(ip, port, login, name).
Необходимо получить код, который будет лаконичным, читабельным и при помощи IntelliSense обеспечить легкое и удобное потребление кода. Пример:
_ftpService
.OnConfigurate(pathSource)
.Download(file)
.ToFile(localFile);
и/или
_ftpService
.OnConfigurate(pathSource)
.Download(file)
.ToSteam(memStream);
и/или
_ftpService
.OnConfigurate(pathDestination)
.Upload(fileNameDestination)
.FromStream(memStream);
Для начала определяем интерфейсы по принципам SRP(единственной ответственности):
/// <summary>
/// Интерфейс настройки фтп сервиса
/// </summary>
public interface ITransferFileService
{
string Url { get; }
ITransferServiceAction OnConfigurate(string path);
}
/// <summary>
/// Интерфейс записи данных с фтп
/// </summary>
public interface ITransferServiceWrite
{
void FromFile(string filePath);
void FromStream(Stream stream);
}
/// <summary>
/// Интерфейс чтения данных с фтп
/// </summary>
public interface ITransferServiceRead
{
void ToFile(string filePath);
void ToStream(Stream stream);
}
/// <summaty>
/// Интерфейс доступных действий с фтп
/// </summary>
public interface ITransferServiceAction
{
ITransferServiceRead Download(string fileName);
ITransferServiceWrite Upload(string fileName);
void Delete(string fileName);
IEnumerable<string> GetNameFiles();
}
Теперь добавим класс с реализацией описанных выше интерфейсов.
public class FtpService :
ITransferFileService,
ITransferServiceAction,
ITransferServiceRead,
ITransferServiceWrite
{
private readonly Logger _logger;
private string _fileName;
public FtpService(string url, ILogger logger)
{
_logger = logger;
Url = url;
}
public string Url { get; }
/// <summary>
/// Порт(по умолчанию 21)
/// </summary>
public int Port { get; private set; } = 21;
/// <summary>
/// Пароль для подключения к фтп
/// </summary>
private string Password { get; set; }
/// <summary>
/// Логин для подключения в фтп
/// </summary>
private string Login { get; set;}
/// <summary>
/// Путь
/// </summary>
private string Path { get; set; }
public void SetCredential(string login, string password)
{
Login = login;
Password = password;
}
public ITransferServiceAction OnConfigurate(string path)
{
Path = path;
return this;
}
public ITransferServiceRead Download(string fileName)
{
_fileName = fileName;
return this;
}
public ITransferServiceWrite Upload(string fileName)
{
_fileName = fileName;
return this;
}
public void Delete(string fileName)
{
try
{
var request = (FtpWebRequest) WebRequest.Create($"{Url}/{Path}/{_fileName}");
request.Credentials = new NetworkCredential(Login,Password);
request.Method = WebRequestMethods.Ftp.DeleteFile;
request.GetResponse();
}
catch (Exception ex)
{
_logger.Error($"Ошибка удаления файла с ftp сервера - {ex.Message} ");
}
}
public void FromFile(string filePath)
{
if(string.IsNullOrEmpty(filePath))
return;
try
{
using (var client = new WebClient())
{
client.Credentials = new NetworkCredential(Login, Password);
client.UploadFile($"{Url}/{Path}/{_fileName}", WebRequestMethods.Ftp.UploadFile, filePath);
}
}
catch (Exception ex)
{
_logger.Error(ex);
}
}
public void FromStream(Stream stream)
{
if(stream == null)
return;
try
{
var request =
(FtpWebRequest)WebRequest.Create($"{Url}/{Path}/{_fileName}");
request.Credentials = new NetworkCredential(Login, Password);
request.UsePassive = true;
request.UsePassive = true;
request.KeepAlive = true;
request.Method = WebRequestMethods.Ftp.UploadFile;
using (var ftpStream = request.GetRequestStream())
{
stream.CopyTo(ftpStream);
}
request.GetResponse();
}
catch (Exception ex)
{
_logger.Error(ex);
}
}
private byte[] DownloadFile()
{
var ftpRequest = (FtpWebRequest)WebRequest.Create($"{Url}/{Path}/{_fileName}");
ftpRequest.Credentials = new NetworkCredential(Login, Password);
ftpRequest.UseBinary = true;
ftpRequest.UsePassive = true;
ftpRequest.KeepAlive = true;
ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile;
var ftpResponse = (FtpWebResponse)ftpRequest.GetResponse();
using (var ms = new MemoryStream())
{
ftpResponse.GetResponseStream().CopyTo(ms);
return ms.ToArray();
}
}
public void ToFile(string filePath)
{
try
{
var downloadedFile = DownloadFile();
File.WriteAllBytes(filePath, downloadedFile);
}
catch (WebException ex)
{
_logger.Error(Url);
_logger.Error(ex);
}
}
public void ToStream(Stream stream)
{
if (stream == null)
return;
try
{
using (var writer = new BinaryWriter(stream))
{
var downloadedFile = DownloadFile();
writer.Write(downloadedFile);
}
}
catch (WebException ex)
{
_logger.Error(Url);
_logger.Error(ex);
}
}
public IEnumerable<string> GetNameFiles()
{
var request = (FtpWebRequest)WebRequest.Create($"{Url}/{Path}");
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential(Login, Password);
var files = new List<string>();
using (var response = (FtpWebResponse)request.GetResponse())
{
var responseStream = response.GetResponseStream();
using (var reader = new StreamReader(responseStream))
{
var line = reader.ReadLine();
while (!string.IsNullOrEmpty(line))
{
try
{
files.Add(line);
line = reader.ReadLine();
}
catch (Exception ex)
{
_logger.Error(ex);
}
}
}
}
return files;
}
public void SetPort(int port)
{
Port = port;
}
}
Что можно сделать лучше - добавить асинхронный вариант цепочки. Ничего сложно в этом нет, достаточно добавить в интерфейсы методы с возвращаемым типом Task<T>. Для гибкой настройки сервиса добавим паттерн строитель:
/// <summary>
/// Построитель Ftp сервиса
/// </summary>
public class BuilderFtpService
{
private FtpService ftpService { get; }
/// <summary>
/// Конструктор с ip адресом
/// </summary>
/// <param name="url">Адрес фтп</param>
/// <param name="logger">Логгер</param>
public BuilderFtpService(string url, ILogger logger)
{
ftpService = new FtpService(url,logger);
}
/// <summary>
/// Построить экземпляр сервиса
/// </summary>
public ITransferFileService Build() => ftpService;
/// <summary>
/// Указать авторизационные данные
/// </summary>
/// <param name="login">Логин</param>
/// <param name="password">Пароль</param>
/// <returns>Построитель фтп сервиса</returns>
public BuilderFtpService WithCredential(string login, string password)
{
ftpService.SetCredential(login, password);
return this;
}
/// <summary>
/// Указать авторизационные данные
/// </summary>
/// <param name="login">Логин</param>
/// <param name="password">Пароль</param>
/// <returns>Построитель фтп сервиса</returns>
public BuilderFtpService WithPort(int port)
{
ftpService.SetPort(port);
return this;
}
}
Таким образом у нас получилось реализовать функционал согласно поставленной задачи. Пример настройки и использования:
new BuilderFtpService(ipAddress, logger)
.WithCredential(login, password)
.Build()
.OnConfigurate(pathSource)
.Download(file)
.ToFile(localFile);
Код выше приведен в качестве примера, в реальном приложении рекомендуется все зависимости реализовывать через IoC контейнеры. Такая реализация функционала имеет лаконичный вид, высокую читабельность и повышает интуитивность использования кода. Как и в любом подходе, паттерн fluent interface имеет минусы - проблема отладки. В длинных цепочках вызовов трудно поставить точку остановки.
===========
Источник:
habr.com
===========
Похожие новости:
- [C#, Unity, Agile, Развитие стартапа, AR и VR] Как мы делаем образовательную VR — платформу для университета МИФИ
- [Алгоритмы, Машинное обучение, Голосовые интерфейсы] Голосовой ИИ: технологии под капотом цифрового агента
- [Программирование, .NET, C#] Даты, время и часовые пояса: улучшения в .NET 6
- [Анализ и проектирование систем, Проектирование и рефакторинг, Amazon Web Services] Как мы в Plesk в 2 раза снизили стоимость инфраструктуры AWS для нагруженного сервиса
- [Машинное обучение, Искусственный интеллект] Facebook поделился двумя гигантскими датасетами Conversational AI
- [Программирование, Анализ и проектирование систем, Совершенный код, Проектирование и рефакторинг, ООП] OCP против YAGNI (перевод)
- [.NET, Разработка под Windows] WPF, UWP, WinUI, MAUI, Windows App SDK
- [.NET, C#, WebAssembly] Хостим WASM-приложения на github pages в два клика (перевод)
- [Open source, .NET, C#, Математика, F#] AngouriMath 1.3 update
- [Программирование, .NET] Creating a NuGet package for a library with platform-specific API
Теги для поиска: #_.net, #_proektirovanie_i_refaktoring (Проектирование и рефакторинг), #_c#, #_c#, #_.net, #_patterns, #_fluentinterface, #_.net, #_proektirovanie_i_refaktoring (
Проектирование и рефакторинг
), #_c#
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 07:13
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Мне очень нравится паттерн Fluent interface, за то, что он делает сложный и длинный код максимально читабельным. В статье хочу показать пример реализации этого паттерна при работе с ftp. Задача, что требуется сделать:
_ftpService
.OnConfigurate(pathSource) .Download(file) .ToFile(localFile); _ftpService
.OnConfigurate(pathSource) .Download(file) .ToSteam(memStream); _ftpService
.OnConfigurate(pathDestination) .Upload(fileNameDestination) .FromStream(memStream); /// <summary>
/// Интерфейс настройки фтп сервиса /// </summary> public interface ITransferFileService { string Url { get; } ITransferServiceAction OnConfigurate(string path); } /// <summary> /// Интерфейс записи данных с фтп /// </summary> public interface ITransferServiceWrite { void FromFile(string filePath); void FromStream(Stream stream); } /// <summary> /// Интерфейс чтения данных с фтп /// </summary> public interface ITransferServiceRead { void ToFile(string filePath); void ToStream(Stream stream); } /// <summaty> /// Интерфейс доступных действий с фтп /// </summary> public interface ITransferServiceAction { ITransferServiceRead Download(string fileName); ITransferServiceWrite Upload(string fileName); void Delete(string fileName); IEnumerable<string> GetNameFiles(); } public class FtpService :
ITransferFileService, ITransferServiceAction, ITransferServiceRead, ITransferServiceWrite { private readonly Logger _logger; private string _fileName; public FtpService(string url, ILogger logger) { _logger = logger; Url = url; } public string Url { get; } /// <summary> /// Порт(по умолчанию 21) /// </summary> public int Port { get; private set; } = 21; /// <summary> /// Пароль для подключения к фтп /// </summary> private string Password { get; set; } /// <summary> /// Логин для подключения в фтп /// </summary> private string Login { get; set;} /// <summary> /// Путь /// </summary> private string Path { get; set; } public void SetCredential(string login, string password) { Login = login; Password = password; } public ITransferServiceAction OnConfigurate(string path) { Path = path; return this; } public ITransferServiceRead Download(string fileName) { _fileName = fileName; return this; } public ITransferServiceWrite Upload(string fileName) { _fileName = fileName; return this; } public void Delete(string fileName) { try { var request = (FtpWebRequest) WebRequest.Create($"{Url}/{Path}/{_fileName}"); request.Credentials = new NetworkCredential(Login,Password); request.Method = WebRequestMethods.Ftp.DeleteFile; request.GetResponse(); } catch (Exception ex) { _logger.Error($"Ошибка удаления файла с ftp сервера - {ex.Message} "); } } public void FromFile(string filePath) { if(string.IsNullOrEmpty(filePath)) return; try { using (var client = new WebClient()) { client.Credentials = new NetworkCredential(Login, Password); client.UploadFile($"{Url}/{Path}/{_fileName}", WebRequestMethods.Ftp.UploadFile, filePath); } } catch (Exception ex) { _logger.Error(ex); } } public void FromStream(Stream stream) { if(stream == null) return; try { var request = (FtpWebRequest)WebRequest.Create($"{Url}/{Path}/{_fileName}"); request.Credentials = new NetworkCredential(Login, Password); request.UsePassive = true; request.UsePassive = true; request.KeepAlive = true; request.Method = WebRequestMethods.Ftp.UploadFile; using (var ftpStream = request.GetRequestStream()) { stream.CopyTo(ftpStream); } request.GetResponse(); } catch (Exception ex) { _logger.Error(ex); } } private byte[] DownloadFile() { var ftpRequest = (FtpWebRequest)WebRequest.Create($"{Url}/{Path}/{_fileName}"); ftpRequest.Credentials = new NetworkCredential(Login, Password); ftpRequest.UseBinary = true; ftpRequest.UsePassive = true; ftpRequest.KeepAlive = true; ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile; var ftpResponse = (FtpWebResponse)ftpRequest.GetResponse(); using (var ms = new MemoryStream()) { ftpResponse.GetResponseStream().CopyTo(ms); return ms.ToArray(); } } public void ToFile(string filePath) { try { var downloadedFile = DownloadFile(); File.WriteAllBytes(filePath, downloadedFile); } catch (WebException ex) { _logger.Error(Url); _logger.Error(ex); } } public void ToStream(Stream stream) { if (stream == null) return; try { using (var writer = new BinaryWriter(stream)) { var downloadedFile = DownloadFile(); writer.Write(downloadedFile); } } catch (WebException ex) { _logger.Error(Url); _logger.Error(ex); } } public IEnumerable<string> GetNameFiles() { var request = (FtpWebRequest)WebRequest.Create($"{Url}/{Path}"); request.Method = WebRequestMethods.Ftp.ListDirectory; request.Credentials = new NetworkCredential(Login, Password); var files = new List<string>(); using (var response = (FtpWebResponse)request.GetResponse()) { var responseStream = response.GetResponseStream(); using (var reader = new StreamReader(responseStream)) { var line = reader.ReadLine(); while (!string.IsNullOrEmpty(line)) { try { files.Add(line); line = reader.ReadLine(); } catch (Exception ex) { _logger.Error(ex); } } } } return files; } public void SetPort(int port) { Port = port; } } /// <summary>
/// Построитель Ftp сервиса /// </summary> public class BuilderFtpService { private FtpService ftpService { get; } /// <summary> /// Конструктор с ip адресом /// </summary> /// <param name="url">Адрес фтп</param> /// <param name="logger">Логгер</param> public BuilderFtpService(string url, ILogger logger) { ftpService = new FtpService(url,logger); } /// <summary> /// Построить экземпляр сервиса /// </summary> public ITransferFileService Build() => ftpService; /// <summary> /// Указать авторизационные данные /// </summary> /// <param name="login">Логин</param> /// <param name="password">Пароль</param> /// <returns>Построитель фтп сервиса</returns> public BuilderFtpService WithCredential(string login, string password) { ftpService.SetCredential(login, password); return this; } /// <summary> /// Указать авторизационные данные /// </summary> /// <param name="login">Логин</param> /// <param name="password">Пароль</param> /// <returns>Построитель фтп сервиса</returns> public BuilderFtpService WithPort(int port) { ftpService.SetPort(port); return this; } } new BuilderFtpService(ipAddress, logger)
.WithCredential(login, password) .Build() .OnConfigurate(pathSource) .Download(file) .ToFile(localFile); =========== Источник: habr.com =========== Похожие новости:
Проектирование и рефакторинг ), #_c# |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 07:13
Часовой пояс: UTC + 5