Здравствуйте, Варвар, Вы писали:
В>Здравствуйте, Александер Малафеев, Вы писали:
АМ>>А так получился далеко не очевидный код и типичный пример "антипаттерна" синглтон.
В>Сталкивался с чем то подобным в Remoting. Тут проблема не в том что используют статики вместо классического синглтона (разницы никакой), а в том что есть трудно прослеживаемая связь между ServicePointManager и WebClient.
Ну я собственно про это и писал, что чтобы проследить связь нужно просмотреть много документации. А синглтон был просто как один из способов упрощения(хотя далеко не лучший), т.к. на него можно было бы получить ссылку из WebClient. Еще один — но очень существенный недостаток, что если у меня несколько клиентов то все они будут вести себя одинаково, что иногда может быть неприемлимым.
Синглетоны в лес к товарищу волку который знает кого кушать... Статик классы тудаже.
Вместо синглетонов нужно использовать контексты которые реализуют IServiceProvider. Синглетоны имеют смысл ОЧЕНЬ редко. И эти исключения скорее подтверждают правила.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>Синглетоны в лес к товарищу волку который знает кого кушать... Статик классы тудаже. WH>Вместо синглетонов нужно использовать контексты которые реализуют IServiceProvider. Синглетоны имеют смысл ОЧЕНЬ редко. И эти исключения скорее подтверждают правила.
А сами контексты уж не синглетоны-ли?
... << RSDN@Home 1.2.0 alpha rev. 650>>
Now playing: «Тихо в лесу…»
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
WH>>Синглетоны в лес к товарищу волку который знает кого кушать... Статик классы тудаже. WH>>Вместо синглетонов нужно использовать контексты которые реализуют IServiceProvider. Синглетоны имеют смысл ОЧЕНЬ редко. И эти исключения скорее подтверждают правила.
_FR>А сами контексты уж не синглетоны-ли?
Нет.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>Здравствуйте, AKoldin, Вы писали:
WH>Синглетоны в лес к товарищу волку который знает кого кушать... Статик классы тудаже. WH>Вместо синглетонов нужно использовать контексты которые реализуют IServiceProvider. Синглетоны имеют смысл ОЧЕНЬ редко. И эти исключения скорее подтверждают правила.
Здравствуйте, Варвар, Вы писали:
WH>>Синглетоны в лес к товарищу волку который знает кого кушать... Статик классы тудаже. WH>>Вместо синглетонов нужно использовать контексты которые реализуют IServiceProvider. Синглетоны имеют смысл ОЧЕНЬ редко. И эти исключения скорее подтверждают правила.
В>А можно по подробней?
Допустим у нас есть некоторое приложение состоящие из нескольких уровней.
class RootContext : IServiceProvider
{
ISomeRootService1 m_ISomeRootService1 = new SomeRootService1Impl();
ISomeRootService2 m_ISomeRootService2 = new SomeRootService2Impl();
ISomeRootService3 m_ISomeRootService3 = new SomeRootService3Impl();
public object GetService(Type serviceType)
{
if (serviceType == typeof(ISomeRootService1))
return m_ISomeRootService1;
if (serviceType == typeof(ISomeRootService2))
return m_ISomeRootService2;
if (serviceType == typeof(ISomeRootService3))
return m_ISomeRootService3;
return null;
}
//Тут могут быть еще какието методы и свойства специфичные для этого контекста
}
class SessionContext : IServiceProvider
{
RootContext m_RootContext;
public SessionContext(RootContext rootContext)
{
m_RootContext = rootContext;
}
ISomeSessionService1 m_ISomeSessionService1 = new SomeSessionService1Impl();
ISomeSessionService2 m_ISomeSessionService2 = new SomeSessionService2Impl();
ISomeSessionService3 m_ISomeSessionService3 = new SomeSessionService3Impl();
public object GetService(Type serviceType)
{
if (serviceType == typeof(ISomeSessionService1))
return m_ISomeSessionService1;
if (serviceType == typeof(ISomeSessionService2))
return m_ISomeSessionService2;
if (serviceType == typeof(ISomeSessionService3))
return m_ISomeSessionService3;
return m_RootContext.GetService(serviceType);
}
//Тут могут быть еще какието методы и свойства специфичные для этого контекста
}
class GuiContext : IServiceProvider
{
SessionContext m_SessionContext;
public GuiContext(SessionContext sessionContext)
{
m_SessionContext = sessionContext;
}
ISomeGuiService1 m_ISomeGuiService1 = new SomeGuiService1Impl();
ISomeGuiService2 m_ISomeGuiService2 = new SomeGuiService2Impl();
ISomeGuiService3 m_ISomeGuiService3 = new SomeGuiService3Impl();
public object GetService(Type serviceType)
{
if (serviceType == typeof(ISomeGuiService1))
return m_ISomeGuiService1;
if (serviceType == typeof(ISomeGuiService2))
return m_ISomeGuiService2;
if (serviceType == typeof(ISomeGuiService3))
return m_ISomeGuiService3;
return m_SessionContext.GetService(serviceType);
}
//Тут могут быть еще какието методы и свойства специфичные для этого контекста
}
При такой схеме в одном приложении могут быть несколько сессий. Причем сервивы уровня сессии для каждой сессии свои, а сервисы корневого уровня доступны всем.
Также у в одной сессии может быть несколько отображений. И опять сервисы уровня отображения у каждого отображения свои.
Теперь нам вдруг на уровне Gui понадобилось логирование. Но создать этот сревис можно только на корневом уровне ибо только он знает где лежит приложение итп.
Меняем SessionContext так
class RootContext : IServiceProvider
{
ISomeRootService1 m_ISomeRootService1 = new SomeRootService1Impl();
ISomeRootService2 m_ISomeRootService2 = new SomeRootService2Impl();
ISomeRootService3 m_ISomeRootService3 = new SomeRootService3Impl();
ILogger m_Logger = new LoggerImpl();
public object GetService(Type serviceType)
{
if (serviceType == typeof(ILogger))
return m_Logger;
if (serviceType == typeof(ISomeRootService1))
return m_ISomeRootService1;
if (serviceType == typeof(ISomeRootService2))
return m_ISomeRootService2;
if (serviceType == typeof(ISomeRootService3))
return m_ISomeRootService3;
return null;
}
//Тут могут быть еще какието методы и свойства специфичные для этого контекста
}
Теперь за счет этой механики логер доступен всем нижележащим слоям. При этом нам не пришлось трогать контексты кроме корневого.
Потом нам понадобилось чтобы в лог добавлялась информация о том из какой сессии идет запись.
Меняем SessionContext так
class SessionContext : IServiceProvider
{
ILogger m_Logger;
RootContext m_RootContext;
public SessionContext(RootContext rootContext)
{
m_RootContext = rootContext;
m_Logger = new SessionLogger(SessionInfo, (ILogger)m_RootContext.GetService(typeof(ILogger)));
}
ISomeSessionService1 m_ISomeSessionService1 = new SomeSessionService1Impl();
ISomeSessionService2 m_ISomeSessionService2 = new SomeSessionService2Impl();
ISomeSessionService3 m_ISomeSessionService3 = new SomeSessionService3Impl();
public object GetService(Type serviceType)
{
if (serviceType == typeof(ILogger))
return m_Logger;
if (serviceType == typeof(ISomeSessionService1))
return m_ISomeSessionService1;
if (serviceType == typeof(ISomeSessionService2))
return m_ISomeSessionService2;
if (serviceType == typeof(ISomeSessionService3))
return m_ISomeSessionService3;
return m_RootContext.GetService(serviceType);
}
//Тут могут быть еще какието методы и свойства специфичные для этого контекста
}
Теперь все записи в лог которые производятся с рамках конкретной сессии будут снабжатся информацией об этой сессии. При это мы не поменяли ни строчки кода на других уровнях приложения.
Таки образом мы может на каждом уровне добавлять свои срвисы, замещать сервисы нижних уровней и даже скрывать сервисы нижних уровней.
В реальных приложениях и слоев больше и сервисов не одни десяток так что механизм который позволяет держать этот зоопарк под контролем не помешает.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>Допустим ...
Идея интересная, надо обдумать. Это ты сам придумал, или где вычитал? Можно ссылку?
WH>В реальных приложениях и слоев больше и сервисов не одни десяток так что механизм который позволяет держать этот зоопарк под контролем не помешает.
Менеджер зоопарка не должен быть одиночкой?
Здравствуйте, WolfHound, Вы писали:
В>>А можно по подробней?
Тогда, все сервисы каждого из компонентов должны быть документированы (что бы знать на каком уровне что находится — понадобится разработчикам сервисов)?
... << RSDN@Home 1.2.0 alpha rev. 650>>
Now playing: «Тихо в лесу…»
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Тогда, все сервисы каждого из компонентов должны быть документированы (что бы знать на каком уровне что находится — понадобится разработчикам сервисов)?
А в чем проблема?
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, Варвар, Вы писали:
В>Идея интересная, надо обдумать. Это ты сам придумал, или где вычитал? Можно ссылку?
Я работал над проектом в котором такая система использована.
В>Менеджер зоопарка не должен быть одиночкой?
Кокой менеджер?
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
_FR>>Тогда, все сервисы каждого из компонентов должны быть документированы (что бы знать на каком уровне что находится — понадобится разработчикам сервисов)? WH>А в чем проблема?
Если это так, то зачем унифицированный доступ? Почему нельзя сделать сервисы открытыми свойствами?
... << RSDN@Home 1.2.0 alpha rev. 650>>
Now playing: «Тихо в лесу…»
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Если это так, то зачем унифицированный доступ? Почему нельзя сделать сервисы открытыми свойствами?
И при появлении каждого нового сервиса протаптывать его во всех контекстах? А не озвереешь?
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>Здравствуйте, Варвар, Вы писали:
В>>Идея интересная, надо обдумать. Это ты сам придумал, или где вычитал? Можно ссылку? WH>Я работал над проектом в котором такая система использована.
В>>Менеджер зоопарка не должен быть одиночкой? WH>Кокой менеджер?
Здравствуйте, WolfHound, Вы писали:
_FR>>Если это так, то зачем унифицированный доступ? Почему нельзя сделать сервисы открытыми свойствами? WH>И при появлении каждого нового сервиса протаптывать его во всех контекстах? А не озвереешь?
Нет, как раз не "протаптывать", а обращаться к тому, какой нужен, раз уж они заранее известны. Просто в приведённой реализации пропадает самая соль "ServiceProvider`а", которая, имхо, в том, что типы (параметра GetService(…) и экземпляра) должны сравниваться не на равенство, а на реализацию, примерно так:
using System;
using System.Diagnostics;
namespace ConsoleApplication2
{
interface IGenericServiceProvider : IServiceProvider
{
T GetService<T>() where T : class;
}
interface IService1
{
}
interface IService2
{
}
interface IService3
{
}
interface IService4 : IService1
{
}
class Service14 : IService1, IService4
{
}
class Service23 : IService2, IService3
{
}
class SomeContext : IGenericServiceProvider
{
Service14 service14 = new Service14();
Service23 service23 = new Service23();
IServiceProvider externalService = null;
public object GetService(Type serviceType)
{
object service;
if(FindService(service14, serviceType, out service)
|| FindService(service23, serviceType, out service)
|| FindServiceFromProvider(externalService, serviceType, out service)
) {
return service;
}//ifreturn null;
}
public T GetService<T>() where T : class
{
return (T)GetService(typeof(T));
}
private bool FindService(object service, Type serviceType, out object instance)
{
if(service == null)
{
throw new ArgumentNullException("service");
}//ifreturn (instance = serviceType.IsAssignableFrom(service.GetType()) ? service : null) != null;
}
private bool FindServiceFromProvider(IServiceProvider service, Type serviceType, out object instance)
{
if(service == null)
{
throw new ArgumentNullException("service");
}//ifreturn (instance = service.GetService(serviceType)) != null;
}
}
class Program
{
static void Main(string[] args)
{
IGenericServiceProvider provider = new SomeContext();
IService1 service1 = provider.GetService<IService1>();
IService2 service2 = provider.GetService<IService2>();
IService3 service3 = provider.GetService<IService3>();
IService4 service4 = provider.GetService<IService4>();
}
}
}
IGenericServiceProvider тут для простоты доступа "из-вне": позволяет указывать имя типа сервиса на один раз меньше.
... << RSDN@Home 1.2.0 alpha rev. 650>>
Now playing: «Тихо в лесу…»
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>А как осуществляется доступ к экземпляру такого констекста?
Контекст передают либо при вызове метода либо в конструктор объекта.
ВВ>Мне кажется этот паттерн несколько ортогонален синглетону...
Он делает синглитон в его классической реализации не нужным.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, _FRED_, Вы писали:
_FR>Нет, как раз не "протаптывать", а обращаться к тому, какой нужен, раз уж они заранее известны. Просто в приведённой реализации пропадает самая соль "ServiceProvider`а", которая, имхо, в том, что типы (параметра GetService(…) и экземпляра) должны сравниваться не на равенство, а на реализацию, примерно так:
То что ты написал это детали реализации которая может быть сколь угодно наворочена. Ничего принципиально они не меняют.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, Варвар, Вы писали:
В>Наверно я плохо понял пример. В>Имеется ввиду Dependency Injection?
Почти. Идея таже только ребята из мелкософта ИМХО перемудрили с реализацией.
Особенно мне во всем этом деле не нравится IServiceContainer совершенно не контролируемая штука.
Да и остальные решения весьма сомнительные... Зачем IComponent'у IDisposable? Зачем ISite'у Name и DesignMode?
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>Он делает синглитон в его классической реализации не нужным.
Может я чего-то и не догоняю, но в том что ты описал я не увидел особого отличия от паттерна Service Layer. И сделать сервис-провайдер синглетоном вполне может оказаться куда более удобным чем передача его экземпляра. Т.е. во всем что ты описал — аналог собственно синглтона это та самая "передача экмзепляра", которую вполне и без сервис лаера можно использовать и считать это чем-то более продвинутым чем синглетон мне кажется несколько затруднительным.