Android与设计模式(3)代理/适配器/装饰/享元

详细代码请见https://github.com/zackLangChina/DesignPatternLearning

Android与设计模式(1)
单例模式 ** 工厂模式 ** 策略模式 ** 命令模式
Android与设计模式(2)
观察者模式 ** 备忘录模式 ** 模板方法模式 ** 中介者模式
Android与设计模式(3)
代理模式 ** 适配器模式 ** 装饰模式 ** 享元模式
Android与设计模式(4)
外观模式 ** 桥接模式 ** MVP模式

9,代理模式

代理模式

使用场景:

是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。意义在于:客户端与服务端解耦,以及在代理中封装一些功能(比如AIDL中代理类Proxy封装了binder通信的功能)。
代理模式分为静态代理和动态代理。区别为静态代理使用时需要通过new出代理类,而动态代理是使用APT、反射等方式创建代理类。
很多框架(如retrofit)中通常会使用java.lang.reflect包中的Proxy和InvocationHandler来实现动态代理。当我们使用proxy的方法:

Object newProxyInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2)

内部实现会通过反射获得的具备var1接口的实例,并且这个实例是被装饰过的,每当调用var1中方法时,都会首先调用InvocationHandler 接口实现类var2中的方法:

Object invoke(Object var1, Method var2, Object[] var3)

这种插入代码逻辑的编程思想,就叫做AOP,面向切片编程。
代理模式有些类似中介模式,区别如下:
中介模式中,双方都可以互相访问;代理模式中,访问是单向的,只能由客户端通过代理访问服务端。

Android中的应用:

AIDL。代码示例部分会模仿AIDL的实现(没有binder)

组成部分

客户端 | 代理 | 服务端

代码示例:

import java.util.HashMap;
import java.util.Map;
//模仿android中的ServiceManager,管理所有service(其实管理的是代理proxy)
class ProxyServiceManager {
    private static ProxyServiceManager mInstant = new ProxyServiceManager();
    private Map<String, IProxyService> mProxyList = new HashMap<String, IProxyService>();

    private ProxyServiceManager() {
    }

    public static ProxyServiceManager getInstance() {
        return mInstant;
    }

    public void addService(String name,IProxyService service) {
        mProxyList.put(name,service);
    }

    public IProxyService getService(String name) {
        return mProxyList.get(name);
    }
}
//服务和代理的公用接口。不同服务实现不同的接口。类似于AIDL
interface IProxyService {
    void doSomething();
}
//类似于AIDL生成的代码的结构(没有binder),真正的服务中嵌套一个内部proxy类
class ProxyService implements IProxyService {
    public String SERVICE_NAME_PROXYSERVICE = "proxy";
    private ProxyService mInstant;

    public ProxyService() {
        mInstant = this;
        ProxyServiceManager.getInstance().addService(SERVICE_NAME_PROXYSERVICE,new MyProxy());
    }
    //服务端的接口实现,业务代码放在这
    @Override
    public void doSomething() {
        System.out.println("now, Service do something");
    }
    //内部proxy类,实际上注册到ProxyServiceManager的是它。负责与真正的service通信
    class MyProxy implements IProxyService {
        //proxy的接口实现,只负责通信
        @Override
        public void doSomething() {
            System.out.println("proxy call Service do something...");
            mInstant.doSomething();
        }
    }
}

10,适配器模式

适配器模式

使用场景:

将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
说人话!
可以把Adapter想象成一个加工车间,把原材料送进去,产出的是成品。客户端只要伸手拿成品就行,不需要关心加工过程。

Android中的应用:

ListView或RecycleView通过适配器来获取子View。
只要把子View的布局和所有子VIew中资源ID对应的数据给到Adapter,就能转成客户端所需的View。
代码示例部分会模仿这块实现。

组成部分

客户端 | 适配器

代码示例:

import java.util.ArrayList;
import java.util.List;
//目标产品
class Cake {
    private int sugers;
    private int eggs;

    public Cake(int sugers, int eggs) {
        this.sugers = sugers;
        this.sugers = eggs;
        System.out.println("I am Cake, made by " +  sugers + " sugers and " + eggs + " eggs");
    }
}
//适配器接口,我们可以有不同的适配器实现
interface ICakeAdapter {
    List<Cake> productCake();
}
//适配器实现,用糖和鸡蛋做蛋糕
class CakeAdapter implements ICakeAdapter{
    private int[] sugerList;
    private int[] eggList;

