Использование класса в его конструкторе С# — это пахнет?

Код ниже пахнет? Я рефакторинг некоторого кода и обнаружил эту циклическую связь, где foo нужен класс, которому нужен интерфейс, который реализует сам foo.

В реальном коде foo — это пользовательский элемент управления Silverlight, а ifoo имеет методы для выполнения действий типа пользовательского интерфейса, таких как вызов диалогового окна (например, ShowMessage). Класс needsAnIfoo — это (своего рода) контроллер, который использует интерфейс ifoo всякий раз, когда ему нужно что-то сделать с пользовательским интерфейсом. У меня есть разные «тематические» пользовательские интерфейсы, которые реализуют iFoo и имеют один и тот же шаблонный код в своем конструкторе. needsAnIfoo имеет различные свойства, которые привязаны к данным пользовательского интерфейса (так что это тоже своего рода модель).

Он компилируется и работает нормально, но мне интересно, есть ли лучший способ.

Итак, пахнет?

    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);

person Daniel James Bryars    schedule 08.11.2009    source источник


Ответы (6)


Звучит как отличное место для создания шаблона, я бы сказал, Фабричный метод.

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);

Узоры — ваш друг!

person Josh    schedule 08.11.2009
comment
Спасибо за пример! Я разместил, где я должен в данный момент. - person Daniel James Bryars; 10.11.2009
comment
Наконец-то проголосовал за вас! (Я просматриваю некоторые старые сообщения и объединяю учетные записи, поэтому я только что вспомнил об этом сообщении.) - person Daniel James Bryars; 09.01.2011

Мне это кажется неправильным. Пахнет хрупкостью.

Рассматривали ли вы возможность использования шаблона построителя или фабрики для создания соответствующих объектов и установления отношений между ними? Это может обеспечить более безопасный путь вперед.

person Andrew    schedule 08.11.2009
comment
Да, спасибо, фабрика кажется подходящим вариантом. Я собрал фабрику прототипов, которая делает мои iFoos - я собираюсь опубликовать код. - person Daniel James Bryars; 10.11.2009

Я согласен с тем, что шаблон Builder или Factory или аналогичный был бы лучше. Предоставленный код не очень хорошо тестируется и, как уже упоминалось, довольно хрупок, поэтому некоторая форма внедрения зависимостей была бы хорошей.

Используемый шаблон будет зависеть от того, как foo и needAnIFoo используют друг друга. Возможно, вам также придется рассмотреть шаблон Observer, если needAnIFoo — это субъект, foo — наблюдатель, а bar() — метод обновления.

person David    schedule 08.11.2009
comment
Хорошо, я немного переставил вещи — я создал Фабрику и использовал Событие в качестве основы для шаблона наблюдателя. Я собираюсь опубликовать то, что я сделал через мгновение. - person Daniel James Bryars; 10.11.2009

Похоже, это может быть слишком сложно, и что вы делаете свою тему контроллером и имеете контроллер (путем реализации обоих классов 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()); 
person dan    schedule 08.11.2009
comment
Не говоря уже о чем-то, но если это (новый?) дизайн, он определенно должен использовать шаблон построения (фабричный метод кажется наиболее разумным). - person Josh; 09.11.2009
comment
Да, фабричный метод завершил бы дизайн :) - person dan; 09.11.2009

Я пытаюсь немного понять вопрос, но если каждый needAnIfoo привязан только к одному типу класса, а needAnIfoo ничего не делает для себя, кажется, что вы можете создать статический класс needAnIfoo с методами расширения, нет необходимости передавать это как конструктор аргумент.

руководство по программированию метода расширения

person AndrewB    schedule 09.11.2009

Вот фабрика с наблюдателем — похоже, это помогает и позволяет избежать обновления «контроллера» внутри моего тематического пользовательского интерфейса.

   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();
    }
person Daniel James Bryars    schedule 10.11.2009
comment
На самом деле это не фабрика — это больше похоже на «Фабрику легковесов», предложенную Gamma et. др. (стр. 198). Шаблон фабрики учебников используется для создания новых объектов; не предоставлять ссылки на существующие объекты. При этом шаблоны — это ориентиры. Я считаю, что этот фрагмент значительно улучшен по сравнению с оригиналом — и это шаблон, применимый, скажем, для переключения между темами в реальном времени (а не для выбора при запуске, для которого больше подходит фабрика учебников). - person Josh; 10.11.2009
comment
Извините - это фабрика; только не фабрика учебников. Ни в коем случае не плохо; просто разъясняю цель шаблона Factory! :D - person Josh; 10.11.2009