Implementasi MVP berdasarkan ApplicationController dan IoC dalam aplikasi WinForms

Selamat sore!

Pada artikel ini, saya akan berbicara tentang bagaimana saya menerapkan pola MVP dalam aplikasi Windows Forms saya dan menjelaskan situasi dan fitur praktis menggunakan IoC dan ApplicationController. Beralih dari codebehind ke MVP memungkinkan saya:

  • meningkatkan keterbacaan karena pemisahan kode (SRP) yang lebih baik - pisahkan BL dari View;
  • untuk mengembangkan metodologi untuk lebih memperluas fungsionalitas aplikasi;
  • singkirkan singleton, yang saya gunakan untuk bekerja dengan pengaturan aplikasi.

Tentang aplikasi


Aplikasi untuk membantu mengelola grup VC. Memungkinkan Anda mengisi grup dengan tulisan yang menunggu keputusan. Fungsi utama saat ini adalah memuat posting yang tertunda ke dalam grup VK dengan gambar atau video, tagar, jajak pendapat, lokasi geografis dan kemampuan untuk mengkonfigurasi waktu publikasi dan jumlah posting berdasarkan hari. Saat ini, aplikasi memiliki satu bentuk.

Dengan tumbuhnya fungsinya, banyak codebehind terakumulasi, yang mulai membingungkan fakta bahwa bentuknya menjadi sangat dimuat dan berisi segala sesuatu dengan sendirinya. Ketika merencanakan pengembangan lebih lanjut dari proyek ini, saya tidak bisa membayangkan apa yang bisa dilanjutkan lebih lanjut dalam semangat yang sama, dan sekarang saatnya tiba ketika tugas utama bukan memperbaiki fungsi, tetapi refactoring. Dan saya mulai mencari solusi yang akan membantu mengoptimalkan dan membagi kode dan secara umum meningkatkan arsitektur aplikasi sehingga akan lebih menyenangkan untuk bekerja dengannya.

Sumber

Solusi Refactoring


Solusinya adalah menerapkan pola MVP. Sebagai dasar, saya mengambil artikel Fitur Implementasi MVP untuk Formulir Windows .

Artikel mem-parsing contoh luas aplikasi sederhana dengan 3 bentuk: 2 modal utama dan 1. Artikel ini membahas pendekatan yang sangat maju:

  • selain ApplicationController dan IoC, Adaptor juga digunakan di sana, yang memungkinkan penggunaan IoC yang berbeda;
  • 3 jenis bentuk: dengan parameter, tanpa parameter dan modal;
  • Prinsip DIP diterapkan secara luas.

Dalam proyek saya, saya hanya menggunakan satu bentuk tanpa argumen, meninggalkan adaptor (mengikuti prinsip YAGNI), karena IoC Lightinject sudah cukup untuk saya dan pada tingkat lebih rendah saya menggunakan DIP untuk menyederhanakan proyek.

Implementasi MVP


MVP (Model-View-Presenter) adalah pola desain yang dirancang untuk kenyamanan memisahkan logika bisnis dari cara ditampilkan. Anda dapat membaca lebih lanjut tentang teori dalam artikel di atas. Saya akan menjelaskan komponen-komponen dalam implementasi saya:

  • Model adalah struktur data yang ditransfer antara View dan Presenter dan berisi data baik untuk tampilan maupun untuk eksekusi logika. Dalam kasus saya, modelnya adalah Pengaturan. Ketika proyek dimulai, Pengaturan dimuat ke MainFormView, dan ketika memuat dimulai, MainFormView memeriksa dan meneruskan Setuju ke Presenter sehingga Presenter menjalankan logika di sisinya.
  • Lihat adalah formulir di mana data ditampilkan kepada pengguna. Dalam kasus saya, ini adalah data dari model Pengaturan, dan Lihat juga menyediakan acara sehingga Presenter mengaitkan Tampilan dengan BL.

MainFormView mengimplementasikan antarmuka IView umum yang umum untuk semua Tampilan

    public interface IView
    {
        void Show();

        void Close();
    }

serta antarmuka IMainFormView pribadi khusus untuk Tampilan ini saja. Pada awalnya, saya berpikir untuk meninggalkannya, tetapi jika Anda mengaitkan Presenter langsung dengan formulir, maka ketika bekerja dengan Tampilan seperti itu, seluruh rangkaian metode khusus untuk Formulir akan tersedia, yang tidak nyaman.

    public interface IMainFormView: IView
    {
        void LoadSettings(Settings settings);

        void UpdateSettings(Settings settings);

        void ShowMessage(string message);

        void LoadGroups(List<Group> groups);

        void EnableVKUploadGroupBox();

        bool Check();

        event Action Login;

        new event Action Close;

        event Action VKUpload;
    }

Inovasi MVP lainnya adalah bahwa metode Show telah diganti dalam bentuk dan ApplicationContext diteruskan ke formulir melalui konstruktor, sehingga ketika beralih dari formulir ke formulir dan penutup, formulir utama dipindahkan.

        protected ApplicationContext _context;

        public MainForm(ApplicationContext context)
        {
            _context = context;
            InitializeComponent();

            dateTimePickerBeginDate.Format = DateTimePickerFormat.Custom;
            dateTimePickerBeginDate.CustomFormat = "MM/dd/yyyy hh:mm:ss";

            buttonAuth.Click += (sender, args) => Invoke(Login);
            this.FormClosing += (sender, args) => Invoke(Close);
            buttonLoad.Click += (sender, args) => Invoke(VKUpload);
        }

        public new void Show()
        {
            _context.MainForm = this;
            Application.Run(_context);
        }

- Presenter adalah kelas yang merangkum View, Services, dan business logic (BL), dengan bantuan yang mengatur interaksi antara View dan Services. BL terutama diimplementasikan dalam penanganan acara View. Berbeda dengan CodeBehind yang sebelumnya digunakan, dalam penanganan acara MVP penangan BL ditampilkan di Presenter, dan untuk kesederhanaan, acara dalam Tampilan ditampilkan dalam bentuk Tindakan tanpa argumen. Penangan menerima semua data yang diperlukan untuk dieksekusi melalui model yang diperoleh dari formulir melalui metode publik.

Presenter berisi metode Run, yang dipanggil oleh ApplicationController dan yang meluncurkan formulir:

    public interface IPresenter
    {
        void Run();
    }

ApplicationController - satu titik kontrol dan eksekusi seluruh aplikasi. Merangkum semua logika itu sendiri: IoC, Presenter, View, Services.

Manajemen melalui metode Run, yang memanggil Presenter yang sesuai. Semua Presenter terhubung satu sama lain melalui ApplicationController, yang diterima Presenter di konstruktor. Dengan demikian, Presenter dapat memanggil Presenter lain dengan memanggil metode Run, yang secara internal memanggil Kontainer IoC untuk mendapatkan Presenter yang diinginkan dan memulainya.

    public class ApplicationController
    {
        ServiceContainer _container;

        public ApplicationController(ServiceContainer serviceContainer)
        {
            _container = serviceContainer;
            _container.RegisterInstance<ApplicationController>(this);
        }

        public void Run<TPresenter>() where TPresenter:class, IPresenter
        {
            var presenter = _container.GetInstance<TPresenter>();
            presenter.Run();
        }
    }

Kontainer IoC adalah agregator dari semua "dependensi" yang digunakan dalam logika aplikasi. Itu mengandung:

  • Lihat konstruktor
  • Pembawa Acara Konstruktor
  • contoh layanan
  • konteks aplikasi
  • ApplicationController

Semua dependensi ditambahkan ke wadah selama startup, ini dapat dilihat di file Program.cs:

         static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            ulong appid = ulong.Parse(ConfigurationManager.AppSettings["AppIdForTest"]);
            VKGroupHelperWorker vk = new VKGroupHelperWorker(appid);


            ServiceContainer container = new ServiceContainer();
            container.RegisterInstance<VKGroupHelperWorker>(vk);
            container.RegisterInstance<Settings>(Globals.Settings);
            container.RegisterInstance<ApplicationContext>(Context);
            container.Register<IMainFormView,MainForm>();
            container.Register<MainFormPresenter>();

            ApplicationController controller = new ApplicationController(container);
            controller.Run<MainFormPresenter>();
        }

Untuk IoC, saya menggunakan komponen Lightinject, yang harus diinstal melalui NPM sebelum digunakan.

Dengan demikian, wadah bisa berisi konstruktor objek dan objek itu sendiri, seperti yang dilakukan dengan Pengaturan dan VKGroupHelperWorker (klien VK API), membentuk himpunan semua sumber daya aplikasi yang digunakan. Fitur yang berguna dari wadah adalah bahwa semua sumber daya yang tertanam ini, kelas dapat diperoleh melalui argumen konstruktor. Misalnya,
ApplicationController, IMainFormView, VKGroupHelperWorker - dependensi yang sebelumnya diimplementasikan, yang dapat berupa konstruktor objek atau instance. Jika sebuah instance telah diimplementasikan, maka semua objek yang dihasilkan akan bekerja dengan instance yang sama, yang memungkinkan Anda untuk menyingkirkan pola singleton jika itu digunakan.

public MainFormPresenter(ApplicationController applicationController, IMainFormView mainForm, Settings settings, VKGroupHelperWorker vk)
        {
            _view = mainForm;
            _settings = settings;
            _vk = vk;

            _view.Login += () => Login();
            _view.Close += () => Close();
            _view.VKUpload += () => VKUpload();
        }

Implementasi MVP memungkinkan saya untuk:

  • sebagian menyingkirkan Singleton, yang saya gunakan untuk bekerja dengan pengaturan aplikasi;
  • Pisahkan BL dari View, sehingga meningkatkan pemisahan kode (SRP)
  • untuk mengembangkan pendekatan untuk memperluas aplikasi tanpa mengacaukan View.

Informasi lebih lanjut tentang apa yang telah dilakukan dapat ditemukan di repositori proyek .

All Articles