Страницы

Поиск по вопросам

понедельник, 16 декабря 2019 г.

Помогите разобраться с отделением бизнес-логики от формы

#c_sharp #winforms #entity_framework #linq #mvp


Я только начинаю осваивать C#. Сейчас пытаюсь разобраться в аспектах проектирования
приложения для работы с базами данных. Практически каждый раз я слышу такую фразу "бизнес-логика
должна существовать отдельно от формы". Но я не совсем понимаю как этого добиться при
программировании WinForms?

Посоветуйте пожалуйста исчерпывающее руководство или литературу на этот счет.

UPD:

На данный момент удалось понять, что при использовании WinForms необходимо использовать
паттерн MVP. И единственный пример использования MVP для WinForm, по которому удалось
построить рабочее приложение, я смог найти вот в этом топике Как начать пользоваться
MVP + WinForms?. Следую изложенной в нём информации у меня получилось следующее приложение.

Можете оценить, насколько у меня получилась правильная реализация применения паттерна
MVP и отделения бизнес-логики от формы?

View

using System.Linq;

namespace EFCodeFirstMVP
{
    interface IView
    {
        void SetData(IQueryable items);
    }
}

using System;
using System.Linq;
using System.Windows.Forms;


namespace EFCodeFirstMVP
{
    public partial class Form1 : Form, IView
    {
        private readonly GoodsPresenter presenter;

        public Form1()
        {
            presenter = new GoodsPresenter(this, new GoodsModel());
            InitializeComponent();
        }

        public void SetData(IQueryable items)
        {
            dataGridView1.DataSource = items.ToList();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            presenter.LoadData();
        }
    }
}


Presenter

namespace EFCodeFirstMVP
{
    class GoodsPresenter
    {
        private readonly IView view;
        private readonly IModel model;

        public GoodsPresenter(IView view, IModel model)
        {
            this.view = view;
            this.model = model;
        }

        public void LoadData()
        {
            var data = model.LoadData();
            view.SetData(data);
        }
    }
}


Model

using System.Linq;

namespace EFCodeFirstMVP
{
    interface IModel
    {
        IQueryable LoadData();
    }
}

using System.Linq;

namespace EFCodeFirstMVP
{
    class GoodsModel : IModel
    {
        public IQueryable LoadData()
        {
            Context context = new Context();

            var items = from Items in context.Goods
                        select Items;

            return items;
        }
    }
}


Data

using System.Data.Entity;

namespace EFCodeFirstMVP
{
class Context: DbContext
    {
        public DbSet Goods { get; set; }
        public DbSet GoodsList { get; set; }

        public Context()
        {
            Database.SetInitializer(new CreateDatabaseIfNotExists());
        }
    }

public class Goods
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public string Barcode { get; set; }
        public int Price{ get; set; }
    }
}

    


Ответы

Ответ 1



Окей, давайте попробуем поговорить об этом вне привязки к WinForms. Смотрите. У вас есть две различные вещи: внутреннее поведение программы, и то, как она показывает это пользователю. Представьте себе, чтобы вы пишете программное обеспечение радара. У вашей программы внутри есть список отслеживаемых самолётов. Вы принимаете информацию с датчиков, обсчитываете её, принимаете решение о том, возник новый самолёт, или ложная цель, или известный вам самолёт переместился. Всё это происходит внутри, и для этого взаимодействие с пользователем не так уж и обязательно. Это внутренняя часть программы, модель. Теперь, вам нужно донести эту информацию до оператора. В каком виде вы будете представлять информацию — в виде распечаток зелёного текста на чёрном фоне, или в виде трёхмерной голографической визуализации — не так уж важно, и модель по существу не зависит от этой части. Поэтому вы должны писать модель так, чтобы модель ничего не знала о представлении. Это не то, чтобы строго обязательно, но это позволяет разделить программу на независимые части, и даёт лёгкость работы с ними. Здесь ещё остаются открытыми вопросы о том, как передавать действия пользователя модели, но это отдельная тема. Посмотрим на более приземлённый пример: работа с базой данных. Точно так же у вас есть модель: база данных, и операции над ней, которые вы собираетесь делать. Это всё организуется в модуль, возможно, навешивается сверху синхронизация и асинхронность, на этом модель можно считать оконченной. Представление должно просто показывать пользователю часть модели принимать у пользователя команды, и доставлять их модели после обновления модели показывать обновлённую информацию Обычно выделяют ещё и промежуточный уровень — бизнес-логику, контроллер, view model, которые занимаются пинанием модели, с тем чтобы представление занималось только представлением.

Ответ 2



Когда говорят, что форма/UI/View отделена от логики/модели, то имеется ввиду, что UI определен в отдельном namespace и/или class. При этом View получает минимальное количество информации о модели. View подключается к стандартным интерфейсам модели и таким образом может отслеживать и выводить на экран изменения данных. Ниже пример, в котором модель отделена от View. По таймеру в модель добавляются данные, которые выводятся в View. class View { static public void Show(object model, string member) { // создаем UI var f = new Form(); var g = new DataGridView() { Parent = f, Dock = DockStyle.Fill, DataSource = model, DataMember = member }; f.ShowDialog(); } } class Model { public Model() { // создаем модель - набор данных и правила их обработки var d = new DataSet(); d.ReadXml(new StringReader("")); // создаем таймер, для изменения модели раз в секунду var timer = new Timer() { Interval = 1000 }; // обработчик событий таймера timer.Tick += (s, e) => { var t = d.Tables["row"]; // создаем новую строку var r = t.NewRow(); r[0] = DateTime.Now.Millisecond; // доавляем строку в DataSet. при этом UI обновится сам. t.Rows.Add(r); }; // запускаем таймер timer.Start(); this.DataMember = "row"; this.DataSource = d; } public readonly string DataMember; public readonly object DataSource; } [STAThread] static void Main() { var m = new Model(); // создаем UI и привязываем его к модели View.Show(m.DataSource, m.DataMember); }

Комментариев нет:

Отправить комментарий