轉自http://www.eoeandroid.com/home.php?mod=space&uid=871316&do=blog&id=48239
最近在學習下設計模式,而加深學習的不錯的方法就是把心得寫出來吧。記錄下自己的了解。現在自己看的書是《head.frist設計模式》這本書。比較不錯,想學習設計模式的朋友可以看下這本書。
觀察者<observer>模式(有時又被稱為釋出-訂閱<publish/subscribe>模式、模型-視圖<model/view>模式、源-收聽者<source/listener>模式或從屬者<dependents>模式)是軟體設計模式的一種。在此種模式中,一個目标物件管理所有相依于它的觀察者物件,并且在它本身的狀态改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實作。此種模式通常被用來實作事件處理系統。(源自百度百科)
(五月三十一号修正内容)
看下結構圖:
開始一個小例子
observer接口:
[java]
view plaincopy
public interface isubscribe {
void getnewpaper();
}
實作observer接口的觀察者:
個人訂閱者:
public class personalsubscriber implements isubscribe {
private string strname;
public void setnewspapername(string strname){
this.strname = strname;
}
public string getnewspapername(){
return strname;
@override
public void getnewpaper() {
// todo auto-generated method stub
system.out.println("我是個人使用者,我得到了我的報紙:"+getnewspapername());
企業訂閱者:
public class enterprisesubscriber implements isubscribe {
system.out.println("我是企業使用者,我得到了我的報紙:"+getnewspapername());
被觀察者:subject
public abstract class publish {
public list<isubscribe> list;
public publish(){
list = new arraylist();
public void registered(isubscribe isubscribe){
list.add(isubscribe);
public void unregistered(isubscribe isubscribe){
list.remove(isubscribe);
public abstract void sendnewspaper();
這裡使用的是抽象類
下面是實作:
public class postoffice extends publish{
public void sendnewspaper() {
iterator iterator = list.iterator();
while(iterator.hasnext()){
((isubscribe) iterator.next()).getnewpaper();
}
測試:
public static void main(string[] args) {
postoffice postoffice = new postoffice();
//得到個人使用者
personalsubscriber person = new personalsubscriber();
person.setnewspapername("《南方周末》");
//得到企業使用者
enterprisesubscriber enterprise = new enterprisesubscriber();
enterprise.setnewspapername("《企業報》");
//注冊觀察者
postoffice.registered(person);
postoffice.registered(enterprise);
//發放報紙
postoffice.sendnewspaper();
測試結果:
我是個人使用者,我得到了我的報紙:《南方周末》
我是企業使用者,我得到了我的報紙:《企業報》
下面是有些錯誤五月18号版本:後面有錯誤解析!
舉個例子,張三從郵局訂閱了《南方周末》,李四從郵局訂閱了《新京報》,王五從郵局裡面訂閱了《南方都市報》。當報紙抵達郵局的時候,郵局就會把報紙送遞訂閱者。而不需要訂閱者天天到郵局詢問報紙是否到達郵局。
下面開始代碼:首先應該是個訂閱者接口:
package cn.demo;
//從郵局訂閱報紙
public void registered(postoffice postoffice);
//從郵局退訂報紙
public void unregistered(postoffice postoffice);
//擷取報紙名稱
public void getnewspaper();
第三個方法非必須,前兩個方法必須要有。(應修改為前兩個非必須,第三個方法必須有)
然後有三個訂閱者類:
張三:
public class zhangsan implements isubscribe{
private string mname;
private string mnewspapername;
public zhangsan(string mname, string mnewspapername) {
this.mname = mname;
this.mnewspapername = mnewspapername;
public string getname() {
return mname;
public string getnewspapername() {
return mnewspapername;
public void registered(postoffice postoffice){
postoffice.registerednewspaper(this);
public void unregistered(postoffice postoffice){
postoffice.unregisterednewspaper(this);
public void getnewspaper() {
system.out.println("我是"+getname()+",我收到我訂閱的"+getnewspapername());
李四:
public class lisi implements isubscribe{
public lisi(string mname, string mnewspapername) {
王五:
public class wangwu implements isubscribe{
public wangwu(string mname, string mnewspapername) {
三個訂閱者,都有方法訂閱報紙,或者取消訂閱。其實關于訂閱者的資訊,是存儲在郵局類裡面。
郵局類:(郵局類應該繼承一個抽象類或者接口(subject),這裡沒有實作)
import java.util.arraylist;
import java.util.list;
public class postoffice {
private list<isubscribe> subscribelist = new arraylist<isubscribe>();
public void registerednewspaper(isubscribe subscribe) {
subscribelist.add(subscribe);
public void unregisterednewspaper(isubscribe subscribe) {
if (subscribe != null) {
subscribelist.remove(subscribe);
public void getnewspaper(boolean bool) {
if (bool) {
sendnewspaper();
for(isubscribe subscribe : subscribelist) {
subscribe.getnewspaper();
在這個類裡面,有變化通知是使用的sendnewspaper()這個方法,周遊所有的訂閱者。
測試類:
postoffice mpostoffice = new postoffice();
isubscribe zhangsan = new zhangsan("張三", "《南方周末》");
isubscribe lisi = new lisi("李四", "《新京報》");
isubscribe wangwu = new wangwu("王五", "《南方都市報》");
//開始訂閱報紙
zhangsan.registered(mpostoffice);
lisi.registered(mpostoffice);
wangwu.registered(mpostoffice);
//郵局收到報紙,開始發放
mpostoffice.getnewspaper(true);
//李四退訂
lisi.unregistered(mpostoffice);
列印結果:
[html]
我是張三,我收到我訂閱的《南方周末》
我是李四,我收到我訂閱的《新京報》
我是王五,我收到我訂閱的《南方都市報》
這裡隻是提供了一個簡單的例子,而且代碼你會發現有備援,三個訂閱者類,幾乎是一樣,因為沒有在類裡面添加他們獨自的屬性,可以用一個person類來替代。這裡寫這三個類是為了顯示清晰。
關于五月18号的例子,觀察者沒必要有注冊和取消注冊的方法。他們的方法的實作也是調用的被觀察者的注冊和取消注冊,不如直接使用被觀察者的方法。
在android中,button.setonclicklistener()這個方式是比較常見的觀察者模式:當然,衆所周知,onclick是著名的回調方法,在這裡不會研究回調,不用太在意。
看下代碼:
button button1 = (button)findviewbyid(r.id.button1);
button button2 = (button)findviewbyid(r.id.button2);
button button3 = (button)findviewbyid(r.id.button3);
button1.setonclicklistener(this);
button2.setonclicklistener(this);
button3.setonclicklistener(this);
public void onclick(view v) {
switch(v.getid()) {
case r.id.button1 :
// do some thing
case r.id.button2 :
// do some thing
case r.id.button3 :
先button.setonclicklistener()進行注冊。相當于郵局中的lisi.registered(mpostoffice),此處錯誤,應該是第一例中的postoffice.registered(person)。onclick響應事件相當于郵局中的
public void sendnewspaper() {
這個中的subscribe.getnewspaper()這個方法。view.onclicklistener相當于郵局。view也就是button是我們的訂閱者,也就相當于張三。我們也可以在getnewspaper中加一些自己的switch判斷。
最後,如果我們的郵局要實作類似button這樣的回調這麼實作呢?
isubscribe isubscribe;
isubscribe.getnewspaper();
}