Внедрение Presenter в шаблон Модель-Вид-Презентатор с StructureMap - PullRequest
6 голосов
/ 05 февраля 2009

Я реализовал свою собственную копию шаблона презентатора представления модели (в духе фабрики программного обеспечения веб-клиента), чтобы я мог использовать свою собственную структуру DI вместо привязки к ObjectBuilder WCSF, с которым у меня были многочисленные проблемы. У меня есть несколько способов сделать это, но ни один из них не делает меня счастливым. Я хотел знать, есть ли у кого-нибудь еще какие-нибудь идеи.

Раствор № 1а

Использует HttpModule для перехвата контекста. PreRequestHandlerExecute для вызова ObjectFactory.BuildUp (HttpContext.Current.Handler)

public partial class _Default : Page, IEmployeeView
{
    private EmployeePresenter _presenter;

    private EmployeePresenter Presenter
    {
        set
        {
            _presenter = value;
            _presenter.View = this;
        }
    }
}

Решение # 1b

Вызовите наращивание при загрузке страницы вместо использования HttpModule

public partial class _Default : Page, IEmployeeView
{
    private EmployeePresenter _presenter;

    private EmployeePresenter Presenter
    {
        set
        {
            _presenter = value;
            _presenter.View = this;
        }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        ObjectFactory.BuildUp(this);
    }
}

Решение # 1c

Доступ к презентатору через свойство позволяет Getter в BuildUp, если необходимо.

public partial class _Default : Page, IEmployeeView
{
    private EmployeePresenter _presenter;

    public EmployeePresenter Presenter
    {
        get
        {
            if (_presenter == null)
            {
                ObjectFactory.BuildUp(this);
            }

            return _presenter;
        }
        set
        {
            _presenter = value;
            _presenter.View = this;
        }
    }
}

Решение № 2

public partial class _Default : Page, IEmployeeView
{
    private EmployeePresenter _presenter;

    private EmployeePresenter Presenter
    {
        get
        {
            if (_presenter == null)
            {
                _presenter = ObjectFactory.GetInstance<EmployeePresenter>();
                _presenter.View = this;
            }

            return _presenter;
        }
    }
}

Решение # 2b

public partial class _Default : Page, IEmployeeView
{
    private EmployeePresenter _presenter;

    private EmployeePresenter Presenter
    {
        get
        {
            if (_presenter == null)
            {
                Presenter = ObjectFactory.GetInstance<EmployeePresenter>();
            }

            return _presenter;
        }
        set
        {
            _presenter = value;
            _presenter.View = this;
        }
    }
}

Редактировать : добавлено решение 1с, 2b

Ответы [ 4 ]

7 голосов
/ 10 февраля 2009

Я бы использовал решение # 1b и создал бы супертип слоя для всех страниц, чтобы СУШИТЬ инициализацию презентатора немного больше. как это:

Код страницы:

public partial class _Default : AbstractPage, IEmployeeView
{
    private EmployeePresenter presenter;

    private EmployeePresenter Presenter
    {
        set
        {
            presenter = value;
            presenter.View = this;
        }
    }
    protected override void Do_Load(object sender, EventArgs args)
    {
        //do "on load" stuff 
    }

}

Абстрактный код страницы:

public abstract class AbstractPage : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ObjectFactory.BuildUp(this);
        this.Do_Load(sender,e); 
        //template method, to enable subclasses to mimic "Page_load" event

    }
    //Default Implementation (do nothing)
    protected virtual void Do_Load(object sender, EventArgs e){}
}

С этим решением у вас есть инициализация презентатора (созданная ObjectFactory) только в одном классе, если вам нужно изменить его позже, вы можете сделать это легко.

Изменить:

Должно ли Do_Load быть абстрактным или виртуальным ?

Шаблонный метод первоначально заявляет, что метод должен быть Абстрактным, чтобы подклассы реализовывали его, придерживаясь контракта суперкласса. (см. пример «Монополии» из Википедии <«Игра»). </p>

С другой стороны, в данном конкретном случае мы не хотим заставлять пользовательский класс переопределять наш метод, но даем ему возможность сделать это. Если вы объявите его абстрактным, многие классы будут вынуждены переопределить метод, чтобы оставить его пустым (это явно запах кода). Поэтому мы предоставляем разумное значение по умолчанию (ничего не делаем) и делаем метод виртуальным.

1 голос
/ 14 февраля 2009

Я тоже создал свой собственный фреймворк MVP. Я нашел лучший способ для меня - использовать дженерики с базовым классом страниц. Указав тип Presenter в определении общего класса, я упускаю большую часть кода, который требуется каждому из ваших предложений.

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

Извините, у меня нет кода для вас, но я могу опубликовать его для вас, если хотите. У меня также есть старая версия кода, размещенная по адресу www.codeplex.com/aspnetmvp, если вы хотите узнать, как он работает.

1 голос
/ 14 февраля 2009

Я использовал базовый класс страниц с:

protected override void OnInit(EventArgs e)
    {
        StructureMap.ObjectFactory.BuildUp(this);
        base.OnInit(e);
    }

Подход базового класса также работает и с пользовательскими элементами управления, что само по себе удерживало меня от модуля (не хотелось иметь два способа его настройки) Для страницы это

public partial class Employee : View, IEmployeeView
{
    public ViewPresenter Presenter { get; set; }
    private void Page_Load(object sender, EventArgs e){}
}

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

static T GetView<T>()
{
    return (T) HttpContext.Current.Handler;
}

В конфигурации структуры карты используйте соглашение как для докладчика, так и для внедрения представления.

0 голосов
/ 16 февраля 2009

Спасибо всем за ваш очень ценный вклад. Каждый из ваших ответов дал мне ценные идеи для объединения в моем окончательном решении, и вот что я придумал:

public abstract class ViewBasePage<TPresenter, TView> :
    Page where TPresenter : Presenter<TView>
{
    protected TPresenter _presenter;

    public TPresenter Presenter
    {
        set
        {
            _presenter = value;
            _presenter.View = GetView();
        }
    }

    /// <summary>
    /// Gets the view. This will get the page during the ASP.NET
    /// life cycle where the physical page inherits the view
    /// </summary>
    /// <returns></returns>    
    private static TView GetView()
    {
        return (TView) HttpContext.Current.Handler;
    }

    protected override void OnPreInit(EventArgs e)
    {
        ObjectFactory.BuildUp(this);
        base.OnPreInit(e);
    }
}

И унаследовал мою исходную страницу:

public partial class _Default : 
    ViewBasePage<EmployeePresenter, IEmployeeView>, IEmployeeView
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            _presenter.OnViewInitialized();
        }

        _presenter.OnViewLoaded();
        Page.DataBind();
    }

    #region Implementation of IEmployeeView

    ...

    #endregion
}
...