什么是观察者模式?
观察者模式是一种软件设计模式 ,可在对象之间建立一对多的依赖关系。 只要对象之一(“对象”或“可观察”)的状态发生变化,就会通知依赖于该对象的所有其他对象(“观察者”)。
让我们以订阅通过电子邮件从Envato Market接收报价的用户为例。 在这种情况下,用户是观察者。 Envato Market随时提供要约时,他们会通过电子邮件收到有关其的通知。 然后,每个用户都可以购买要约,或者决定那时他们可能对此并不真正感兴趣。 用户(观察者)还可以订阅从另一个电子商务市场接收的报价,并且稍后可能完全取消订阅从任何一个电子商务市场接收报价的操作。
此模式与“发布-订阅”模式非常相似。 主题或可观察对象向依赖的观察者发布了一个通知,甚至不知道有多少观察者订阅了该通知,或者他们是谁—观察对象只知道他们应该实现一个接口(我们很快就会谈到),而不必担心关于观察者可能执行的操作。
观察者模式的好处
- 该主题对其观察者知之甚少。 它唯一了解的是观察者实现或同意某个合同或接口。
- 可以重用主题,而无需让观察者参与,观察者也一样。
- 不会对主题进行任何修改以容纳新的观察者。 新的观察者只需要实现对象知道的接口,然后注册到对象即可。
- 观察者可以注册到其注册的多个主题。
所有这些好处使您可以轻松地在代码中的模块之间进行耦合,从而使您能够为应用程序构建灵活的设计。 在本文的其余部分中,我们将研究如何创建自己的Observer模式实现,还将使用内置的Java Observer / Observable API以及可提供此类功能的第三方库。 。
建立我们自己的观察者模式
1.创建主题界面
我们首先定义主题(可观察对象)将要实现的接口。
public interface Subject {
void registerObserver(RepositoryObserver repositoryObserver);
void removeObserver(RepositoryObserver repositoryObserver);
void notifyObservers();
}
在上面的代码中,我们使用三种方法创建了Java接口 。 就像第一个方法所说的registerObserver()
,它将为主题注册一个RepositoryObserver
类型的观察者(我们将很快创建该接口)。 将调用removeObserver()
来删除希望停止从主题获取通知的观察者,最后,只要发生更改, notifyObserver()
就会向所有观察者发送广播。 现在,让我们创建一个具体的主题类,该类将实现我们创建的主题接口:
import android.os.Handler;
import java.util.ArrayList;
public class UserDataRepository implements Subject {
private String mFullName;
private int mAge;
private static UserDataRepository INSTANCE = null;
private ArrayList<RepositoryObserver> mObservers;
private UserDataRepository() {
mObservers = new ArrayList<>();
getNewDataFromRemote();
}
// Simulate network
private void getNewDataFromRemote() {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
setUserData("Chike Mgbemena", 101);
}
}, 10000);
}
// Creates a Singleton of the class
public static UserDataRepository getInstance() {
if(INSTANCE == null) {
INSTANCE = new UserDataRepository();
}
return INSTANCE;
}
@Override
public void registerObserver(RepositoryObserver repositoryObserver) {
if(!mObservers.contains(repositoryObserver)) {
mObservers.add(repositoryObserver);
}
}
@Override
public void removeObserver(RepositoryObserver repositoryObserver) {
if(mObservers.contains(repositoryObserver)) {
mObservers.remove(repositoryObserver);
}
}
@Override
public void notifyObservers() {
for (RepositoryObserver observer: mObservers) {
observer.onUserDataChanged(mFullName, mAge);
}
}
public void setUserData(String fullName, int age) {
mFullName = fullName;
mAge = age;
notifyObservers();
}
}
上面的类实现了Subject
接口。 我们有一个ArrayList
来容纳观察者,然后在私有构造函数中创建它。 观察者通过添加到ArrayList
注册,同样,通过将其从ArrayList
中移除来注销。
请注意,我们正在模拟一个网络请求以检索新数据。 调用setUserData()
方法并为其提供全名和年龄的新值后,我们将调用notifyObservers()
方法,该方法将新数据更改通知或发送给所有注册的观察者。 全名和年龄的新值也将传递。 该主题可以有多个观察者,但是在本教程中,我们将只创建一个观察者。 但首先,让我们创建观察者接口。
2.创建观察者界面
public interface RepositoryObserver {
void onUserDataChanged(String fullname, int age);
}
在上面的代码中,我们创建了观察者接口,具体的观察者应该实现此接口。 这使我们的代码更加灵活,因为我们是在编码接口而不是具体的实现。 一个具体的Subject
类不需要知道它可能拥有的许多具体的观察者。 关于它们的所有知识仅是它们实现了RepositoryObserver
接口。
现在让我们创建一个实现该接口的具体类。
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class UserProfileActivity extends AppCompatActivity implements RepositoryObserver {
private Subject mUserDataRepository;
private TextView mTextViewUserFullName;
private TextView mTextViewUserAge;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_profile);
mTextViewUserAge = (TextView) findViewById(R.id.tv_age);
mTextViewUserFullName = (TextView) findViewById(R.id.tv_fullname);
mUserDataRepository = UserDataRepository.getInstance();
mUserDataRepository.registerObserver(this);
}
@Override
public void onUserDataChanged(String fullname, int age) {
mTextViewUserFullName.setText(fullname);
mTextViewUserAge.setText(age);
}
@Override
protected void onDestroy() {
super.onDestroy();
mUserDataRepository.removeObserver(this);
}
}
上面代码中首先要注意的是UserProfileActivity
实现了RepositoryObserver
接口,因此它必须实现onUserDataChanged()
方法。 在Activity的onCreate()
方法中,我们获得了UserDataRepository
一个实例,然后对其进行初始化,最后将该观察者注册到该实例。
在onDestroy()
方法中,我们要停止获取通知,因此我们取消了接收通知的注册。
在onUserDataChanged()
方法中,我们要使用新的数据值集更新TextView
部件mTextViewUserFullName
和mTextViewUserAge
。
现在,我们只有一个观察者类,但是对于我们来说,创建想要成为UserDataRepository
类的观察者的其他类是可能且容易的。 例如,我们可以很容易地拥有一个SettingsActivity
,它也希望通过成为观察者来获得有关用户数据更改的通知。
推拉模型
在上面的示例中,我们使用观察者模式的推送模型。 在此模型中,主题通过传递已更改的数据将更改通知给观察者。 但是在拉模型中,主题仍然会通知观察者,但实际上并不会传递已更改的数据。 观察者一旦收到通知,便提取所需的数据。
利用Java的内置Observer API
到目前为止,我们已经创建了自己的Observer模式实现,但是Java在其API中内置了Observer / Observable支持。 在本节中,我们将使用它。 如您所见,该API简化了某些实现。
1.创建可观察的
现在,我们的UserDataRepository
它是我们的主题或可观察的对象)将扩展java.util.Observable
超类,使其成为Observable 。 这是一个类,希望被一个或多个观察者观察。
import android.os.Handler;
import java.util.Observable;
public class UserDataRepository extends Observable {
private String mFullName;
private int mAge;
private static UserDataRepository INSTANCE = null;
private UserDataRepository() {
getNewDataFromRemote();
}
// Returns a single instance of this class, creating it if necessary.
public static UserDataRepository getInstance() {
if(INSTANCE == null) {
INSTANCE = new UserDataRepository();
}
return INSTANCE;
}
// Simulate network
private void getNewDataFromRemote() {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
setUserData("Mgbemena Chike", 102);
}
}, 10000);
}
public void setUserData(String fullName, int age) {
mFullName = fullName;
mAge = age;
setChanged();
notifyObservers();
}
public String getFullName() {
return mFullName;
}
public int getAge() {
return mAge;
}
}
现在,我们已经重构了UserDataRepository
类以使用Java Observable API,让我们看看与以前的版本相比有什么变化。 首先要注意的是,我们正在扩展一个超类(这意味着该类不能扩展任何其他类),并且没有像上一节中那样实现接口。
我们不再持有观察者的ArrayList
; 这是在超级类中处理的。 同样,我们不必担心观察者的注册,删除或通知java.util.Observable
正在为我们处理所有这些。
另一个区别是在本课程中,我们采用拉式样式。 我们用notifyObservers()
通知观察者发生了更改,但是观察者将需要使用我们在此类中定义的字段获取器来提取数据。 如果要改用推送样式,则可以使用方法notifyObservers(Object arg)
并将更改后的数据传递给object参数中的观察者。
超类的setChanged()
方法将标志设置为true,指示数据已更改。 然后,您可以调用notifyObservers()
方法。 请注意,如果在调用notifyObsevers()
之前不调用setChanged()
notifyObsevers()
,则不会通知观察者。 您可以使用该方法检查此标志的值hasChanged()
并清除回假clearChanged()
现在我们已经创建了可观察类,下面让我们看看如何设置观察者。
2.创建观察者
我们的UserDataRepository
可观察类需要一个对应的Observer才有用,因此让我们重构UserProfileActivity
以实现java.util.Observer
接口。
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import com.chikeandroid.tutsplusobserverpattern.R;
import java.util.Observable;
import java.util.Observer;
public class UserProfileActivity extends AppCompatActivity implements Observer {
private Observable mUserDataRepositoryObservable;
private TextView mTextViewUserFullName;
private TextView mTextViewUserAge;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_profile);
mTextViewUserAge = (TextView) findViewById(R.id.tv_age);
mTextViewUserFullName = (TextView) findViewById(R.id.tv_fullname);
mUserDataRepositoryObservable = UserDataRepository.getInstance();
mUserDataRepositoryObservable.addObserver(this);
}
@Override
public void update(Observable observable, Object o) {
if (observable instanceof UserDataRepository) {
UserDataRepository userDataRepository = (UserDataRepository)observable;
mTextViewUserAge.setText(String.valueOf(userDataRepository.getAge()));
mTextViewUserFullName.setText(userDataRepository.getFullName());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mUserDataRepositoryObservable.deleteObserver(this);
}
}
在onCreate()
方法中,通过使用java.util.Observable
超类中的addObserver()
onCreate()
方法,将此类作为观察者添加到可观察的UserDataRepository
java.util.Observable
。
在观察者必须实现的update()
方法中,我们检查作为参数接收的Observable
是否是UserDataRepository
的实例(请注意,观察者可以预订不同的Observable
),然后将其强制转换为该实例并检索我们想要使用字段获取器的值。 然后,我们使用这些值来更新视图小部件。
当活动被销毁时,我们不需要从可观察对象获取任何更新,因此我们只需通过调用deleteObserver()
方法从观察者列表中删除活动deleteObserver()
。
图书馆实施观察者模式
结论
在本教程中,您学习了Java中的Observer模式:它是什么,使用它的好处,如何使用Java Observer API来实现自己的观察器,以及一些用于实现此模式的第三方库。
翻译自: https://code.tutsplus.com/tutorials/android-design-patterns-the-observer-pattern--cms-28963