Android开发设计模式03

来源:互联网 发布:上帝之眼软件 编辑:程序博客网 时间:2024/05/16 15:23

享元模式,给我的感觉就是对象池,缓存单例对象。
java中的享元模式最经典的例子就是String类了,还有一个最容易理解的就是word文档字符共享的例子,也是享元模式的经典应用。
本文对android中的sql编译类SQLiteCompiledSql说明,展开分析,也是很容易理解的一个例子,其实,android SDK中必然有很多地方需要用到享元模式。
享元模式,Flyweight  Pattern,说的严重点,一些程序如果不使用享元模式的话,根本不能使用面向对象的方法实现,对象会多的撑爆你的内存:"用面向对象思想设计的应用常常会面临对象实例过多的问题"。 

1.意图
运用共享技术有效地支持大量细粒度的对象。
热门词汇:共享 池 缓存 内部状态 外部状态 对象 单例 

2.结构 


这是一个完整的享元模式结构图。
客户端通过享元工厂获取享元对象,享元对象的创建则根据工厂的享元池来控制,如果有享元池中没有这个对象,则创建这个对象并保存到享元池中,如果享元池中有这个对象,则直接使用这个对象。因为享元对象在共享的同时,说明它重用属性的不变性,不然都是变化的东西,不存在共享,这些不变得属性我们称之为内部状态,独立与外部场景。而另外一些属性,可以根据外部场景变化的,我们称之为外部状态,在上图中我们也看到,我们可以通过Operation改变外部状态。
Android中SQLiteCompiledSql的使用,其实是很多数据库系统典型的实现。从应用启动,通过各种数据库操作,我们不知道进行了多少次的查询操作,而这些操作中又有相当一部分sql语句是相同的,这些编译后的sql编译对象其实是一样的,是可以共用共享的,其实就是缓存。SQLiteCompiledSql就是这样的一个需要共享的享元对象,画出相关的UML图如下:


其中SqliteDatabase中的mCompiledQuerie就是存放享元对象的容器。
通过这种方式大大减少了sql编译对象的创建,提高了数据库操作的性能。

3.代码
享元对象类SQLiteCompiledSql,主要是内部状态sql语句:

1
2
3
4
5
class SQLiteCompiledSql {
    private String mSqlStmt = null;
    native_compile(sql);
    native_finalize();
}

享元工厂类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class SQLiteDatabase{
     Map<String, SQLiteCompiledSql> mCompiledQueries = Maps.newHashMap();
     SQLiteCompiledSql getCompiledStatementForSql(String sql) {
        SQLiteCompiledSql compiledStatement = null;
        boolean cacheHit;
        synchronized(mCompiledQueries) {
            if (mMaxSqlCacheSize == 0) {
                return null;
            }
            cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
        }
        if (cacheHit) {
            mNumCacheHits++;
        }else {
            mNumCacheMisses++;
        }
        return compiledStatement;
    }
 
    private void deallocCachedSqlStatements() {
        synchronized (mCompiledQueries) {
            for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
                compiledSql.releaseSqlStatement();
            }
            mCompiledQueries.clear();
        }
    }
 
    void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
         //省略具体代码
    }
}

  其他类几个相关类是对这个集合的操作相关,和享元模式没有什么实质性的关系,代码省略。

4.效果
(1).结构型模式;
(2).节约存储的方法:用共享减少内部状态的消耗,用计算时间换取对外部状态的存储;
(3).缓冲。 

--------------------------------

命令模式,在.net,java平台的事件机制用的非常多,几乎每天都与之打交道。
android中对我印象最深的就是多线程多进程的环境,所以必然大量使用到Runbable,Thread,其实用的就是最简单的命令模式。
命令模式,Command Pattern,把请求封装为一个对象,多么巧妙的一个说法啊。

1.意图
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
热门词汇:动作 事物 请求封装 排队 打包 异步 

2.结构


Command接口提供了Execute方法,客户端通过Invoker调用命令操作来调用Recriver,绕了一大圈,但是却把具体对Receiver的操作请求封装在具体的命令中,是客户端对recriver的操作清晰简明。
但是在实际项目中,我们常常忽略Receiver,而把命令对象的目标对象直接设置为子类自己的成员变量或者作为execute()方法的临时变量。
以Android中的Runnable(在java.lang包下)为例,我们画出UML结构图如下:


