查看原文
其他

设计模式是什么鬼(观察者)

凸凹里歐 Java知音 2019-06-23

//本文作者:凸凹里歐

//本文收录菜单栏:《设计模式是什么鬼》专栏中


眼观六路,耳听八方,观察者很忙,随时监控着关注对象的一举一动。记者、摄影师、重症监护的护士,被套的股民,无不为了完成任务疲于奔命,而观察者模式正是为了解决这个问题而诞生。



观察者的痛点到底在哪里呢?让我们用购物来做例程,假设某件商品(水果手机)卖得非常火爆,长期处于脱销的状态。由于供不应求,师徒四人也加入了抢购的队伍,不时的去商店询问是否有货,先看商店类代码。


1public class Shop {
2    private String product;//商品
3    //初始商店无货
4    public Shop() {
5        this.product = "无商品";
6    }
7    //商店出货
8    public String getProduct() {
9        return product;
10    }
11    //商店进货
12    public void setProduct(String product) {
13        this.product = product;
14    }
15}


简单易懂,然后是买家类,充当观察者角色。


1public class Buyer {// 买家
2    private String name;// 买家姓名
3    private Shop shop;// 商店引用
4
5    public Buyer(String name, Shop shop) {
6        this.name = name;
7        this.shop = shop;
8    }
9
10    public void buy() {// 买家购买商品
11        System.out.print(name + "购买:");
12        System.out.println(shop.getProduct());
13    }
14}


注意第3行买家持有商店的引用,用来在第10行的购买行为中获取商品,最后是客户端类来模拟买家与商家的互动。


1public class Client {
2    public static void main(String[] args) {
3        Shop shop = new Shop();
4        Buyer wukong = new Buyer("悟空", shop);
5        Buyer shaseng = new Buyer("沙僧", shop);
6        Buyer bajie = new Buyer("八戒", shop);
7
8        wukong.buy();// 悟空购买:无商品
9        bajie.buy();// 八戒购买:无商品
10        shaseng.buy();// 沙僧购买:无商品
11        bajie.buy();// 八戒购买:无商品
12
13        // 师傅忍不住了,也加入了购买行列。
14        Buyer tangseng = new Buyer("唐僧", shop);
15        tangseng.buy();// 唐僧购买:无商品
16
17        // 除了八戒其他人都放弃了
18        bajie.buy();// 八戒购买:无商品
19        bajie.buy();// 八戒购买:无商品
20
21        // 商店终于进货了
22        shop.setProduct("最新旗舰手机");
23        bajie.buy();// 八戒购买:最新旗舰手机
24    }
25}


看到这些买家的疯狂行为没有?一开始师傅命三位徒弟去抢购,商店一直处于无货状态,师傅坐立难安,也加入了抢购大军,最终徒儿刚鬣脱颖而出,终于抢到了梦寐以求的手机,整个过程堪比九九八十一难。




大家有没有发现问题?除了最后一步目的达成之外,之前的部分都是在做无用功,并且此处代码只是模拟了师徒四人而已,真实情况并非如此简单,可能会有成千上万的疯狂粉丝不断询问有没有到货,商家的店员可能会被逼疯。


到这里大家肯定已经想到了,与其让观察者不断的询问不如当到货的时候让商家主动通知观察者们来买吧,换个角度分析问题马上迎刃而解,醍醐灌顶般清爽,开始设计优雅观察者的模式,首先从商家类开始重构。


1public class Shop {
2
3    private String product;
4    private List<Buyer> buyers;// 持有买家的引用
5
6    public Shop() {
7        this.product = "无商品";
8        this.buyers = new ArrayList<>();
9    }
10
11    // 为了主动通知买家,买家得来店里注册。
12    public void register(Buyer buyer) {
13        this.buyers.add(buyer);
14    }
15
16    public String getProduct() {
17        return product;
18    }
19
20    public void setProduct(String product) {
21        this.product = product;// 到货了
22        notifyBuyers();// 到货后通知买家
23    }
24
25    // 通知所有注册买家
26    public void notifyBuyers() {
27        buyers.stream().forEach(b -> b.inform());
28    }
29}


