您的位置:首页 > 编程语言 > Java开发

Java设计模式之观察者模式

2018-03-23 13:42 423 查看


Java设计模式,又称为java管理模式,一切的设计都只是为了更好地管理某些事物(使得代码不重复累赘)。

注:

(1)Vector 是在 java 中可以实现自动增长的对象数组。

(2)在面向对象的概念里推荐的一条就是面向接口编程,所以在实际使用的时候好的编程习惯就应该针对接口去写实现。例如:动物有:天上飞,陆上跑,水里游。

对于陆地上的动物,实现“路上跑”接口,但是两栖动物就是实现2个接口。

使用的时候就通过水里游或者路上跑这样的接口去调用实际的方法。

引用: IA a = new A();

强制类型转换:A = (A)a;

(3)attribute和property在英语里有什么区别?

Property在英语里有财产的含义,一般指对象的组成部分,可以是简单数据也可以是对象或对象集合. Attribute多指一个对象的特征,绝大的数情况下是一个描述性的数据。

(4)comment,计算机专业术语,表示 HTML 或 XML 文档中的注释节点 的内容。

(5)Map集合中的entry是什么?

Map是

关键代码(【one-to-many】 【一个主题对象-管理-多个观察者对象】的【创建和更新数据】 )

(1)观察者接口IObserver
public interface IObserver{
public void response(String data);  //更新一下通知的内容
}

(2)主题接口ISubject
public interface ISubject{
public void register(IObserver obr);  //注册观察者
public void unregister(IObserver obr); //注销观察者
public void notifyObservers();        //通知所有观察者
}

(3)主题实现类
public class Subject implements ISubject {
private Vector<IObserver> vec = new Vector();  //观察者对象的维护向量(一个可以自增的观察者数组)
private String data;     //主题的中心数据

public String getData(){return data;}
public String setData(String data){this.data = data;}

public void register(IObserver obr){
vec.add(obr);    //主题注册(添加)观察者
}

public void unregister(IObserver obr){
if(vec.contains(obr))
vec.remove(obr);  //主题注销(删除)观察者
}

public void notifyObservers(){
for(int i = 0; i < vec.size(); i++){
IObserver obr = vec.get(i);   //遍历一下观察者数组
obr.response(data);            //【主题通知所有观察者进行数据的接收和响应】
}
}
}

(4)一个具体观察者类Observer
public class Observer implements IObserver {
public void response(String data){
System.out.printIn("我接收到主题的数据:"+data);
}
}

(5)一个简单的测试类Test
public class Test{
public static void main (String[] args){
IObserver obr = new Observer();  //定义观察者对象
ISubject sub = new Subject();  //定义主题对象
sub.register(obr);             //主题添加观察者
sub.setData("hello");         //主题中心数据发生变动
sub.notifyObservers();        //通知所有观察者接收数据并进行数据响应
}
}


泛型接口

注意:上文的data是String类型的,若改为其他类型,则IObserver接口等相关代码都需要进行修改。其实,只要把ISubject、IObserver接口改为泛型接口就可以了。

这样参数T就必须是类类型,不能是基本数据类型,如不能是int,但可以是Integer。

改为:
(1)观察者泛型接口IObserver<T>
public interface IObserver<T>{
public void response(T data);
}
(2)主题泛型接口ISubject<T>
public interface ISubject<T>{
public void register(IObserver<T> obr);
public void unregister(IOservser<T> obr);
public void notifyObservsers();
}


“拉”数据【一个观察者对象 管理 一个主题对象】

注意:上文中观察者的data是主题对象直接“推”送给观察者的;但是

我们需要的是观察者自己主动去向主题对象“拉”数据。

改为:
(1)观察者【主动型】接口IObserver
public interface IObserver{
public void response(ISubject obj);  //【为了防止耦合,此处参数类型设为主题接口】
}
(2)主题接口ISubject
public interface ISubject{
public void register(IObserver obr);
public void unregister(IObserver obr);
public void notifyObservers();
}
(3)主题实现类
public class Subject implements ISubject {
//。。。
public void notifyObservers(){
for(int i = 0; i < vec.size(); i++){
IObserver obr = vec.get(i);
obr.response(this);        //【this指当前subject对象,代替原来的data】
}
}
}
(4)一个具体观察者类Observer
public class Observer implements IObserver {
public void response(ISubject obj){       //主题接口obj是通过【参数引用】的方式引用了主题对象
Subject subject = (Subject)obj;      //必须进行强制类型转换
System.out.printIn("我接收到主题的数据:"+subject.getData());
}
}


增加父类层AbstractSubject

