一、回复TD退订
日常生活中,这种短信无处不在,各种广告,在互联网高速发展的今天,个人信息可以说是透明
的。没有任何隐私可言,类似这种通知
其实跟我们开发过程使用的观察者模式(Observer Pattern)
如出一辙。更贴切的像初中时代,英语学习的周报订阅,一个班级大部分还是会订阅,基本上一周一期。这个算是比较典型的观察者模式
也即-发布-订阅
。可以这样理解,Publishers+Subscribers=Obeserver Pattern
。像这种一对多
的关系,一个对象状态的改变,所有订阅
它的对象都会被通知到并进行自己的一些操作可以用观察者模式
来解释。
二、观察者模式 Obeserver Pattern
对象之间存在像这种一对多的依赖关系,当被订阅的对象(Publishers)状态发生变化时,订阅者们(Subscribers)会收到相应的通知并作出相应的操作(更新自身的状态或行为操作),即为观察者模式,是一种对象行为型模式。
三、组成部分
以上述为例:
-
需要抽象的主题(Subject),
英语周报
就是这个主题,直观思考就可以理解,需要存储所有订阅了的学生(Observers),以便确保每个订阅的学生都能收到。需要可以删除不在订阅的学生(Observer),那么同样的也可以新增想要订阅的学生(Observer)。发布者Subject 需要持有所有的订阅者,并提供新增、删除订阅的方法,通知订阅者们自身状态改变的抽象方法(Notify)
-
需要抽象的订阅者(Observser),
学生
在这里可以作为充当订阅者的角色,可以抽象为Observer
,更加通用一点,老师同样可以订阅,其他有需要的人一样可以订阅。Observer
即可理解为角色的抽象。订阅者Observer,抽象的接口或抽象类(一般常见的为接口),提供更新自己行为、或属性的抽象方法
-
需要具体订阅者实现类(Concrete Observer),前面提到了,
学生
仅仅是众多订阅类型的一种,任何有需要的人都可以订阅,仅需实现Observer
接口即可,设计的易扩展性。 -
同样的具体主题实现者(Concrete Subject),是对
Subject
的具体实现,好处不用多说,这也是为什么我们在学习设计模式之初首先需要理解六种基本的设计原则。抽象不依赖实现细节,细节应该依赖抽象,抽象约束了细节使细节更规范可控
。 -
结构图:
四、代码实现
1.设计一个价格变动系统
水果店里的水果众多,应季水果通常很贵,像现在这个季节的车厘子(还没实现车厘子自由🍒),不易保存的水果,草莓等等,价格统一管理并下发到各个门店。
- 主题
Subject
,也即是发布者
/**
* Created by Sai
* on: 10/01/2022 11:41.
* Description:
*/
public abstract class Subject {
private final Logger logger = Logger.getLogger(Subject.class.getName());
/** 保存订阅者对象集合 */
private final List<Observer> observerList = new CopyOnWriteArrayList<>();
/** 新增订阅者Observer */
public boolean attach(Observer observer) {
logger.info(observer + "添加成功");
return observerList.add(observer);
}
/** 解除已经绑定的订阅者的关系 */
public boolean detach(Observer observer) {
logger.info(observer + "解绑成功");
return observerList.remove(observer);
}
/** 通知更新到Observers */
protected void notifyObservers() {
if (observerList.isEmpty()) {
return;
}
for (Observer observer : observerList) {
observer.priceChanged(this);
}
}
}
- 具体的实现类-如车厘子
/**
* Created by Sai
* on: 10/01/2022 12:16.
* Description:
*/
public class Cherry extends Subject {
/** 价格 */
private long price;
/** 名称 */
private String name;
public Cherry(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getPrice() {
return price;
}
public void setPrice(long price) {
/** 当价格发生改变通知到所有门店 */
this.price = price;
notifyObservers();
}
@Override
public String toString() {
return "Cherry{" + "price=" + price + ", name='" + name + '\'' + '}';
}
}
- 定义观察者
Observer
接口
/**
* Created by Sai
* on: 10/01/2022 11:34.
* Description:
*/
public interface Observer {
//通知的是主题的一些信息
void priceChanged(Subject subject);
}
- 具体实现类-门店Store
/**
* Created by Sai
* on: 10/01/2022 12:22.
* Description:
*/
public class Store implements Observer {
private String storeName;
public Store(String storeName) {
this.storeName = storeName;
}
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
private void show(Subject subject) {
System.out.println(this.storeName + subject.toString());
}
@Override
public void priceChanged(Subject subject) {
System.out.println(this.storeName);
System.out.println("Price Changed - Refreshing price");
show(subject);
System.out.println("---------------------------------------->");
}
}
- 测试用例
/**
* Created by Sai
* on: 10/01/2022 12:27.
* Description:
*/
public class Demo {
public static void main(String[] args) {
Cherry cherry = new Cherry("车厘子");
Store one = new Store("门店一");
Store two = new Store("门店二");
cherry.attach(one); cherry.attach(two);
cherry.setPrice(100);
//价格涨了
cherry.setPrice(200);
//门店一倒闭了
cherry.detach(one);
cherry.setPrice(99);
}
}
- 打印信息
信息: com.observer.sai.Store@2f92e0f4添加成功
1月 10, 2022 1:01:22 下午 com.observer.sai.Subject attach
信息: com.observer.sai.Store@4f3f5b24添加成功
1月 10, 2022 1:01:22 下午 com.observer.sai.Subject detach
信息: com.observer.sai.Store@2f92e0f4解绑成功
门店一
Price Changed - Refreshing price
门店一Cherry{price=100, name='车厘子'}
---------------------------------------->
门店二
Price Changed - Refreshing price
门店二Cherry{price=100, name='车厘子'}
---------------------------------------->
门店一
Price Changed - Refreshing price
门店一Cherry{price=200, name='车厘子'}
---------------------------------------->
门店二
Price Changed - Refreshing price
门店二Cherry{price=200, name='车厘子'}
---------------------------------------->
门店二
Price Changed - Refreshing price
门店二Cherry{price=99, name='车厘子'}
---------------------------------------->
Process finished with exit code 0
2.一些思考
门店管理系统,实现了水果价格变动时动态的下发信息到各个门店下面,这个动态的联动关系;只要主题对象的状态或者行为发生改变。那么订阅者们感知到这个变化相应的作出自身的行为。虽然有一定的耦合,但也是抽象层面的,耦合程度还是比较低的,扩展性得到了保证。设计的初衷也仅仅就为了解决这几个问题:降低耦合,提高内聚、扩展性、层次结构性。
- 降低了发布者(
Publishers
)与订阅者(Subscribers
)直接的耦合程度,抽象间耦合。 - 实现了发布者与订阅者之间的动态联动,订阅者对发布者行为的改变作出自身的改变。
当然缺点也是很明显的,从例子中就可以看出,随着门店的扩张,系统的调用深度也会增加。其次门店的闭店来不及解绑的情况下,也导致了一些无谓的通知,增加系统的负担。
- 即时订阅与即时解绑,前者会导致通知不到位,错过重要的信息;而后者则会造成无谓的通知。
- 如果存在互相依赖的情况下,那么会出现
死循环
的情况,当然这种场景下,首先应该考虑的到的是观察者模式
是否真的适合在此时使用?
五、实际问题
在题主目前工作内容中涉及到使用Observer Pattern
的场景还是比较多的,其中购物车模块业务,当商品被加到购物车中:总价格发生变化、商品数目红点数量信息
改变、商品库存、单个商品被选购的次数等都发生了变动。反之删除商品同样。
public interface OnCartObserver {
...
void onDeleteProduct(int position, int needUpdateCount);
void onUpdateProduct(int position, CartProductVO product);
void onCartClear();
//备注的更新
void onRemarkUpdate(String[] remarks);
...
}
public abstract class BaseOnCartObserver implements OnCartObserver {
...
@Override
public void onDeleteProduct(int position, int needUpdateCount) {
}
@Override
public void onCartClear() {
}
@Override
public void onRemarkUpdate(String[] remarks) {
}
@Override
public void onUpdateProduct(int position, CartProductVO product) {
}
...
}
//调用采用了匿名内部类的形式,并没有严格的遵循观察者模式UML实现
public class CartGoodsFragment extends BaseFragment {
private final OnCartObserver onCartObserver = new BaseOnCartObserver() {
//行为的更新
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
XX.provideController.addObserver(onCartObserver);
}
@Override
public void onDestroy() {
super.onDestroy();
XX.provideController.removeObserver(onCartObserver);
}
}
严格意义上这并不是标准的观察者模式,但是观察者模式的本质-一对多,主题状态改变时,依赖者会即时感知并作出反应,通俗的讲就是出发联动效果。
还是回到设计的本质,设计的初衷降低耦合、降低复杂度、提高扩展性,设计没有定式,如果完全照搬不仅会适得其反,其次也可能使项目变得四不像
。为了设计而设计并不可取,重要的还是细想的理解。实际业务与功能千变万化,对业务的抽象能力,思考才是最重要,设计模式仅仅是把思路转化为实现细节的手段。
六、该何时使用
- 抽象模型之间存在
一对多
这种关系时,并且主体的改变会引起其他依赖者的改变时。 - 需要
动态联动
的关系,且构建尽可能低的耦合度的系统。
是标准的观察者模式,但是观察者模式的本质-一对多,主题状态改变时,依赖者会即时感知并作出反应,通俗的讲就是出发联动效果。
还是回到设计的本质,设计的初衷降低耦合、降低复杂度、提高扩展性,设计没有定式,如果完全照搬不仅会适得其反,其次也可能使项目变得四不像
。为了设计而设计并不可取,重要的还是细想的理解。实际业务与功能千变万化,对业务的抽象能力,思考才是最重要,设计模式仅仅是把思路转化为实现细节的手段。
六、该何时使用
- 抽象模型之间存在
一对多
这种关系时,并且主体的改变会引起其他依赖者的改变时。 - 需要
动态联动
的关系,且构建尽可能低的耦合度的系统。
最后
有小伙伴私信问Compose的问题,好不好用啊,现在要不要学啊?
其实答案很简单,自从谷歌2019年公布了声明式UI框架Jetpack Compose后,两年多的时间,各种大力宣传,和大量资源的倾斜,API功能都趋于稳定了。
至于好不好用,各种用过的同行都是持肯定态度的。优势大概就是这四点:
强大的工具和直观的Kotlin API
简化并加速了Android上的UI开发
可以帮助开发者用更少更直观的代码创建View
有更强大的功能,以及还能提高开发速度
这么大的优势,毋庸置疑,肯定是要学的嘛,而且越快掌握越好。别等刀架到脖子上了,才去练金钟罩。
至于怎么快速上手,可以给大家免费分享一份**《Jetpack Compose 完全开发手册》**,手把手教大家从入门到精通。
第一章 初识 Jetpack Compose
-
为什么我们需要一个新的UI 工具?
-
Jetpack Compose的着重点
加速开发
强大的UI工具
直观的Kotlin API
- API 设计
- Compose API 的原则
一切都是函数
顶层函数(Top-level function)
组合优于继承
信任单一来源
- 深入了解Compose
Core
Foundation
Material
- 插槽API
第二章 Jetpack Compose构建Android UI
- Android Jetpack Compose 最全上手指南
Jetpack Compose 环境准备和Hello World
布局
使用Material design 设计
Compose 布局实时预览
……
- 深入详解 Jetpack Compose | 优化 UI 构建
Compose 所解决的问题
Composable 函数剖析
声明式 UI
组合 vs 继承
封装
重组
……
- 深入详解 Jetpack Compose | 实现原理
@Composable 注解意味着什么?
执行模式
Positional Memoization (位置记忆化)
存储参数
重组
……
第三章 Jetpack Compose 项目实战演练(附Demo)
- Jetpack Compose应用1
开始前的准备
创建DEMO
遇到的问题
- Jetpack Compose应用2
- Jetpack Compose应用做一个倒计时器
数据结构
倒计时功能
状态模式
Compose 布局
绘制时钟
- 用Jetpack Compose写一个玩安卓App
准备工作
引入依赖
新建 Activity
创建 Compose
PlayTheme
画页面
底部导航栏
管理状态
添加页面
- 用Compose Android 写一个天气应用
开篇
画页面
画背景
画内容
……
- 用Compose快速打造一个“电影App”
成品
实现方案
实战
不足
……
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
有需要的话可以点下面二维码免费领取↓↓↓