觀察者模式
觀察者--OOD線程
簡單理解
起一個(gè)被觀察者線程和一個(gè)觀察者線程。觀察者不斷的循環(huán)檢查被觀察者狀態(tài)是否發(fā)生改變。此設(shè)計(jì)缺點(diǎn),因?yàn)樾枰粩嗟难h(huán)進(jìn)行監(jiān)視,所以耗CPU資源。
舉例場景
孩子在睡覺,醒后要吃東西,父親不斷的監(jiān)視者看孩子有沒有醒
代碼實(shí)現(xiàn)
child
package observersOOD;
/**
* 觀察者模式-OOD線程
* 起一個(gè)被觀察者線程和一個(gè)觀察者線程。觀察者不斷的循環(huán)檢查被觀察者狀態(tài)是否發(fā)生改變
* 案例:孩子在睡覺,醒后要吃東西,父親不斷的監(jiān)視者看孩子有沒有醒
*/
public class Child implements Runnable{
private boolean wakenUp = false;
public boolean isWakenUp() {
return wakenUp;
}
public void setWakenUp(boolean wakenUp) {
this.wakenUp = wakenUp;
}
public void wakeUp(){
System.out.println("孩子醒了....");
wakenUp = true;
}
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
wakeUp();
}
}
Dad類
package observersOOD;
public class Dad implements Runnable {
private Child child;
public Dad(Child child) {
this.child = child;
}
public void feed(Child child){
System.out.println("feed child......");
}
public void run() {
while (!child.isWakenUp()){
try {
System.out.println("我在監(jiān)視孩子。。。。");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
feed(this.child);
}
public static void main(String[] args) {
Child child = new Child();
Dad dad = new Dad(child);
Thread childThread = new Thread(child);
Thread dadThread = new Thread(dad);
childThread.start();
dadThread.start();
}
}
test
public static void main(String[] args) {
Child child = new Child();
Dad dad = new Dad(child);
Thread childThread = new Thread(child);
Thread dadThread = new Thread(dad);
childThread.start();
dadThread.start();
}
運(yùn)行結(jié)果
我在監(jiān)視孩子。。。。
我在監(jiān)視孩子。。。。
我在監(jiān)視孩子。。。。
我在監(jiān)視孩子。。。。
孩子醒了....
feed child......
改進(jìn)(上面由于父類需要主動(dòng)監(jiān)視孩子,需要通過不斷的循環(huán)實(shí)現(xiàn),太浪費(fèi)資源)。
改進(jìn)后有孩子醒后主動(dòng)通知父親。
class Child implements Runnable {
private Dad dad;
public Child(Dad dad) {
this.dad = dad;
}
public void wakeUp(){
dad.feed(this);
}
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
wakeUp();
}
}
class Dad {
void feed(Child c) {
System.out.println("feed child");
}
}
public class Test {
public static void main(String[] args) {
Dad d = new Dad();
Child c = new Child(d);
new Thread(c).start();
}
}
把孩子睡醒后封裝成事件
class WakenUpEvent{
private long time;
private String location;
private Child source;
public WakenUpEvent(long time, String location, Child source) {
super();
this.time = time;
this.location = location;
this.source = source;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public Child getSource() {
return source;
}
public void setSource(Child source) {
this.source = source;
}
}
class Child implements Runnable {
private Dad dad;
public Child(Dad dad) {
this.dad = dad;
}
public void wakeUp(){
dad.actionToWakenUp(new WakenUpEvent(System.currentTimeMillis(), "bed", this));
}
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
wakeUp();
}
}
class Dad {
public void actionToWakenUp(WakenUpEvent e) {
System.out.println(e.getTime());
System.out.println(e.getLocation());
System.out.println(e.getSource());
System.out.println("Fedd the child");
}
}
public class Test {
public static void main(String[] args) {
Dad d = new Dad();
Child c = new Child(d);
new Thread(c).start();
}
}
上面例子中child都會(huì)持有dad類,如果觀察者發(fā)生改變,需要修改代碼很不方便。
解決方法:動(dòng)態(tài)的注冊到被觀察者中。持有addListener方法
定義監(jiān)聽者接口
interface WakenUpListener {
public void actionToWakenUp(WakenUpEvent e);
}
定義實(shí)際監(jiān)聽者
class Dad implements WakenUpListener {
public void actionToWakenUp(WakenUpEvent e) {
System.out.println("Fedd the child");
}
}
class GrandFather implements WakenUpListener {
public void actionToWakenUp(WakenUpEvent e) {
System.out.println("抱孩子");
}
}
定義child,child中持有添加觀察者方法,當(dāng)醒后一一通知他的觀察者
package observersOOD.Listener;
import java.util.ArrayList;
import java.util.List;
public class Child implements Runnable{
public Child() {
}
private List<WakenUpListener> wakenUpListeners = new ArrayList<WakenUpListener>();
//提供一個(gè)注冊觀察者方法
public void addWakenUpListener(WakenUpListener wakenUpListener){
wakenUpListeners.add(wakenUpListener);
}
//提供一個(gè)通知方法
public void wakeUp(){
for (int i=0;i<wakenUpListeners.size();i++){
WakenUpEvent wakenUpEvent = new WakenUpEvent(111111L,"111111",this);
wakenUpListeners.get(i).actionToWakenUp(new WakenUpEvent(1111L,"1111",this));
}
}
public void run() {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
wakeUp();
}
}
定義Test類
package observersOOD.Listener;
public class Test {
public static void main(String[] args) {
WakenUpListener dad = new Dad();
WakenUpListener grandFather = new GrandFather();
Child child = new Child();
child.addWakenUpListener(dad);
child.addWakenUpListener(grandFather);
Thread thread = new Thread(child);
thread.start();
}
}
運(yùn)行結(jié)果
1111
1111
開始喂孩子....
抱孩子......