前言
在设计模式演化之观察者模式1
中,我们实现了一个经典的观察者模式,下边继续对观察者模式进行优化,以便于能够在各种场景下更好的工作。
优化
1.我们在Subject中新增多个属性。然后一下Subject类的名字,使它更符合业务场景。现在我们要观察一个User类多个属性的变化。
public class User {
private String name;
private int age;
private String address;
}
此时,你会发现按照前面观察者模式的写法麻烦就来了。之前,为了能让多个对象观察name属性的变化,我们专门定义个了一个Observer接口,并提供了它的实现类。如果新增的属性也按照这个模式去定义,那观察这个类的代价就太大了。显然经典模式的写法,不足以应对这种观察多个属性的情况。
你可能会想,能不能将这些被观察的属性也抽象出一个公共的接口Observable(注意不是Observer),让被观察的属性类实现这个接口来表示它是一个可观察对象呢?很遗憾,答案是否定的。
- 首先,抽象之后会造成属性的类型丢失,这对观察它变化的类来说是不可行的。抽象之后所有的属性都变成了Observable类型了。
- 要让被观察属性实现Observable接口,就需要去改这些属性类的源码,没有源码自然就改不了了。如第三库中的类。
- 对于基本类型和String类型,我们无法让它们去实现这个抽象出来的接口。
此时,我们也可以得出观察多个属性时需要的基本要求
- 被观察属性必须具备统一的通知观察者刷新的方法——onChange()。这样可以使得观察者只需要Observer接口就可以,观察该对象
- 被观察的属性不能丢时类型,且通知观察者刷新时,必须在onChange()中传递准确的类型
- 不得改动被观察属性类的源码
既然修改被观察属性的类源码行不通,那我们就考虑给它们包装一层,并在包装类里去调用onChange()方法,这样它们就可以具有统一的行为。然后为了保证属性类型不丢失,我们可以使用泛型来保存实际被观察的属性类型,当调用onChange()时,将实际类型传递出去。这里就要求onChange()方法也具备接收泛型的能力,因此还需要对Observer也做出相应的修改。代码如下
public class Observable<T> {
private T value;
private final List<Observer<T>> observers = new ArrayList<>();
// 改一下方法名,使含义更突出。实际就是注册观察者的功能
public void observe(Observer<T> observer) {
observers.add(observer);
}
public void unregisterObserver(Observer<T> observer) {
observers.remove(observer);
}
public void setValue(T value) {
this.value = value;
for (Observer<T> observer: observers) {
observer.onChange(value);
}
}
}
public interface Observer<T> {
void onChange(T t);
}
现在,我们可以重新定义User类中的属性了。
public class User {
public Observable<String> name;
public Observable<Integer> age;
public Observable<String> address;
}
回想前面在观察多个对象时提出三条基本要求,这种做法已经满足要求了。
2.在上一步骤中,我们完成了对被观察属性的定义,下一步就是使用它们了。再定义UserManager,使它观察User中的各个数据变化,然后做出对应的操作。此时你会发现,麻烦还是有的。虽然我们统一了各个属性的观察方式,即实现Observer< T> 接口,但还是需要定义各个具体的实现类,而且UserManager与这些实现类的交互也非常麻烦。所以我们再改一种写法,将具体实现类在UserManager中以匿名内部类的形式实现。UserManager就可以在处理更多业务的同时,还具备了观察User属性的功能,代码如下:
public class UserManager {
User user = new User();
private void initObserver() {
user.name.observe(new Observer<String>() {
@Override
public void onChange(String s) {
System.out.println(s);
}
});
user.age.observe(new Observer<Integer>() {
@Override
public void onChange(Integer integer) {
System.out.println(integer);
}
});
user.address.observe(new Observer<String>() {
@Override
public void onChange(String s) {
System.out.println(s);
}
});
}
}
在Java 1.8 之后,我们还可以利用Lambda表达式来进一步优化上述代码。
public class UserManager {
User user = new User();
private void initObserver() {
user.name.observe(s -> {
// do many things
System.out.println(s);
});
user.age.observe(integer -> {
// do many things
System.out.println(integer);
});
user.address.observe(s -> {
// do many things
System.out.println(s);
});
}
}
代码量得到了极大的简化。不过这么写依然会存在一点小问题。我们这里对于变化的结果只是调用了System.out.println把它输出而已。真正的业务场景,这里显然会比较复杂,可能会有二三十行代码。当可观察变量增加时,这些变化后的处理逻辑会混合在一起,难以阅读,且会导致initObserver()方法的行数爆表。因此还需要做一点小小的改动。
public class UserManager {
User user = new User();
private void initObserver() {
user.name.observe(this::onNameChange);
user.age.observe(this::onAgeChange);
user.address.observe(this::onAddressChange);
}
private void onNameChange(String name) {
// do many things
System.out.println(name);
}
private void onAgeChange(int age) {
// do many things
System.out.println(age);
}
private void onAddressChange(String address) {
// do many things
System.out.println(address);
}
}
虽然看上去多定义三个方法,但是这样把三者的逻辑解开了,而且避免了initObserve()中代码过多的问题。以后新增可观察对象时,也更加容易扩展。因此这时多写一些代码是完全值得的。
总结:最终,我们通过新增Observable< T> 和Observer< T> 两个文件。就可以很方便的在任何对象之间建立观察者关系。这也是RxJava与Android中LiveData对于观察者模式的应用,当然这里只是简单的模仿实现。