Композитный шаблон проектирования: как передать результаты из одного компонента в другой?
У меня есть следующий код:
interface IService
{
void Execute();
}
class ServiceA : IService
{
public void Execute() { ... }
}
class ServiceB : IService
{
public void Execute() { ... }
}
class ServiceComposite : IService
{
List<IService> _services = new List<IService>();
public ServiceComposite()
{
_services.Add(new ServiceA());
_services.Add(new ServiceB());
}
public void Execute()
{
foreach (IService service in _services)
{
service.Execute();
}
}
}
Проблема заключается в том, что ServiceB зависит от некоторых результатов ServiceA. Моя идея состоит в том, чтобы создать контейнерный класс для хранения результатов, а затем ввести его в ServiceA и ServiceB:
class ServiceResults
{
public string SomeProperty {get; set;}
}
public ServiceComposite()
{
ServiceResults result = new ServiceResults();
_services.Add(new ServiceA(result));
_services.Add(new ServiceB(result));
}
Мне интересно, является ли это лучшим возможным способом решения проблемы. Может быть, это нарушает какие-то принципы или правила, о которых я не знаю, или просто "кодовый запах". Каковы лучшие способы сделать это?1 ответ:
Если
ServiceB
нужен результатServiceA
для его правильной работы, то почему бы не иметьServiceB
в зависимости отServiceA
?public interface IService { void Execute(); } public class ServiceA : IService { public void Execute() { ... } } class ServiceB : IService { public ServiceB(IService service) { Service = service; } public void Execute() { ... } public IService Servie { get; set; } }
Тогда в случае, если вы выполняете все
Service
s в коллекции, вы можете добавитьServiceBase
, чтобы убедиться, что эта служба выполняется только один раз: (полный пример)на эту базовую реализацию можно добавить: асинхронную
Execute
, потокобезопасную проверку выполненияInnerExecute
, Flyweight factory для генерации конкретнойIService
, Have aResponseBase
с производными ответами для каждыйService
....public class ServiceResponse { } public interface IService { ServiceResponse Execute(); } public abstract class ServiceBase : IService { public ServiceResponse Execute() { if (_response == null) { _response = InnerExecute(); } return _response; } public abstract ServiceResponse InnerExecute(); private ServiceResponse _response; } public class ServiceA : ServiceBase { public override ServiceResponse InnerExecute() { return new ServiceResponse(); } } public class ServiceB : ServiceBase { public ServiceB(IServiceFactory serviceFactory) { ServiceFactory= serviceFactory; } public override ServiceResponse InnerExecute() { return ServiceFactory.GetServices(ServicesTypes.ServiceA).Execute(); } public IServiceFactory ServiceFactory { get; set; } }
И тот, кто использует эти
Service
s:public enum ServicesTypes { ServiceA, ServiceB.... } public interface IServiceFactory { IEnumerable<IService> GetServices(); IService GetServices(ServicesTypes servicesTypes); } public class SomeOtherThatExecuteServices { public SomeOtherThatExecuteServices(IServiceFactory serviceFactory) { ServiceFactory = serviceFactory; } public IEnumerable<ServiceResponse> ExecuteServices() { return ServiceFactory.GetServices() .Select(service => service.Execute()); } public IServiceFactory ServiceFactory { get; set; } }
возможно, вы захотите получить доступ к фабрике с помощью какого-то ключа отображения, и, вероятно, вам понадобится правильная логика в
SomeOtherThatExecuteServices
, но все это приведет вас на правильный путь (+используя какой-то правильный контейнер IoC)