Моите колеги и аз съставяме малка рамка за отчитане за онлайн магазин. Ние изградихме библиотека, следвайки модела на хранилище, използвайки „отчети“ като хранилища и много лек сервизен слой за взаимодействие с тези отчети. Нашият код работи чудесно и е доста лесен за използване. Има обаче едно нещо, което ме притеснява лично: на ниво фабрика за услуги трябва да декларираме нашия тип връщане два пъти (не се извежда). Ето основата на нашия проект:
Интерфейсът за отчет
Това е, което се използва като наши „Хранилища“. Те приемат обекти за достъп до данни, като класове обвивки към SQL/Oracle връзка или API на нашия Storefront.
internal interface IReport<T>
{
T GetReportData(dynamic options);
}
Фабриката за хранилища
Това предоставя лесен начин за генериране на тези отчети, като знаете типа.
internal interface IReportFactory
{
TR GenerateNewReport<T, TR>() where TR : IReport<T>;
}
internal class ReportFactory : IReportFactory
{
public ReportFactory()
{
// some initialization stuff
}
public TR GenerateNewReport<T, TR>() where TR : IReport<T>
{
try
{
return (TR)Activator.CreateInstance(typeof(TR));
}
catch(Exception ex)
{
// Logging
}
}
}
Примерен отчет (хранилище)
Ето как би изглеждал един отчет. Забележете, че той връща DataTable и е деклариран с това в общия интерфейс (това ще влезе за игра скоро).
internal class ItemReport : IReport<DataTable>
{
public DataTable GetReportData(dynamic options)
{
return new DataTable();
}
}
Услугата за докладване
Лека услуга, която приема отчет (хранилище) и работи с него. Леко, но позволява лесно тестване на единици и други подобни, и ако някога искаме да добавим допълнителна обработка, след като отчетите бъдат извлечени, ще бъде лесно да го направим.
public interface IReportService<T>
{
T GetReportData(dynamic options);
}
public class ReportService<T> : IReportService<T>
{
private readonly IReport<T> _report;
public ReportService(IReport<T> report)
{
_report = report;
}
public T GetReportData(dynamic options)
{
return _report.GetReportData(options);
}
}
Фабриката за услуги
Имаме настройка на фабриката за услуги като абстрактен клас (тъй като всички фабрики за услуги ще трябва да създадат фабриката за отчети), принуждавайки конструктор по подразбиране:
public abstract class ReportServiceFactory
{
protected IReportFactory ReportFactory;
protected ReportServiceFactory(connection strings and other stuff)
{
ReportFactory = new ReportFactory(connection strings and other stuff);
}
}
След това можем да създадем отделни фабрики за услуги въз основа на функция. Например, имаме фабрика за обслужване на „стандартен отчет“, както и фабрики за обслужване, специфични за клиента. Реализацията тук е мястото, където е въпросът ми.
public class SpecificUserServiceFactory : ReportServiceFactory
{
public SpecificUserServiceFactory(connection strings and other stuff) : base(connection strings and other stuff){}
public IReport<DataTable> GetItemReport()
{
return new ReportService<DataTable>(ReportFactory.GenerateNewReport<DataTable, ItemReport>());
}
}
Защо трябва да бъда толкова многословен, когато създавам фабриката за услуги? Декларирам връщания тип два пъти. Защо не мога да направя нещо подобно:
return new ReportService(ReportFactory.GenerateNewReport<ItemReport>());
Забележете, че не декларирам DataTable в това; Мисля, че трябва да се направи извод от факта, че ItemReport е IReport. Всякакви предложения как да го накарате да работи по този начин ще бъдат високо оценени.
Съжалявам за толкова дългото обяснение да задам такъв прост въпрос, но реших, че целият този код за поддръжка ще помогне да се намери решение. Благодаря отново!
IReportFactory
изглежда странно. - person leppie   schedule 13.11.2011