Означает ли код ниже? Я рефакторинг некоторого кода и обнаружил это круговое отношение, где foo нуждается в классе, которому нужен интерфейс, который сам реализует.
В реальном коде foo - это Silverlight UserControl, и ifoo имеет методы для создания типов пользовательского интерфейса, например, для создания диалогового окна (например, ShowMessage
). Класс needsAnIfoo
- это (вид) контроллера, который использует интерфейс ifoo всякий раз, когда он хочет что-либо сделать с пользовательским интерфейсом. У меня есть разные "тематические" пользовательские интерфейсы, которые реализуют iFoo и имеют один и тот же код плиты котла в своем конструкторе. needsAnIfoo
имеет различные свойства, которые привязываются к UI (так что это тоже модель).
Он компилируется и работает отлично, но мне интересно, есть ли лучший способ.
Итак, это пахнет?
interface ifoo
{
void bar();
}
class foo : ifoo
{
readonly needsAnIfoo _needsAnIfoo;
internal foo()
{
_needsAnIfoo = new needsAnIfoo(this);
}
#region ifoo Members
public void bar()
{
throw new NotImplementedException();
}
#endregion
}
class needsAnIfoo
{
readonly ifoo _myfoo;
public needsAnIfoo(ifoo foo)
{
_myfoo = foo;
}
}
static void Main(string[] args)
{
foo foo = new foo();
}
Возможно, я должен обновить needsAnIfoo
без передачи iFoo в конструкторе, а затем дать ему iFoo
в методе Initialize
. Но это выглядит очень странно:
foo foo = new foo();
needsAnIfoo needsAnIfoo = new needsAnIfoo(foo);
foo.Initialise(needsAnIfoo);
Звучит как отличное место для создания шаблона, я бы сказал Factory Method.
class Factory
{
public:
virtual needsAnIfoo* Create(ProductId);
};
needsAnIfoo* Factory::Create(ProductId id)
{
if (id == TYPE1) return new needsAnIfoo(ifooType1());
if (id == TYPE2) return new needsAnIfoo(ifooType2());
...
return 0;
}
Затем вы будете использовать его так:
Factory f = new Factory();
theme1 = f.Create(TYPE1);
theme2 = f.Create(TYPE2);
Шаблоны - ваш друг!
Я согласен с тем, что шаблон Builder или Factory, или аналогичный, будет лучше. Предоставленный код не очень проверен и, как уже упоминалось, является хрупким, поэтому некоторая форма инъекции зависимостей будет хорошей.
Используемый шаблон будет зависеть от того, как foo и needsAnIFoo используют друг друга. Возможно, вам придется рассмотреть шаблон Observer, если needAnIFoo является субъектом, foo является наблюдателем, а bar() - методом обновления.
Похоже, что это может быть слишком сложно, и что вы делаете свою тему контроллером и имеете контроллер (если оба класса реализуют ifoo)
Вы можете получить лучшие результаты, если вы разделите понятия темы и контроллера, чтобы в контроллере была тема. Затем, например, когда контроллер что-то делает, как всплывающее диалоговое окно, он просматривает свою тему, чтобы узнать, какой шрифт использовать.
Вот так:
interface itheme {} // to describe properties of the theme
class theme : itheme {}// a bunch of different themes, this previously would have been the "foo"
class theme2 :itheme{} //etc.
abstract class icontroller
{
protected icontroller(itheme ptheme) {theme = ptheme;}
protected itheme theme;
//function declarations
// ....
}
class control : icontroller {} // implements the icontrol functions.
//not sure if you need more than one control implementation...
// if not, i'd get rid of the icontrol interface.
//use it by passing a theme into the controller constructor:
icontroller myUIController = new control(new ClassicTheme());
Это не выглядит правильным. Запахи хрупкие.
Рассматривали ли вы просмотр шаблона построителя или factory для создания соответствующих объектов и установления отношений между ними? Это может обеспечить более безопасный путь вперед.
Здесь factory с наблюдателем - кажется, делает трюк и избегает встраивания "контроллера" в мой тематический интерфейс.
interface ifoo
{
void bar();
}
class foo : ifoo
{
public void bar() { Console.Write("do a foo type thing"); }
}
class foo2 : ifoo
{
public void bar() { Console.Write("do a foo2 type thing"); }
}
class needsAnIfoo
{
public event EventHandler SomethingIFooCanDealWith;
System.Threading.Timer _timer;
public needsAnIfoo()
{
_timer = new System.Threading.Timer(MakeFooDoSomething, null, 0, 1000);
}
void MakeFooDoSomething(Object state)
{
if (SomethingIFooCanDealWith != null)
{
SomethingIFooCanDealWith(this,EventArgs.Empty);
};
}
}
class fooFactory
{
needsAnIfoo _needsAnIfoo = new needsAnIfoo();
Dictionary<String, ifoo> _themedFoos = new Dictionary<string,ifoo>();
ifoo _lastFoo = null;
public void RegisterFoo(String themeName, ifoo foo)
{
_themedFoos.Add(themeName, foo);
}
public ifoo GetThemedFoo(String theme)
{
if (_lastFoo != null) { _needsAnIfoo.SomethingIFooCanDealWith -= (sender, e) => _lastFoo.bar(); };
ifoo newFoo = _themedFoos[theme];
_needsAnIfoo.SomethingIFooCanDealWith += (sender, e) => newFoo.bar();
_lastFoo = newFoo;
return newFoo;
}
}
static void Main(string[] args)
{
fooFactory factory = new fooFactory();
factory.RegisterFoo("CompanyA", new foo());
factory.RegisterFoo("CompanyB", new foo2());
ifoo foo = factory.GetThemedFoo("CompanyA");
Console.Write("Press key to switch theme");
Console.ReadKey();
foo = factory.GetThemedFoo("CompanyB");
Console.ReadKey();
}
Я пытаюсь понять вопрос немного, но если каждый needAnIfoo привязан только к одному типу класса, а needAnIfoo ничего не делает для себя, кажется, что вы можете создать статический класс needsAnIfoo с методами расширения, которые не нужно передавать это как конструктор arg.