    public CakeAdapter(int[] sugerList, int[] eggList) {
        if(sugerList.length!=eggList.length) {
            System.out.println("原料数量不匹配");
            return;
        }
        this.sugerList = sugerList;
        this.eggList = eggList;
    }
    //用糖和鸡蛋批量制作蛋糕提供给客户端
    @Override
    public List<Cake> productCake() {
        List<Cake> cakes = new ArrayList<Cake>();
        for(int i=0;i<sugerList.length;i++) {
            cakes.add(new Cake(sugerList[i],eggList[i]));
        }
        sugerList = null;
        eggList = null;
        return cakes;
    }
}

11,装饰模式

代理模式

使用场景:

装饰模式在功能上类似于通过子类继承,扩展父类的功能。但是装饰模式相对于继承,有以下好处:
1,可以动态地切换需要装饰的对象,比生成子类更灵活。
2,因为装饰类可以切换装饰对象,所以一个装饰类可以用来给一批父类相同的子类来搞装饰,而不用像继承那样,扩展一个类就得定义一个子类

Android中的应用:

Context

ContextWrapper(ContextThemeWrapper的父类(Acrtivity的父类))是一个地地道道的装饰者。它本身并没有实现业务功能,只是调用了ContextImpl的方法,所以ContextWrapper是ContextImpl的装饰类。
同样的,Activity、Service和Application作为ContextWrapper的子类,一样是ContextImpl的装饰类!因为他们仅仅是对Context接口的实现进行了装饰。
这里其实并没有很体现装饰模式的修饰,但如果未来Context有更多的实现类...嗯,那就牛逼了。

组成部分

接口或基类 | 功能实现类或子类 | 装饰类

代码示例:

interface Sword {
    void showProperty();
}
//一把黄金剑
class GoldSword implements Sword {

    @Override
    public void showProperty() {
        System.out.println(getClass().getName());
    }
}
//一把木剑
class WoodSword implements Sword {

    @Override
    public void showProperty() {
        System.out.println(getClass().getName());
    }
}
//装饰类
class SwordDecorator implements Sword {
    Sword mBase;

    public SwordDecorator(Sword mBase) {
        this.mBase = mBase;
    }
    //可以动态更换需要装饰的类
    public void attachBase(Sword mBase) {
        this.mBase = mBase;
    }
    //给大宝剑们上Buff!
    @Override
    public void showProperty() {
        mBase.showProperty();
        System.out.println("+8 稀有品质!");
    }
}

12,享元模式

享元模式

使用场景:

享元模式通过共享技术实现相同或相似对象(享元)的重用,避免大量重复申请内存导致的问题。
享元工厂负责生产和管理享元,当客户端申请享元的时候,首先查询是否有现成的享元,如果没有,再生产一个新的享元。

Android中的应用:

线程池

组成部分

享元接口 | 具体的享元类 | 享元工厂

代码示例:

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

//享元工厂
class Library {
    //可借的书,书池,借书时如果书池中有要借的书,就不用生产新书了
    private List<Book> bookList = new ArrayList<Book>();
    //借书出去
    public Book borrowBook(Class clazz) {
        for(Book book : bookList) {
            if(clazz.isInstance(book)) {
                bookList.remove(book);
                book.borrow();
                return book;
            }
        }
        //没有找到要借的书,生产一本
        try {
            Book book = (Book) clazz.newInstance();
            book.borrow();
            return book;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
    //还书回来
    public void returnBook(Book returnBook) {
        //放入书架
        bookList.add(returnBook);
        returnBook.returnBook();
    }
}
//享元接口(书),可以借书和还书
interface Book {
    void borrow();
    void returnBook();
}
//数学书
class MathBook implements Book {
    public MathBook() {
        System.out.println("create " + getClass().getName());
    }

    @Override
    public void borrow() {
        System.out.println("borrow " + getClass().getName());
    }

    @Override
    public void returnBook() {
        System.out.println("return " + getClass().getName());
    }
}
//漫画书
class ComicBook implements Book {
    public ComicBook() {
        System.out.println("create " + getClass().getName());
    }

    @Override
    public void borrow() {
        System.out.println("borrow " + getClass().getName());
    }

    @Override
    public void returnBook() {
        System.out.println("return " + getClass().getName());
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容