想不到我们天天写的代码无意识中就是用到了命令模式,所谓模式,就是无所不在。

3.代码
命令接口Runnable定义如下:

1
2
3
public interface Runnable {
    public abstract void run();
}

调用者Thread简化版代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//命令模式这里不需要继承Runnable接口,但是这里考虑到实际情况,比如方便性等,继承了Runnable接口,实现了run方法,这个是Thread自身的运行run的方法
class Threadimplements Runnable {
    private Runnable target;
     
    public Thread(Runnable target) {
        this.target = target;
    }
 
     public synchronized void start() {
 
        if (threadStatus != 0 ||this != me)
            throw new IllegalThreadStateException();
        group.add(this);
        start0();//这个是本地方法,调用run方法
        if (stopBeforeStart) {
        stop0(throwableFromStop);
    }
    }
 
    //可选
    public void run() {
    if (target != null) {
        target.run();
    }
    }
}

  客户端只需要new Thread(new Runnable(){}).start()就开始执行相关的一系列的请求,这些请求大部分都是实现Runnable接口的匿名类。

4.效果
(1).行为型模式;
(2).将调用对象的操作和知道如何实现该操作的对象解耦;
(3).多个命令可以装配成一个复合命令;
(4).增加新的命令很容易。

------------

工厂方法模式,往往是设计模式初学者入门的模式,的确,有人称之为最为典型最具启发效果的模式。
android中用到了太多的工厂类,其中有用工厂方法模式的,当然也有很多工厂并不是使用工厂方法模式的,只是工具管理类。
今天以ThreadFactory举例说明一下简单工厂模式和工厂方法模式。 
工厂方法模式,Factory Method,简单的方式,不简单的应用。

1.意图
定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方式模式使一个类的实例化延迟到其子类。
热门词汇:虚构造器 延迟 创建对象 子类 

2.结构图和代码
我们先看看标准的工厂方法结构图:


先抽象的产品类,抽象的工厂类,然后用客户端具体的工厂生产相应的具体的产品,但是客户端并不知道具体的产品是怎么生产的,生产的过程封装在工厂里。所以说,某种程度上,工厂方法模式改变了我们直接用new创建对象的方式,一个很好的开始,意义重大。
以ThreadFactory为例:

这张图其实和原本的结构图有细微的区别,那就是参数化得工厂,而且从业务意义上也有些不同,但是思想是一样的。
我们来看下具体的代码:

1
2
3
4
5
6
7
8
9
//抽象产品
public interface Runnable {
    public abstract void run();
}
 
//抽象工厂
public interface ThreadFactory {
    Thread newThread(Runnable r);
}

下面是具体的实现:
比如AsyncTask类中工厂的具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
//工厂实现类
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);
 
    public Thread newThread(Runnable r) {
        return new Thread(r,"AsyncTask #" + mCount.getAndIncrement());
    }
};
//那么产品类在哪里呢?
//做为参数Runnable r,我们可以创建千千万万个此系列的产品类
//同理,我们可以创建另外类似的工厂,生产某种专门的线程,非常容易扩展

看到这里,我们一方面为它的生产便利性感叹,一方面又为没创建某类产品都要创建一个工厂而感到繁琐,所以我们下面介绍简单工厂,它的结构图如下:


简单工厂把抽象工厂去掉了,你就创建一个专门生产某类产品就好。在一些特定而又不负责的领域非常实用方便套用这个模式。
在android中的Connection类中使用到了这个类:


其中Connection这个抽象类,既充当抽象产品类,也充当具体工厂类。
因为这种情况下,我们往往需要的是马上生产子类,getConnection方法往往是静态的,所以简单工厂,也叫静态工厂方法。
我们看看代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class Connection{   
    static Connection getConnection(
            Context context, HttpHost host, HttpHost proxy,
            RequestFeeder requestFeeder) {
 
        if (host.getSchemeName().equals("http")) {
            return new HttpConnection(context, host, requestFeeder);
        }
 
        // Otherwise, default to https
        return new HttpsConnection(context, host, proxy, requestFeeder);
    }
}

这就是简单工厂,一个很简单的参数化工厂,真的很简单。

3.效果
1. 创建型模式;
2.参数化工厂方法模式得到相应的对象;
3.为子类提供挂钩;
4.连接平行的类层次。