假设有多个主题类,且每个主题类的方法都是相同的,那么为了不重复register、unregister和notifyObsertvers方法,

我们用父类层(封装了这三个方法)来解决代码重复问题。

改为:
(1)增加的父类层AbstractSubject【封装了这三个方法,然后子类Subject在继承了此类的同时也继承了这三个方法】
public class AbstractSubject implements ISubject{
Vector<IOserver> vec = new Vector();
public void register(IObserver obr){
vec.add(obr);
}
public void unregister(IObserver obr){
if(vec.contains(obr))
vec.remove(obr);
}
public void notifyObserver(){
for(int i=0; i < vec.size(); i++){
IObserver obr = vec.get(i);
obr.response(this);
}
}
}
(2)派生主题类Subject
public class Subject extends AbstractSubject{
private Integer data;
public Integer getData(return data;}
public void setData(Integer data){this.data = data;}
}


避免重复添加同一类型的观察者对象

IObserver  obr1 = new Observer();
IObserver  obr2 = new Observer();
Subject sub = new Subject();
sub.register(obr1);
sub.register(obr2);


其中obr1和obr2是同一类型的观察者对象,但它们的物理地址是不同的,

最重要的是Vector类的contains()方法默认是物理查询,因此它们仍然都添加到了vec中,怎么办?

解决办法是【我们需要一个【MARK标志常量】来标记各自的观察者类型】,以便通过逻辑运算符【==】来判断MARK是否相等。

改为:
(1)添加了【getMark()多态方法】的观察者接口IObserver
public interface IObserver{
public int getMark();
//...
}
(2)添加了【MARK标志常量】的观察者类
第一个类型的观察者类:
public class Observer1 implements IObserver{
private final int MARK = 1;

public int getMark(){return MARK;}

public boolean equals(Object arg0){
Observer obr = (Observer)arg0;
return  obr.getMark() == MARK;
}
//...
}

第二个类型的观察者类:
public class Observer2 implements IObserver{
private final int MARK = 2;

public int getMark(){return MARK;}

public boolean equals(Object arg0){
Observer obr = (Observer)arg0;
return  obr.getMark() == MARK;
}
//...
}


注解:为什么在观察者接口IObserver中必须添加一个多态方法getMark()?

因为register()方法的参数是IObserver类型,所以在IObserver中必须有一个getMark()方法来获取MARK值。

反射技术的应用

将观察者类信息封装在xml配置文件中,从而利用反射技术可以动态加载观察者对象。配置文件采用键值配对形式,值对应的是具体观察者的类名称。由于键是关键字,不能重复,为了编程方便,键采用“统一前缀+流水号”的形式,配置文件示例如下表:

说明:
键前缀“observer” 和 键流水号“1,2,3,...”
xml配置文件:
<?xml version="1.0" encoding = "utf-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Observer</comment>
<entry key="observer1">Observer1</entry>
<entry key="observer2">Observer2</entry>
</properties>

具体程序代码如下:
public interface IObserver{//同上文}

public interface ISubject{
public void register(String strXMLPath); //表明从配置文件加载观察者对象
public void unregister(IObserver obr);
public void notifyObservers();
}
//主体类Subject
public class Subject implements ISubject{
//其他代码同上文
public void register(String strXMLPath){
String prefix = "observer";
String observerClassName = null;
Properties p = Properties();
try{
FileInputStream in = new FileInputStream(strXMLPath);
p.loadFormXML(in);
int n = 1;
while((observerClassName = p.getProperty(prefix+n))!=null){
Constructor c = Class.forName(observerClassName).getConstructor();
IObserver obr = (IObserver)Class.forName(observerClassName).newInstance();
vec.add(obr);
n++;
}
in.close();
}catch(Exception e){
e.printStackTrace();
}
}
}

//一个具体观察者类Observer
public class Observer1 implements IObserver{//同上文}
public class Observer2 implements IObserver{//同上文}

//一个简单的测试类
public class Test{
public static void main(String[] args) throws Exception{
Subject sub = new Subject();         //定义主题对象
sub.register("d:/config/info5.xml"); //主题通过配置文件加载观察者对象
sub.setData("hello");                //主题数据变化了
sub.notifyObservers();               //通知各个观察者对象进行数据响应
}
}


JDK中的观察者设计模式

JDK的java.until包提供了系统的主题类Observable以及观察者接口Observer

应用探究

机房温度监测仿真功能。

分析:监测功能是以温度为中心的,因此用观察者设计模式实现程序架构是比较方便的。

总体思想是:温度作为主题类,两个观察者类,一个观察者负责记录数据,另一个观察者负责异常处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: