什么是观察者模式?
网上有很多种对于观察者模式的描述,我简单的说个比喻大概就能理解:我们常用的QQ邮箱,有订阅功能,你订阅了一个邮箱,以后这个邮箱每次有什么新内容,都会主动通知到你的邮箱里,你并不需要主动去订阅账号去看内容。当然我们进入邮箱获取邮箱是一个主动请求接口的过程,而观察者模式是一个不需要主动就能获取通知的一种“订阅”关系,我订阅了你,以后你只要发送一个消息,我都能收到,有3个订阅者那就3个都能收到,所以观察者模式一般会有“注册订阅”和“取消订阅”的两个行为
我们在开发中有什么场景会运用到观察者模式呢,最常见的就是EventBus了,不过EventBus是通过注解来获取订阅者的回调方法,而我们在开发中一般是运用接口来获取回调。我们在Android中所用到的各种监听setOnclickListener,setOnLongClicklistener等也是观察者模式,只不过这里是一对一的观察者模式,多对一有没有呢,Android里那些addXXXListener就属于多对一的观察者模式了,追进源码你会发现内部是通过一个集合来维护这些“订阅者”,说了这么多我们来聊一聊简物中运用观察者模式的场景
简物中的观察者模式
简物中除了使用EventBus作为发布/订阅事件总线,也使用了自己写的观察者莫斯,简物是一个电商APP,自然有商品详情界面,而简物中的商品详情又有入口可以进入到别的商品,也就是可以无限打开多个商品详情,商品详情下方的购物车区域有一个商品数量显示,点击购物车可以进入购物车界面,旁边的添加购物车可以添加当前商品到购物车
那需求是什么呢?我可以打开多个详情界面,也可以进入购物车界面,我在任意一个界面执行了添加商品到购物车或者在购物车列表进行了加减商品数量或者删除了商品,都要立即更新商品数量到每个商品界面,如果我们不用观察者模式那我们能怎么实现呢,大概就是在返回Activity的时候在onResume生命周期方法的地方去主动调用获取购物车数量更新到界面,或者在当前界面执行了添加购物车的方法后同时也调用更新购物车数量的方法,这样做确实能实现功能,但是这样做并不优雅而且要衔接一堆代码,对于更新迭代也非常不便
对于程序中的功能,除非是涉及到算法或者硬件或者底层的东西,要实现一个常规应用功能并不是难与不难的问题,可能初级新手也能实现这个功能,运行的界面也是一模一样,但是对于我们而言,我们应该要想着如何让程序有更好的扩展性,让我们的代码有更深层次的结构意义,这样做不是为了突出与别人的不同,而是为了让程序更健壮
这种场景下的功能,如果把要更新购物车数量的地方作为观察者,把管理购物车增删改查结果的地方作为被观察者,那我们只要给他们绑定一个订阅与被订阅的关系,并且在我们添加、删除、修改购物车的接口均给被订阅者发布一个更新消息,那这样不是所有的订阅者都能收到购物车被更改的消息,然后拿到订阅者传给我们的数据进行更新
那我们怎么做呢,这里我直接使用java的Observer(订阅者)和Observable(发布者)来实现观察者模式,这是java直接封装好的通用的观察者模式模型,那我们怎么用呢,首先在UI逻辑类(要更新购物车数量的地方)实现Observer接口,让它成为一个观察者,并且实现未实现的方法
public class SaleDetailCategory extends Category implements Observer{
/**
* 购物车商品数量
*/
@Bind(R.id.sale_number)
TextView mSaleNumber;
public SaleDetailCategory(BaseActivity activity) {
super(activity);
}
...
@Override
public void update(Observable observable, Object data) {
}
}
然后我们来封装一个发布者类,那首先我大概说一下简物中是如何获取购物车数量,因为每个购物车实体都有购物车uuid(cart_uuid, String)以及购物车商品数量(goods_number, Integer),那计算购物车中商品数量的方法应该是将所有cart_uuid的goods_number加起来,因为要不断的修改对应购物车的数量,所以我用HashMap<String, Integer>来做,既然是更新购物车数量的包装类,那自然会有更新购物车列表、更新指定购物车数量、添加购物车商品、删除购物车商品、清空购物车商品(添加商品到订单)的操作方法,那我就直接将这个类贴出来,不做详细描述啦
/**
* Created by Barry on 2017/2/11.
*/
public class ShoppingCartState extends Observable{
/**
* 用于返回到购物车界面是否要自动刷新的一个标识
*/
private boolean mShoppingCartStateChanged;
private HashMap<String, Integer> mShoppingCart = new HashMap<>();
/**
* 获取购物车列表成功,购物车列表刷新成功
* @param carts
*/
public void updateShoppingCart(List<CartBean.Cart> carts){
mShoppingCart.clear();
for(CartBean.Cart cart:carts){
mShoppingCart.put(cart.getCart_uuid(), cart.getGoods_number());
}
notifyDataChanged();
}
/**
* 获取购物车总数量
* @return
*/
public int getShoppingCartNumber(){
int shoppingCartNumber = 0;
Iterator iterator = mShoppingCart.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry entry = (Map.Entry) iterator.next();
Object value = entry.getValue();
shoppingCartNumber += ((Integer)value).intValue();
}
return shoppingCartNumber;
}
/**
* 更新指定商品购物车数量
* @param cart_uuid
* @param number
*/
public void updateCartNumber(String cart_uuid, int number){
if(mShoppingCart.containsKey(cart_uuid)){
mShoppingCart.put(cart_uuid, number);
}
notifyDataChanged();
}
/**
* 获取购物车数量文案
* @return
*/
public String getShoppingCartNumberString(){
return getShoppingCartNumber() + BaseUtils.getString(R.string.activity_saledetail_cart_has_sale_end);
}
/**
* 商品详情添加商品到购物车成功
* @param cart
*/
public void addShoppingCartSuccess(CartBean.Cart cart){
if(BaseUtils.isEmpty(cart)){
return;
}
mShoppingCart.put(cart.getCart_uuid(), cart.getGoods_number());
notifyDataChanged();
}
/**
* 提交购物车商品到订单成功
*/
public void addCartToOrderSuccess(){
clearShoppingCart();
notifyDataChanged();
}
public void deleteShoppingCartSuccess(String cart_uuid){
if(mShoppingCart.containsKey(cart_uuid)){
mShoppingCart.remove(cart_uuid);
}
notifyDataChanged();
}
/**
* 清空购物车数量:提交订单到购物车|退出登录
*/
public void clearShoppingCart(){
mShoppingCart.clear();
notifyDataChanged();
}
/**
* 更新到观察者
*/
public void notifyDataChanged(){
setChanged();
notifyObservers();
}
/**
* 购物车状态改变
*/
public void shoppingCartLoaded(){
setShoppingCartStateChanged(false);
}
public boolean isShoppingCartStateChanged() {
return mShoppingCartStateChanged;
}
public void setShoppingCartStateChanged(boolean shoppingCartStateChanged) {
this.mShoppingCartStateChanged = shoppingCartStateChanged;
}
}
那我们现在只要在对应更新购物车接口回调的地方调用这个类的方法即可,我这里贴一个栗子吧
/**
* Created by Barry on 2017/1/20.
* 添加到购物车实现
*/
public class AddCartModelImpl extends BaseModelImpl implements AddCartModel {
public AddCartModelImpl(BaseView baseView) {
super(baseView);
}
@Override
public AddCartView getListener() {
return (AddCartView)baseView;
}
@Override
public void addCart(CartParams cart) {
addCart(cart, false);
}
@Override
public void addCart(CartParams cart, boolean finish) {
addCart(cart.getUser_uuid(), cart.getGoods_id(), cart.getGoods_img(), cart.getGoods_sn(), cart.getProduct_id(), cart.getGoods_name(), cart.getMarket_price(), cart.getGoods_price(), cart.getGoods_number(), cart.getGoods_attr(), cart.getGoods_attr_id(), finish);
}
@Override
public void addCart(String user_uuid, int goods_id, String goods_img, String goods_sn, int product_id, String goods_name, String market_price, String goods_price, int goods_number, String goods_attr, String goods_attr_id, final boolean finish) {
QHApi.addCart(user_uuid, goods_id, goods_img, goods_sn, product_id, goods_name, market_price, goods_price, goods_number, goods_attr, goods_attr_id, this, CartBean.class, new OkHttpClientManager.Callback<CartBean>() {
@Override
public void onFailure() {
getListener().addCartError(NETWORK_ERROR);
}
@Override
public void onResponse(CartBean o) {
if(isEmpty(o)){
getListener().addCartError(ERROR);
return;
}
if(o.getStatus() != HttpCode.OK){
getListener().addCartError(o.getMessage());
return;
}
App.getInstance().shoppingCartStateChanged();
getListener().addCartSuccess(finish);
try{
/**
* 更新购物车数量
*/
App.getInstance().getShoppingCartState().addShoppingCartSuccess(o.getItems().get(0));
}catch (Exception e){
App.getInstance().getShoppingCartState().addShoppingCartSuccess(null);
}
}
});
}
}
那回调成功并且调用购物车数量更新包装类之后,我们的订阅者也能拿到消息,并且更新界面啦,怎么实现的呢
public class SaleDetailCategory extends Category implements Observer{
/**
* 购物车商品数量
*/
@Bind(R.id.sale_number)
TextView mSaleNumber;
public SaleDetailCategory(BaseActivity activity) {
super(activity);
}
...
@Override
public void update(Observable observable, Object data) {
if(observable instanceof ShoppingCartState){
updateShoppingCartNumber();
}
}
public void updateShoppingCartNumber(){
mSaleNumber.setText(App.getInstance().getShoppingCartState().getShoppingCartNumberString());
if(mCartWillDestoryInTimeLayout.getVisibility() == View.VISIBLE && App.getInstance().getShoppingCartState().getShoppingCartNumber() <= 0){
startCartWillDestoryLeaveLayoutAnimation();
}
if(mCartWillDestoryInTimeLayout.getVisibility() == View.GONE && App.getInstance().getShoppingCartState().getShoppingCartNumber() > 0){
shoppingCartAddAnimation();
}
}
}
不过还要记得在生命周期方法中注册和取消注册观察者哦
public class SaleDetailActivity extends BaseActivity{
@Override
protected void setContentView() {
setContentView(R.layout.activity_saledetail);
}
@Override
protected void initCategory() {
category = new SaleDetailCategory(this);
}
@Override
protected void doOnResume() {
if(firstRunning){
App.getInstance().getShoppingCartState().addObserver(category);
}
}
@Override
protected void doOnDestroy() {
super.doOnDestroy();
if(!BaseUtils.isEmpty(category)){
App.getInstance().getShoppingCartState().deleteObserver(category);
}
}
最后贴一个实现的效果图,上传后发现,不但变的不流畅而且界面中1px的分割线都消失了…
注意观察GIF图中底部购物车数量栏目数量的变化
以上,完