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

2

Означает ли код ниже? Я рефакторинг некоторого кода и обнаружил это круговое отношение, где 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);
Теги:
constructor
class-design

6 ответов

1
Лучший ответ

Звучит как отличное место для создания шаблона, я бы сказал 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);

Шаблоны - ваш друг!

  • 0
    Спасибо за пример! Я отправил, где я должен в данный момент.
  • 0
    Может ли брат получить upvote? :) Я прокомментирую ваши вещи прямо сейчас ...
Показать ещё 1 комментарий
1

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

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

  • 0
    Хорошо, я немного переставил вещи - я создал Фабрику и использовал Событие в качестве основы для шаблона наблюдателя. Я собираюсь опубликовать то, что я сделал в данный момент.
1

Похоже, что это может быть слишком сложно, и что вы делаете свою тему контроллером и имеете контроллер (если оба класса реализуют 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()); 
  • 0
    Не для того, чтобы уловить точку, но если это (новый?) Дизайн, то он определенно должен использовать шаблон построения (Factory Method кажется наиболее подходящим).
  • 0
    Да, заводской метод завершит дизайн :)
1

Это не выглядит правильным. Запахи хрупкие.

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

  • 0
    Да, спасибо заводу кажется, что путь. Я собрал фабрику прототипов, которая делает мои "iFoos" - я собираюсь опубликовать код.
0

Здесь 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();
    }
  • 1
    Это на самом деле не фабрика - это больше похоже на «фабрику по производству летучих грузов», предложенную Gamma et. и др. (P198). Шаблон фабрики учебников используется для создания новых объектов; не предоставлять ссылки на существующие объекты. При этом шаблоны являются руководящими принципами. Я полагаю, что этот фрагмент значительно улучшен по сравнению с оригиналом - и это образец, применимый, скажем, для переключения между темами в режиме реального времени (а не выбранный при запуске, для которого фабрика учебников была бы более подходящей).
  • 0
    Извините - это фабрика; просто не учебник фабричный. Ни в коем случае не плохо; просто прояснение намерений фабричного образца! : D
0

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню