Pikiran implementasi Model-View-Presenter

34

Saya mencoba untuk memahami cara menerapkan decoupling yang baik antara UI dan model, tapi saya mengalami kesulitan mencari tahu di mana harus membagi garis.

Saya telah melihat Model-View-Presenter, tapi saya tidak yakin bagaimana cara mengimplementasikannya. Misalnya, Tampilan saya memiliki beberapa dialog ..

  • Haruskah ada kelas tampilan dengan instance dari masing-masing dialog? Lalu dalam hal itu, bagaimana seharusnya dialog berinteraksi dengan Presenter? yaitu. jika sebuah dialog individual perlu meminta data dari Model melalui Presenter, bagaimana seharusnya dialog mendapatkan referensi ke Presenter? Melalui referensi ke Tampilan yang diberikan padanya selama konstruksi?
  • Saya berpikir mungkin pandangan harus kelas statis? Kemudian dialog GetView dan dapatkan Presenter dari sana ...
  • Saya telah berpikir tentang pengaturan Presenter dengan kepemilikan View dan Model (sebagai lawan dari View memiliki Presenter dan Presenter memiliki Model) dan Presenter mendaftarkan panggilan balik untuk acara di View, tetapi itu membuatnya tampak banyak. lebih berpasangan (atau bahasa tergantung, setidaknya.)

Saya mencoba untuk:

  1. buat ini sebagai dipisahkan mungkin
  2. idealnya memungkinkan untuk memasangkan Presenter / Model dengan Tampilan dari bahasa lain (Saya belum melakukan banyak hal antar-bahasa, tapi saya tahu itu mungkin, terutama semakin void(void)saya bisa bertahan, setidaknya aplikasi C # dengan Pustaka C ++
  3. menjaga kode tetap bersih dan sederhana

Jadi .. ada saran bagaimana interaksi harus ditangani?

coba tangkap
sumber
Sudahkah Anda melihat artikel ini ?: en.wikipedia.org/wiki/Model-view-presenter
Bernard
1
Saya punya .. Saya menemukannya agak cepat dan tingkat tinggi, saya ingin lebih memahami bagaimana menangani beberapa dialog dalam proyek besar dengan sesedikit mungkin penggandengan ..
trycatch

Jawaban:

37

Selamat datang di lereng yang licin. Anda pada titik ini menyadari bahwa ada variasi tanpa akhir dari semua interaksi model-view. MVC, MVP (Taligent, Dolphin, Passive View), MVVM hanya untuk beberapa nama.

Pola Presenter Model View, seperti kebanyakan pola arsitektur terbuka untuk banyak variasi dan eksperimen. Satu hal yang sama dari semua variasi adalah peran presenter sebagai "perantara" antara tampilan dan model. Dua yang paling umum adalah Tampilan Pasif dan Presenter / Pengawas Pembimbing - [ Fowler ]. Tampilan Pasif memperlakukan UI sebagai antarmuka yang sangat dangkal antara pengguna dan presenter. Ini mengandung sangat sedikit jika ada logika, mendelegasikan banyak tanggung jawab kepada presenter. Mengawasi Presenter / Pengendalimencoba mengambil keuntungan dari pengikatan data yang dibangun dalam banyak kerangka UI. UI menangani sinkronisasi data tetapi langkah presenter / controller untuk logika yang lebih kompleks. Dalam kedua kasus model, tampilan dan penyaji membentuk triad

Ada banyak cara untuk melakukan ini. Sangat umum untuk melihat ini ditangani dengan memperlakukan setiap dialog / form sebagai tampilan yang berbeda. Sering kali ada hubungan 1: 1 antara pandangan dan presenter. Ini bukan aturan yang keras dan cepat. Sangat umum untuk memiliki satu presenter menangani banyak pandangan terkait atau sebaliknya. Itu semua tergantung pada kompleksitas pandangan dan kompleksitas logika bisnis.

Adapun bagaimana pandangan dan penyaji mendapatkan referensi satu sama lain, ini kadang-kadang disebut kabel . Anda memiliki tiga pilihan:

View memegang referensi ke presenter
A form atau dialog yang mengimplementasikan view. Formulir ini memiliki pengendali acara yang melakukan delade ke presenter menggunakan panggilan fungsi langsung:

MyForm.SomeEvent(Sender)
{
  Presenter.DoSomething(Sender.Data);
}

Karena presenter tidak memiliki referensi ke tampilan, view harus mengirimkan data sebagai argumen. Presenter dapat berkomunikasi kembali ke tampilan dengan menggunakan acara / fungsi panggilan balik yang harus didengarkan oleh view.

Presenter memegang referensi untuk melihat
Dalam skenario tampilan memperlihatkan properti untuk data yang ditampilkan kepada pengguna. Presenter mendengarkan acara dan memanipulasi properti pada tampilan:

Presenter.SomeEvent(Sender)
{
  DomainObject.DoSomething(View.SomeProperty);
  View.SomeOtherProperty = DomainObject.SomeData;
}

Keduanya memegang referensi satu sama lain membentuk ketergantungan sirkuler.
Skenario ini sebenarnya lebih mudah untuk dikerjakan dibandingkan yang lain. Tampilan merespons acara dengan memanggil metode di presenter. Presenter membaca / memodifikasi data dari tampilan melalui properti yang terbuka.

View.SomeEvent(Sender)
{
  Presenter.DoSomething();
}

Presenter.DoSomething()
{
  View.SomeProperty = DomainObject.Calc(View.SomeProperty);
}

Ada masalah lain yang harus dipertimbangkan dengan pola MVP. Urutan penciptaan, masa hidup objek, di mana pengkabelan berlangsung, komunikasi antara triad MVP tetapi jawaban ini telah berkembang cukup lama.

Kenneth Cochran
sumber
1
Ini jelas sangat membantu. Komunikasi antara triad dan seumur hidup adalah di mana aku saat ini mengalami masalah sekarang karena aku memahami sebagian dari ini.
trycatch
8

Seperti yang dikatakan semua orang, ada puluhan pendapat dan tidak ada satu pun yang benar atau salah. Tanpa masuk ke segudang pola dan hanya berfokus pada MVP inilah beberapa saran untuk implementasi.

Pisahkan mereka. Tampilan harus mengimplementasikan antarmuka yang membentuk ikatan antara tampilan dan presenter. Pandangan menciptakan presenter dan menyuntikkan dirinya ke presenter dan memaparkan metode yang ditawarkan untuk presenter untuk berinteraksi dengan tampilan. Pandangan bertanggung jawab untuk menerapkan metode atau properti ini dengan cara apa pun yang diinginkannya. Secara umum Anda memiliki satu tampilan: satu presenter tetapi dalam beberapa kasus Anda dapat memiliki banyak tampilan: satu presenter (web, wpf, dll.). Kuncinya di sini adalah bahwa presenter tidak mengetahui implementasi UI dan hanya berinteraksi dengan tampilan melalui antarmuka.

Ini sebuah contoh. Pertama kita memiliki kelas tampilan dengan metode sederhana untuk menampilkan pesan kepada pengguna:

interface IView
{
  public void InformUser(string message);
}

Sekarang inilah presenternya. Perhatikan bahwa presenter mengambil IView ke konstruktornya.

class Presenter
{
  private IView _view;
  public Presenter(IView view)
  {
    _view = view;
  }
}

Sekarang inilah antarmuka pengguna yang sebenarnya. Ini bisa berupa jendela, dialog, halaman web, dll. Tidak masalah. Perhatikan konstruktor untuk tampilan akan membuat presenter dengan menyuntikkan dirinya ke dalamnya.

class View : IView
{
  private Presenter _presenter;

  public View()
  {
    _presenter = new Presenter(this);
  }

  public void InformUser(string message)
  {
    MessageBox.Show(message);
  }
}

Presenter tidak peduli tentang bagaimana view mengimplementasikan metode yang baru saja dilakukannya. Untuk semua presenter tahu, itu bisa menulis ke file log dan bahkan tidak menunjukkannya kepada pengguna.

Bagaimanapun, presenter melakukan beberapa pekerjaan dengan model di bagian belakang dan pada suatu titik ingin memberi tahu pengguna tentang apa yang terjadi. Jadi sekarang kita memiliki metode di suatu tempat di presenter yang memanggil ke tampilan pesan InformUser.

class Presenter
{
  public void DoSomething()
  {
    _view.InformUser("Starting model processing...");
  }
}

Di sinilah Anda mendapatkan decoupling Anda. Presenter hanya memegang referensi untuk implementasi IView dan tidak terlalu peduli bagaimana itu diterapkan.

Ini juga merupakan implementasi orang miskin karena Anda memiliki referensi ke Presenter dalam tampilan dan objek ditetapkan melalui konstruktor. Dalam solusi yang lebih kuat, Anda mungkin ingin melihat wadah inversi kontrol (IOC) seperti Windsor, Ninject, dll. Yang akan menyelesaikan implementasi IView untuk Anda saat runtime sesuai permintaan dan dengan demikian membuatnya semakin terpisah.

Bil Simser
sumber
4

Saya pikir penting untuk diingat bahwa Pengendali / Presenter adalah tempat di mana tindakan tersebut benar-benar terjadi. Coupling di Controller tidak bisa dihindari karena kebutuhan.

Titik inti Pengontrol adalah agar jika Anda membuat perubahan pada Tampilan, maka Model tidak harus berubah dan sebaliknya (jika Model mengubah Tampilan tidak harus salah satu) karena Controller adalah apa yang menerjemahkan Model menjadi View dan kembali lagi. Tetapi Controller akan berubah ketika Model atau View berubah karena Anda harus secara efektif menerjemahkan dalam Controller bagaimana Model untuk Dilihat bagaimana mendapatkan perubahan yang dibuat dalam View kembali ke Mode.

Contoh terbaik yang bisa saya berikan adalah bahwa ketika saya menulis aplikasi MVC, saya tidak hanya dapat memiliki data dalam tampilan GUI, tetapi saya juga dapat menulis rutin yang mendorong data ditarik dari Model ke dalam stringuntuk ditampilkan di debugger (dan dengan ekstensi ke file teks biasa). Jika saya dapat mengambil data Model dan menerjemahkannya secara bebas ke dalam teks tanpa mengubah View atau Model dan hanya Controller, maka saya berada di jalur yang benar.

Yang sedang berkata, Anda harus memiliki referensi antara komponen yang berbeda untuk membuat semuanya berfungsi. Controller perlu tahu tentang View untuk mendorong data, View perlu tahu tentang Controller untuk memberi tahu ketika ada perubahan (seperti ketika Pengguna mengklik "Simpan" atau "Baru ..."). Pengendali perlu tahu tentang Model untuk menarik data, tetapi saya berpendapat bahwa Model tidak harus tahu tentang hal lain.

Peringatan: Saya berasal dari latar belakang Cocoa yang benar-benar Mac, Objective-C, yang benar-benar mendorong Anda ke dalam paradigma MVC baik Anda mau atau tidak.

Philip Regan
sumber
Ini jelas tujuan saya. Masalah utama saya adalah bagaimana mengatur View - apakah itu harus kelas dengan turunan dari setiap dialog, dan kemudian menggunakan View.Getters yang memanggil Dialog.Getters, atau jika Presenter harus dapat memanggil Dialog.Getters secara langsung ( ini kelihatannya terlalu erat, jadi mungkin tidak?)
trycatch
Saya pikir Presenter / Controller harus bertanggung jawab penuh atas Views, jadi yang terakhir. Sekali lagi, beberapa kopling pasti akan terjadi, tetapi setidaknya jika arah tanggung jawabnya jelas, maka pemeliharaan harus lebih mudah dalam jangka panjang.
Philip Regan
2
Saya tentu setuju P / C harus bertanggung jawab untuk View, tapi saya pikir bagian apa yang seharusnya membuat MVP kuat adalah kemampuan untuk menarik seluruh perpustakaan UI dan pasang yang baru dan dengan beberapa pemijatan (dllimporting dan yang lainnya) dapat menjalankan yang lain di tempatnya. Bukankah ini lebih sulit dengan Pengontrol / Presenter mengakses dialog secara langsung? Saya tentu saja tidak berusaha untuk berdebat, lebih jauh lagi memahami :)
trycatch
Saya pikir kekuatan sebenarnya datang dari dua arah: Yang pertama adalah bahwa View dan Model tidak ada hubungannya dengan yang lain, dan yang kedua bahwa sebagian besar pekerjaan pengembangan, mesin aplikasi, dilakukan dengan rapi unit, Pengendali. Tetapi beberapa pendarahan tanggung jawab pasti terjadi. Setidaknya sebagian besar pertukaran antarmuka akan dilakukan di Controller dan setiap tautan dari View akan minimal. Seperti yang orang lain katakan, beberapa pendarahan logika diharapkan dan diizinkan. MVC bukan peluru ajaib.
Philip Regan
Poin penting untuk decoupling adalah bahwa presenter HANYA mengakses tampilan melalui antarmuka yang terdefinisi dengan baik (perpustakaan UI independen), sehingga bagaimana perpustakaan UI dapat diganti untuk yang lain (yang lain yang akan mengimplementasikan antarmuka yang sama untuk formulir / jendela / dialog / halaman / kontrol / apa pun)
Marcel Toth
2

Secara umum, Anda ingin model Anda merangkum semua interaksi dengan model itu. Misalnya, tindakan CRUD Anda (Buat, Baca, Perbarui, Hapus) semua adalah bagian dari model. Hal yang sama berlaku untuk perhitungan khusus. Ada beberapa alasan bagus untuk ini:

  • Lebih mudah untuk mengotomatisasi pengujian Anda untuk kode ini
  • Itu menyimpan semua hal penting di satu tempat

Di controller (aplikasi MVC) Anda, yang Anda lakukan hanyalah mengumpulkan model yang perlu Anda gunakan dalam tampilan Anda, dan memanggil fungsi yang sesuai pada model. Setiap perubahan pada kondisi model terjadi di lapisan ini.

Tampilan Anda hanya menampilkan model yang Anda siapkan. Pada dasarnya, tampilan hanya membaca model dan menyesuaikan outputnya.

Memetakan prinsip umum ke kelas aktual

Ingat bahwa dialog Anda adalah tampilan. Jika Anda sudah memiliki kelas dialog, tidak ada alasan untuk membuat kelas "Tampilan" lainnya. Lapisan Presenter pada dasarnya mengikat model ke kontrol di View. Logika bisnis dan semua data penting disimpan dalam model.

Berin Loritsch
sumber