注意第12行的注册方法,所有关注商品的买家都应先注册(订阅),比如告知商家手机号以便第20行到货后可以接到通知,以及第26行的通知方法对所有买家进行迭代,并调用买家的inform进行告知。所以这里我们规定,对于Buyer买家必须要有inform方法,这是对各类形形色色买家的定制行为,故我们对买家类进行抽象如下。


1public abstract class Buyer {
2    protected String name;
3    protected Shop shop;
4
5    public Buyer(String name, Shop shop) {
6        this.name = name;
7        this.shop = shop;
8//        shop.register(this);
9    }
10
11    public abstract void inform();
12
13}


很简单,我们对买家进行了抽象,其中第11行inform方法必须得到实现,通知到你了怎样处理自己看着办咯。注意第8行注掉的代码,构造时强制将自己注册入商家名单,但为了灵活起见我们暂让买家自行决定是否注册。接下来我们来看众买家都是些什么样的人,首先是果粉买家。


1public class PhoneFans extends Buyer {
2
3    public PhoneFans(String name, Shop shop) {
4        super(name, shop);//调用父类进行构造
5    }
6
7    @Override
8    public void inform() {
9        String product = shop.getProduct();
10        if(product.contains("水果手机")){//此买家只买水果牌手机
11            System.out.print(name);
12            System.out.println("购买:" + product);
13        }
14    }
15}


买家的行为各式各样,在第8行实现了父类抽象行为,接到通知后他做了逻辑判断,很明显这类买家只稀罕水果牌手机,别的商品不是他的菜。再来看另一类剁手党买家。


1public class HandChopper extends Buyer {
2
3    public HandChopper(String name, Shop shop) {
4        super(name, shop);
5    }
6
7    @Override
8    public void inform() {
9        System.out.print(name);
10        String product = shop.getProduct();
11        System.out.println("购买:" + product);
12    }
13}


与果粉不同,他是来者不拒,只要有货就买买买!最后来看客户端的神操作。


1public class Client {
2    public static void main(String[] args) {
3        Shop shop = new Shop();
4        Buyer tanSir = new PhoneFans("果粉唐僧", shop);
5        Buyer barJeet = new HandChopper("剁手族八戒", shop);
6        shop.register(tanSir);
7        shop.register(barJeet);
8
9        //商店到货
10        shop.setProduct("猪肉炖粉条");
11        shop.setProduct("水果手机【爱疯叉】");
12
13        /*输出结果
14            剁手族八戒购买:猪肉炖粉条
15            果粉唐僧购买:水果手机【爱疯叉】
16            剁手族八戒购买:水果手机【爱疯叉】 
17        */

18    }
19}


无与伦比地优雅,第6行开始对疯狂买家师徒二人进行注册,于是他们再也不见他们终日徘徊于店门之外苦苦等待的身影了。接下来某日商店到货(第10行),至此购买过程就这样神奇地结束了,不信?看输出结果,嗯,不单买家很奇葩,连店都很奇葩。总之,商家只要到货就会马上打电话给这些订阅买家告知可以购买了。


其实,最初商家与买家之间的互动行为非常类似于Web应用中的Poll行为,由于Http无状态连接协议的安全特性,服务端(商家)无法主动推送(Push)消息给客户端(买家),所以有时会用到Poll技术,也就是不断的轮询服务端,有没有更新?有没有更新?有没有更新?严重时,成千上万的客户端会造成服务器瘫痪,所以之后诞生的WebSocket正是为了解决这个问题,这便类似于我们的观察者模式。



观察者模式解决了基于一对多对象结构关系间的互动问题,使观察者(多方买家)专主动为被动,被观察者(单方商家)转被动为主动,此情此景,需动静结合,先后有序,以【一方动多方静】取代【多方动一方静】的模式,大大的提高了沟通效率,别再偷窥了,我show给你好看。


点击图片加入Spring交流群

↓↓↓

看完本文有收获?请转发分享给更多人

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存