什么是觀察者模式
觀察者模式定義了對象之間的一對多關(guān)系,當(dāng)一個對象改變狀態(tài)時,它的所有依賴者都會收到通知并自動更新。
模擬現(xiàn)實中的一種需求
-
報社出版了人民日報潜索,
- A和B都在報社訂閱了人民日報
- 這個時候C也想訂一份人民日報臭增,報社說那好,你交錢我就給你訂上帮辟,以后我每期人民日報你都可以收到,C交了錢玩焰,訂了報紙由驹。
- 以后每天A、B昔园、C都會在門口拿到一份人民日報蔓榄。
- 后來B覺得看報紙沒什么意思,就跟報社說不想訂了默刚,你把錢退給我吧甥郑,報社說行,錢退給你荤西,訂閱名單里邊就沒有你了澜搅,以后就不給你報紙了。
- 只要報社正常經(jīng)營邪锌,會有越來越多的ABC勉躺。
實現(xiàn)方法
- 開始時A、B訂閱了報紙觅丰,代碼大概是這樣
public class 報社 {
String 報紙;
//報社本身屬性饵溅。。妇萄。蜕企。
//假設(shè)每次新出報紙會調(diào)用這個方法
public void onPublic(){
a.門口拿包裝紙(報紙);
b.門口拿包裝紙(報紙);
}
}
public class A {
public void 門口拿報紙(String 報紙){
System.out.println("看國家大事");
}
}
public class B {
public void 門口拿報紙(String 報紙){
System.out.println("看娛樂新聞!9诰洹轻掩!");
}
}
- C也想看報紙,所以接著寫
public class 報社 {
String 報紙;
//報社本身屬性懦底。放典。。基茵。
//假設(shè)每次新出報紙會調(diào)用這個方法
public void onPublic(){
a.門口拿包裝紙(報紙);
b.門口拿包裝紙(報紙);
c.門口拿包裝紙(報紙);
}
}
public class C {
public void 門口拿報紙(String 報紙){
System.out.println("看新發(fā)地菜價7芄埂!");
}
}
- B又不想訂報紙了拱层,那就把B注釋掉
public class 報社 {
String 報紙;
//報社本身屬性弥臼。。根灯。径缅。
//假設(shè)每次新出報紙會調(diào)用這個方法
public void onPublic(){
a.門口拿包裝紙(報紙);
//b.門口拿包裝紙(報紙);
c.門口拿包裝紙(報紙);
}
}
- 當(dāng)ABC越來越多的時候代碼就會成了這樣
public class 報社 {
String 報紙;
//報社本身屬性掺栅。。纳猪。氧卧。
//每次新出報紙會調(diào)用這個方法
public void onPublic(){
a.門口拿包裝紙(報紙);
// b.門口拿包裝紙(報紙);
c.門口拿包裝紙(報紙);
d.門口拿包裝紙(報紙);
e.門口拿包裝紙(報紙);
// f.門口拿包裝紙(報紙);
g.門口拿包裝紙(報紙);
h.門口拿包裝紙(報紙);
// i.門口拿包裝紙(報紙);
j.門口拿包裝紙(報紙);
k.門口拿包裝紙(報紙);
...
}
}
- 所以,正如你看到的
- 對于每一個想訂閱報紙的人氏堤,我們都要修改代碼
- 無法在運行的時候動態(tài)的增加或者刪除觀察者(訂閱報紙的人)
- 針對的是具體實現(xiàn)編程沙绝,并不是針對接口編程
- 沒有封裝改變的部分
OK,報社改個名稱叫Observable鼠锈,ABC改個名稱叫Observer闪檬,知道怎么訂報紙就知道什么是觀察者模式了。
明確三個關(guān)鍵詞购笆,Observable(被觀察者)粗悯、subscribe(訂閱)、Observer(觀察者)同欠。Observable(報社)是一個有各種狀態(tài)的對象样傍,Observer(A、B铺遂、C)subscribe(訂閱)了Observable(報社)
那就用觀察者模式來實現(xiàn)一下上面的需求铭乾,實現(xiàn)觀察者模式的方法不止一種,最常見的就是實現(xiàn)Observabe和Observer接口娃循,下面具體實現(xiàn)炕檩,對比看一下優(yōu)點。
觀察者模式接口實現(xiàn)
- Observable
public interface Observable {
void registerObserver(Observer o);
void removeObserver(Observer o);
//更新狀態(tài)的時候會掉用這個方法
void notifyObserver();
}
- Observer
public interface Observer {
void 門口看報紙(String 報紙);
}
- 接下來讓報社這個類實現(xiàn)Observable接口捌斧,ABC實現(xiàn)Observer接口
public class 報社 implements Observable {
private List<Observer> observers;
String 報紙;
public 報社() {
observers = new ArrayList<>();
}
@Override
public void notifyObserver() {
for(Observer o:observers){
o.門口看報紙(報紙);
}
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
int index = observers.indexOf(o);
if(index>0){
observers.remove(index);
}
}
}
public class A implements Observer{
@Override
public void 門口看報紙(String 報紙) {
System.out.println("看國家大事");
}
}
- 接下來就是訂報紙
public class Test {
public static void main(String[] args) {
報社 test = new 報社();
A a =new A();
B b = new B();
//a 添加訂閱
test.registerObserver(a);
//b 添加訂閱
test.registerObserver(b);
//c 添加訂閱
test.registerObserver(c);
//b 移除訂閱
test.removeObserver(b);
}
}
- 所以笛质,正如你看到的
- 對于每一個想訂閱報紙的人,我們不需要修改代碼捞蚂,只要實現(xiàn)了Observer接口后registerObserver就好了妇押。
- <del>無法</del><mark>隨意</mark>在運行的時候動態(tài)的增加或者刪除觀察者(訂閱報紙的人)
- 針對的是<del>具體實現(xiàn)</del><mark>接口</mark>編程,并不是針對<del>接口</del><mark>具體實現(xiàn)</mark>編程
- <del>沒有</del>封裝改變的部分
結(jié)束語
觀察者模式在JDK就有實現(xiàn)姓迅,只不過是用類實現(xiàn)的敲霍,需要繼承,眾所周知丁存,java是單繼承的肩杈,所以有一定的黑暗面。另外解寝,Java swing 中的JButton扩然、Android中給Button設(shè)置OnClickListenner以及現(xiàn)在非常火的RxJava都是觀察者模式的實現(xiàn)聋伦,有興趣的可以看看源碼夫偶,OK就到這里了界睁,歡迎指正。