偶然間讀到歐萊禮的Java網路程式設計一書,在講解Thread時有提到當下的thread執行run
method時產生一編碼資料並存進陣列內,但是在run還沒有執行完的當下,另一區塊的程式
卻因為該陣列內尚沒有字串的資料而讀取使用導致NullPointerException,因此有應用到
Observer的技巧來巧妙的避開這個問題。
後來發現原來Java API的util有相關Observer、Observable可使用,因此在這邊也撰寫了一範例
來套用看看相關功能。
首先觀察者模式的建構,會定義被觀察者及觀察者,為一對多的關係,即被觀察者可能被多
個觀察者給觀察,當被觀察者呼叫setChanged()及notifyObservers(Object obj)後,各觀察者即
可以透過override的update(Observable o, Object arg) method來取得被觀察者當下的成員變數
資料(get)及傳送過來的Object obj的參數實體。
由此可以知道被觀察者並不知道有哪些觀察者的存在,他只要通知所有的觀察者及把他要
pass的訊息給發送出去,觀察者即可以利用這份資料來做下一步的處理。
緊接著,在這邊小弟將撰寫一個抽獎的範例來套用看看觀察者設計模式
一、定義被觀察者(Observable)
首先這個類別LotteryObservable需繼承Observable,並且定義LoadParticipantList方法來載入
抽獎者名單建立List<Participant> personList (Participant類別自行定義)
接著,processLottery方法為進行抽獎的過程,判斷當下的抽獎型態(定義enum)來顯示當下
要抽哪個獎項。再來透過亂數來取得index決定要抽出list下的Participant,並加入至awardMenu
內或轉換成字串assign至awardMsg內。
最後,呼叫setChanged及notifyObservers來知會觀察者!!
二、定義觀察者(多個)
觀察者需實作Observer並自行定義您的update方法!
1. AwardObserver 觀察者單純只是get被觀察者當下的得獎訊息來做發佈!
2. AwardStatisticsObserver 觀察者將取得被觀察者pass過來的argument來做進一步的分析得獎
名單。區分出各獎項各有男、女各有多少人等簡單分析。
三、定義主要類別(開辦活動)
這個類別會定義被觀察者將被哪些觀察者觀察,並且assign抽獎名單及該活動要抽出哪些
獎項及人數。
四、其他程式
定義獎項的enum及抽獎者
Demo畫面
最後,小弟在此只是簡單講述Observer模式的概念及藉由抽獎的議題來自行發揮Observer
模式,若概念講述有誤或範例舉的不貼切請不吝指教囉!
method時產生一編碼資料並存進陣列內,但是在run還沒有執行完的當下,另一區塊的程式
卻因為該陣列內尚沒有字串的資料而讀取使用導致NullPointerException,因此有應用到
Observer的技巧來巧妙的避開這個問題。
後來發現原來Java API的util有相關Observer、Observable可使用,因此在這邊也撰寫了一範例
來套用看看相關功能。
首先觀察者模式的建構,會定義被觀察者及觀察者,為一對多的關係,即被觀察者可能被多
個觀察者給觀察,當被觀察者呼叫setChanged()及notifyObservers(Object obj)後,各觀察者即
可以透過override的update(Observable o, Object arg) method來取得被觀察者當下的成員變數
資料(get)及傳送過來的Object obj的參數實體。
由此可以知道被觀察者並不知道有哪些觀察者的存在,他只要通知所有的觀察者及把他要
pass的訊息給發送出去,觀察者即可以利用這份資料來做下一步的處理。
緊接著,在這邊小弟將撰寫一個抽獎的範例來套用看看觀察者設計模式
一、定義被觀察者(Observable)
public class LotteryObservable extends Observable{
private List<Participant> personList;
private StringBuilder awardMsg;
public LotteryObservable(File f){
personList = LoadParticipantList(f);
}
public List<Participant> LoadParticipantList(File f){
List<Participant> plist = new ArrayList<Participant>();
try (BufferedReader br = new BufferedReader(new FileReader(f))){
String data = null;
System.out.println("參加抽獎的名單如下:");
while((data = br.readLine()) != null){
Participant p = new Participant(data);
System.out.println(p);
plist.add(p);
}
System.out.println("==============公布得獎名單==============");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.out.println("指定檔案不存在!!");
} catch(IOException e){
System.out.println("IOException");
}
return plist;
}
public void processLottery(int p_num, AwardType atype){
Random rmd = new Random();
List<Participant> awardMenu = new ArrayList<Participant>();
awardMsg = new StringBuilder();
if(AwardType.THIRD_AWARD == atype)awardMsg.append("抽出三獎得主:\n");
else if(AwardType.SPECIAL_AWAED == atype)awardMsg.append("抽出二獎得主:\n");
for(int i = 0 ; i < p_num ; i++){
int n = rmd.nextInt(personList.size()-1);
awardMsg.append(personList.get(n)+"\n");
awardMenu.add(personList.get(n));
personList.remove(n);
}
awardMsg.deleteCharAt(awardMsg.length()-1);
this.setChanged();
this.notifyObservers(awardMenu);
}
public StringBuilder getAwardMsg() {
return awardMsg;
}
}
首先這個類別LotteryObservable需繼承Observable,並且定義LoadParticipantList方法來載入
抽獎者名單建立List<Participant> personList (Participant類別自行定義)
接著,processLottery方法為進行抽獎的過程,判斷當下的抽獎型態(定義enum)來顯示當下
要抽哪個獎項。再來透過亂數來取得index決定要抽出list下的Participant,並加入至awardMenu
內或轉換成字串assign至awardMsg內。
最後,呼叫setChanged及notifyObservers來知會觀察者!!
二、定義觀察者(多個)
觀察者需實作Observer並自行定義您的update方法!
1. AwardObserver 觀察者單純只是get被觀察者當下的得獎訊息來做發佈!
public class AwardObserver implements Observer{
@Override
public void update(Observable o, Object arg) {
// TODO Auto-generated method stub
LotteryObservable lobs = (LotteryObservable)o;
System.out.println(this.getClass().getName()+"\n"+lobs.getAwardMsg().toString());
}
}
2. AwardStatisticsObserver 觀察者將取得被觀察者pass過來的argument來做進一步的分析得獎
名單。區分出各獎項各有男、女各有多少人等簡單分析。
public class AwardStatisticsObserver implements Observer{
@Override
public void update(Observable o, Object arg) {
// TODO Auto-generated method stub
@SuppressWarnings("unchecked")
List<Participant> awardMenu = (List<Participant>) arg;
Map<String, Integer> sex2num = new HashMap<String, Integer>();
int count;
for(Participant p : awardMenu){
if(!sex2num.containsKey(p.getSex())){
count = 1;
}else{
count = sex2num.get(p.getSex()) + 1;
}
sex2num.put(p.getSex(), count);
}
System.out.println(this.getClass().getName());
for(String sex : sex2num.keySet()){
System.out.println(sex + "性\t人數 => "+ sex2num.get(sex));
}
}
}
三、定義主要類別(開辦活動)
這個類別會定義被觀察者將被哪些觀察者觀察,並且assign抽獎名單及該活動要抽出哪些
獎項及人數。
public class Activity {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
LotteryObservable lo = new LotteryObservable(new File("C:/menu.txt"));
lo.addObserver(new AwardObserver());
lo.addObserver(new AwardStatisticsObserver());
lo.processLottery(4, AwardType.THIRD_AWARD);
System.out.println("============================");
lo.processLottery(3, AwardType.SECOND_AWARD);
}
}
四、其他程式
定義獎項的enum及抽獎者
public enum AwardType {
SPECIAL_AWAED, FIRST_AWARD, SECOND_AWARD, THIRD_AWARD
}
public class Participant {
private String name;
private String unit;
private String sex;
private int age;
public Participant(String resource) {
StringTokenizer st = new StringTokenizer(resource, ",");
this.unit = st.nextToken();
this.sex = st.nextToken();
this.age = Integer.parseInt(st.nextToken());
this.name = st.nextToken();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "["+unit+"]"+"=>"+sex+"\t"+age+"(age)\t"+name;
}
}
Demo畫面
最後,小弟在此只是簡單講述Observer模式的概念及藉由抽獎的議題來自行發揮Observer
模式,若概念講述有誤或範例舉的不貼切請不吝指教囉!
留言
張貼留言