android设计模式二十三式(十五)——观察者模式(Observer)

观察者模式

观察者模式,适用于一对多的场合,即,当某一处发生了变化,需要通知多个地方去修改,在项目中是非常常用和常见的方法。它还被称为订阅发布模型,需要关注某个变化的,注册到相应的接口,当该变化发生时,即将变化发送通知到注册了的各个用户。

还是举个例子,有一个报社,每天早上都会给定了报纸的人家准时发送报纸。我们专业一点来分析一下:

1.报社可以接收新的家庭订报纸

2.报社将退订和到期的家庭去掉

3.每天报社要准时送报纸,这就是一个有消息的触发的机制

4.订报纸的家庭每天收到报纸

我们来看代码实现

/**
 * @author: hx
 * @Time: 2019/5/6
 * @Description: Family,所有订报纸的家庭都有的方法
 */
public interface Family {
    /**
     * 收到报纸
     */
    void receive(String newspaper);
}

/**
 * @author: hx
 * @Time: 2019/5/6
 * @Description: FamilyChen 老陈家
 */
public class FamilyChen implements Family {

    @Override
    public void receive(String newspaper) {
        System.out.println("Chen-收到今日的报纸:"+newspaper);
    }
}

/**
 * @author: hx
 * @Time: 2019/5/6
 * @Description: FamilyHu 老胡家
 */
public class FamilyHu implements Family {

    @Override
    public void receive(String newspaper) {
        System.out.println("Hu-收到今日的报纸:"+newspaper);
    }
}

/**
 * @author: hx
 * @Time: 2019/5/6
 * @Description: NewsPaperOffice 报社的基本方法
 */
public interface NewsPaperOffice {
    /**
     * 添加订报纸的家庭
     * @param family
     */
    void add(Family family);

    /**
     * 去掉到期或者退订的家庭
     * @param family
     */
    void del(Family family);

    /**
     * 送报纸
     */
    void send(String newspaper);
}

/**
 * @author: hx
 * @Time: 2019/5/6
 * @Description: MorningNewspaperOffice,一家早上送报纸的具体的报社
 */
public class MorningNewspaperOffice implements NewsPaperOffice {
    private List<Family> mFamilies = new ArrayList<>();

    @Override
    public void add(Family family) {
        mFamilies.add(family);
    }

    @Override
    public void del(Family family) {
        mFamilies.remove(family);
    }

    @Override
    public void send(String newspaper) {
        for (Family family : mFamilies){
            family.receive(newspaper);
        }
    }
}

public static void main(String[] args){
    NewsPaperOffice newsPaperOffice = new MorningNewspaperOffice();
    Family familyHu = new FamilyHu();
    Family familyChen = new FamilyChen();
    newsPaperOffice.add(familyHu);
    newsPaperOffice.add(familyChen);
    newsPaperOffice.send("今日头条:共产党万岁");
    newsPaperOffice.del(familyHu);
    newsPaperOffice.send("今日头条:新中国你好");
}

看一下运行日志

Hu-收到今日的报纸:今日头条:共产党万岁
Chen-收到今日的报纸:今日头条:共产党万岁
Chen-收到今日的报纸:今日头条:新中国你好

使用观察者模式,可以非常友好的将代码解耦,并且使业务流程变得十分清晰。

但是在实际的项目中,这样的观察者模式仅仅只完成了一半,还有另外一半是消息的产生和管理,然后依次对消息进行处理。

假设这家报社,是一家实时新闻报社,会不停的去获取到最新的消息并送到订阅了的家庭,那对消息就需要进行管理并处理。

我们写一个线程模拟一下,每5秒就产生一条新的消息,报社收到消息,放入到处理线程,然后再发送到各个家庭

新增一个消息产生的类News

/**
 * @author: hx
 * @Time: 2019/5/6
 * @Description: News
 */
public class News{
    private NewsListener mNewsListener;
    /**
     * 定时任务线程池
     */
    private ScheduledThreadPoolExecutor mScheduledThreadPoolExecutor;
    /**
     * 创建线程工厂
     */
    private ThreadFactory threadFactory;
    /**
     * 定时产生新闻的任务
     */
    private ScheduledFuture<?> newsScheduledFuture;

    private int i = 0;
    /**
     * 产生消息的线程
     */
    private Runnable makeNews = new Runnable() {
        @Override
        public void run() {
            String news = "第"+i+"条新闻";
            mNewsListener.sendNews(news);
            i++;
        }
    };

    public News(NewsListener newsListener) {
        mNewsListener = newsListener;
        threadFactory = new ThreadFactoryBuilder().setNameFormat("News_pool").build();
        mScheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(3, threadFactory);
        newsScheduledFuture = mScheduledThreadPoolExecutor.scheduleAtFixedRate(makeNews,0,5, TimeUnit.SECONDS);
    }
}

新增一个将消息发送出来的接口

/**
 * @author: hx
 * @Time: 2019/5/6
 * @Description: NewsListener
 */
public interface NewsListener {
    /**
     * 收到消息后处理的方法
     * @param news
     */
    void sendNews(String news);
}

新增一个消息管理的类继承Thread,实现消息监听接口,用于接收News发送出来的消息

/**
 * @author: hx
 * @Time: 2019/5/6
 * @Description: NewsManager
 */
public class NewsManager extends Thread implements NewsListener {
    /**
     * //存储消息的队列
     */
    private LinkedList<String> storage;

    private NewsPaperOffice mNewsPaperOffice;

    public NewsManager(NewsPaperOffice newsPaperOffice) {
        mNewsPaperOffice = newsPaperOffice;
        storage = new LinkedList<>();
        this.start();
    }

    @Override
    public void run() {
        while (true) {
            consumeNews();
        }
    }

    /**
     * 消费队列中的消息
     */
    private void consumeNews() {
        if (storage.isEmpty()) {
            sleepConsume();
        } else {
            String news = storage.getFirst();
            storage.poll();
            mNewsPaperOffice.send(news);
        }
    }

    /**
     * 队列内无处理事件,停止循环
     */
    private void sleepConsume() {
        synchronized (this) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 向队列里放入消息
     *
     * @param news
     */
    private void produceMsg(String news) {
        synchronized (storage) {
            storage.offer(news);
        }
        synchronized (this) {
            this.notify();
        }
    }

    @Override
    public void sendNews(String news) {
        produceMsg(news);
    }
}

修改一下调用的入口

public static void main(String[] args){
    NewsPaperOffice newsPaperOffice = new MorningNewspaperOffice();
    Family familyHu = new FamilyHu();
    Family familyChen = new FamilyChen();
    newsPaperOffice.add(familyHu);
    newsPaperOffice.add(familyChen);
    NewsManager newsManager = new NewsManager(newsPaperOffice);
    new News(newsManager);
}

输出结果如下

Hu-收到今日的报纸:第0条新闻
Chen-收到今日的报纸:第0条新闻
Hu-收到今日的报纸:第1条新闻
Chen-收到今日的报纸:第1条新闻
Hu-收到今日的报纸:第2条新闻
Chen-收到今日的报纸:第2条新闻
Hu-收到今日的报纸:第3条新闻
Chen-收到今日的报纸:第3条新闻
Hu-收到今日的报纸:第4条新闻
Chen-收到今日的报纸:第4条新闻

 在项目中基本都是这样来运用观察者模式,从消息的产生,消息的消费到消息的分发由NewsManager来管理,订阅和发布则由观察者模式组成的代码来管理,分别控制消息和订阅者,形成一个闭环系统。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值