MVP
MVP: 将视图与模型解耦
维基百科将 MVP 称为
在
-
用户交互输入了某些内容
-
View 将用户输入转化为发送给Presenter -
Presenter 控制Model 接收需要改变的点 -
Model 将更新之后的值返回给Presenter -
Presenter 将更新之后的模型返回给View
根据上述流程,我们可知
- View、Presenter、
Model 中皆有ViewLogic 的部分实现 Presenter 负责连接View 与Model ,需要了解View 与Model 的细节。View 需要了解Presenter 的细节,将用户输入转化为事件传递给Presenter Model 需要了解Presenter 的细节,在完成更新之后将最新的模型传递给Presenter View 与Model 之间相互解耦合
Supervising Controller MVP
简化
iOS MVP
import UIKit
struct Person { // Model
let firstName: String
let lastName: String
}
protocol GreetingView: class {
func setGreeting(greeting: String)
}
protocol GreetingViewPresenter {
init(view: GreetingView, person: Person)
func showGreeting()
}
class GreetingPresenter : GreetingViewPresenter {
unowned let view: GreetingView
let person: Person
required init(view: GreetingView, person: Person) {
self.view = view
self.person = person
}
func showGreeting() {
let greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
self.view.setGreeting(greeting)
}
}
class GreetingViewController : UIViewController, GreetingView {
var presenter: GreetingViewPresenter!
let showGreetingButton = UIButton()
let greetingLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
}
func didTapButton(button: UIButton) {
self.presenter.showGreeting()
}
func setGreeting(greeting: String) {
self.greetingLabel.text = greeting
}
// layout code goes here
}
// Assembling of MVP
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
let presenter = GreetingPresenter(view: view, person: model)
view.presenter = presenter
Distribution: 主要的业务逻辑分割在了Presenter 与Model 中,View 相对呆板一点Testability: 较为方便地测试- 易用性
: 代码职责分割的更为明显,不过不像MVC 那样直观易懂了
Android
- 将
Presenter 与View 绑定,并且将用户响应事件绑定到Presenter 中
//Set up presenter
presenter = new MainPresenter();
presenter.attachView(this);
// ...
// Set up search button
searchButton = (ImageButton) findViewById(R.id.button_search);
searchButton.setOnClickListener(new View.OnClickListener () {
@Override
public void onClick(View v) {
presenter.loadRepositories(editTextUsername.getText().toString());
}
});
Presenter 中调用Model 更新数据,并且调用View 中进行重新渲染
public void loadRepositories(String usernameEntered) {
String username = usernameEntered.trim();
if (username.isEmpty()) return;
mainMvpView.showProgressIndicator();
if (subscription != null) subscription.unsubscribe();
ArchiApplication application = ArchiApplication.get(mainMvpView.getContext());
GithubService githubService = application.getGithubService();
subscription = githubService.publicRepositories(username)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(application.defaultSubscribeScheduler())
.subscribe(new Subscriber<List<Repository>>() {
@Override
public void onCompleted() {
Log.i(TAG, "Repos loaded " + repositories);
if (!repositories.isEmpty()) {
mainMvpView.showRepositories(repositories);
} else {
mainMvpView.showMessage(R.string.text_empty_repos);
}
}
@Override
public void onError(Throwable error) {
Log.e(TAG, "Error loading GitHub repos ", error);
if (isHttp404(error)) {
mainMvpView.showMessage(R.string.error_username_not_found);
} else {
mainMvpView.showMessage(R.string.error_loading_repos);
}
}
@Override
public void onNext(List<Repository> repositories) {
MainPresenter.this.repositories = repositories;
}
});
}