Back to Posts

设计模式——观察者模式&朋友圈女神

Posted in DesignPattern

非竞态下的观察者

如果某个类中的一个状态经常会变化,并且每次变化都需要一些类对其做特殊的处理,就可以用状态模式;
《Head Fisrt》里面和各个论坛都喜欢用天气预报板块举例,个人不太理解那个场景;
这里用朋友圈的女神举例:女神自然有一堆关注者,每次她的状态一更新,其跟随者各种大开脑洞为博女神眼球。这里使用观察者模式实现;

package com.git.poan.designparttern;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

// 1. 将女神的状态发布抽象为接口
interface GoddessFollower {
    void post(PostType type);
}

// 2. 将女神的状态类型使用枚举列举处理
enum PostType {
    Image(1, "自拍"),
    Voice(2, "语音"),
    Video(3, "视频"),;

    private final int value;
    public final String content;

    PostType(int value, String content) {
        this.value = value;
        this.content = content;
    }
}

// 3. 女神的跟随者实现状态发布接口,并对`2`中的各种状态做对应处理
class Diaosi implements GoddessFollower {

    @Override
    public void post(PostType type) {
        switch (type) {
            case Image:
                System.out.println("Diaosi评价:好看!");
                break;
            case Video:
                System.out.println("Diaosi评价:好美!赞一个");
                break;
            case Voice:
                System.out.println("Diaosi评价:好听!赞一个");
        }
    }
}


// 3. 女神的跟随者实现状态发布接口,并对`2`中的各种状态做对应处理
class FuShuai implements GoddessFollower {

    @Override
    public void post(PostType type) {
        switch (type) {
            case Image:
                System.out.println("FuShuai评价:小姐姐有空么?");
                break;
            case Video:
                System.out.println("FuShuai评价:小姐姐有空么?");
                break;
            case Voice:
                System.out.println("FuShuai评价:小姐姐有空么?");
        }
    }
}

// 4. 由票圈PiaoCircle来统一调用  
public class PiaoCircle {

    private List<GoddessFollower> goddessesFollowers = new ArrayList<>();

    public void addFollower(GoddessFollower goddessesFollower) {
        goddessesFollowers.add(goddessesFollower);
    }

    public void remove(GoddessFollower goddessesFollower) {
        goddessesFollowers.remove(goddessesFollower);
    }

    public void timePass() {

        PostType[] values = PostType.values();
        Random random = new Random();
        int i = random.nextInt(values.length - 1);
        PostType postType = values[i];

        System.out.println("女神发表了" + postType.content);
        for (GoddessFollower goddessesFollower : goddessesFollowers) {
            goddessesFollower.post(postType);
        }
    }

    public static void main(String[] args) {
        PiaoCircle piaoCircle = new PiaoCircle();
        System.out.println("====================Diaosi关注了女神====================");
        Diaosi diaosi = new Diaosi();
        piaoCircle.addFollower(diaosi);
        piaoCircle.timePass();

        System.out.println("====================FuShuai关注了女神======================");
        piaoCircle.addFollower(new FuShuai());
        piaoCircle.timePass();

        System.out.println("====================Diaosi取消关注了女神====================");
        piaoCircle.remove(diaosi);
        piaoCircle.timePass();


    }

}
  1. 将女神的状态发布抽象为接口
  2. 将女神的状态类型使用枚举列举处理
  3. 女神的跟随者实现状态发布接口,并对2中的各种状态做对应处理
  4. 由票圈PiaoCircle来统一调用
  • 观察者模式这个套路,重点是要观察对象的“状态”,实现中并没有出现过“女神”,所以首先将状态变化抽象到接口的方法中,剩下的只要列举各种观察者需要对这个状态所做出的反应即可。

竞态下的观察者——CopyOnWriteArrayList

观察者模式,观察的对象状态变化非常频繁,可以将goddessesFollowers替换为CopyOnWriteArrayList,可以保证新关注者,不会由于竞态发生误操作;
在实际业务中,会比以上场景复杂,当观察的对象状态发生变化不会仅仅只是打印一句话,比如原本只有3个观察者需要对观察到的状态对自己业务做CRUD时,新来了一个观察者…… 那么这个观察者就会误操作……

其实以上的问题在于,新添加的观察者,不能看到被观察状态改变之前操作,这里可以用CopyOnWriteArrayList替换List实现~ 原因在于CopyOnWriteArrayList是一个在写操作时,会创建一个新的List,不干扰之前的结构,在写入过程中入如果有读操作,则会读取到之前的结构;这样就保证了竞态下观察者模式的线程安全问题。

你看那个人好像一条狗啊

Read Next

设计模式——策略模式与service接口