Android 常用设计模式(一)

由于项目变更的频繁性,作为一名程序员,我们需要掌握设计模式的必要性,就不言而喻~~,下面就是一些我自己学习的设计模式总结。 接下来,主要是针对几个比较常用模式进行讲解,主要是以下几种:

  • 观察者模式
  • 适配器模式
  • 代理模式
  • 工厂模式
  • 单例模式
  • 命令模式

1.观察者模式(Observer Pattern)

释义

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。

故事理解

观察者想知道公司所有MM的情况,只要加入公司的MM情报邮件组就行了,tom负责搜集情报,当发现新情报时,不用一个一个通知我们,直接发布给邮件组,我们作为订阅者(观察者)就可以及时收到情报啦。

常见实例

  1. BaseAdapter.registerDataSetObserver和BaseAdapter.unregisterDataSetObserver两方法来向BaseAdater注册、注销一个DataSetObserver ;
  2. 使用ContentObserver去监听数据库变化。

适用场景

  1. 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变;
  2. 当一个对象必须通知其它对象,而它又不能假定其它对象是谁.

观察者模式主要有观察者和被观察者2个对象,在该模式中,Observable表示被观察者,这个对象是一个抽象类,只能被继承。 Observer表示观察者,他是一个接口,所以观察者可以有多个,实现了该接口的类都是属于观察者。这是网上一个生动细致的demo:
被观察者

public class MyPerson extends Observable {

    private int age;
    private String name;
    private String sax;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        setChanged();
        notifyObservers();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        setChanged();
        notifyObservers();
    }

    public String getSax() {
        return sax;
    }

    public void setSax(String sax) {
        this.sax = sax;
    }

    @Override public String toString() {
        return "MyPerson [age=" + age + ", name=" + name + ", sax=" + sax + "]";
    }
}

MyPerson是被观察者,类中调用了setChange()以及notifyObservers()两个方法,前者是告知数据改变,后者是发送信号通知观察者。

观察者

public class MyObserver implements Observer {

    private int i;
    private MyPerson myPerson; //观察的对象
    public MyObserver(int i) {
        System.out.println("我是观察者---->" + i);
        this.i = i;
    }

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }

    public MyPerson getMyPerson() {
        return myPerson;
    }

    public void setMyPerson(MyPerson myPerson) {
        this.myPerson = myPerson;
    }

    @Override public void update(Observable observable, Object data) {
        System.out.println("观察者---->" + i + "得到更新!");
        this.myPerson = (MyPerson) observable;
        System.out.println(((MyPerson) observable).toString());
    }

}

观察者需要实现Observer接口,其中只有一个update方法,当观察者收到 (被观察者)的通知信号,就会执行该动作。
具体demo源码以及实现效果可点连接

2.适配器模式(Adapter Pattern)

释义:

把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。

故事理解:

在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友kent了,他作为我和Sarah之间的Adapter,让我和Sarah可以相互交谈了(也不知道他会不会耍我)。

常见实例

ListView用于显示列表数据,但是作为列表数据集合有很多形式,有Array,有Cursor,我们需要对应的适配器作为桥梁,处理相应的数据(并能形成ListView所需要的视图)。

适用场景

  1. 业务的接口与工作的类不兼容,(比如:类中缺少实现接口的某些方法)但又需要两者一起工作;
  2. 在现有接口和类的基础上为新的业务需求提供接口。

适配器模式分为类适配器模式和对象适配器模式。关于类适配模式,因为java的单继承,所以在已继承一个类时,另外的只能是接口,需要手动实现相应的方法,这样在客户端就可以创建任一种符合需求的子类,来实现具体功能。而另外一种对象适配器,它不是使用继承再实现的方式,而是使用直接关联,或者称为委托的方式,具体可见该博客详细介绍适配器模式(Adapter):类适配器、对象适配器

接下来就以ListView与ArrayAdapter来讲解下
ListAdapter:

public interface ListAdapter {
    public int getCount();
    Object getItem(int position);
    long getItemId(int position);
    View getView(int position, View convertView, ViewGroup parent);
    boolean isEmpty();
}

ListView作为一个客户端,所需要的目标接口就是ListAdapter,包含getCount(),getItem(),getView()等几个方法,为了兼容List< T >数据类型的数据源,专门定义了ArrayAdapter适配器,说白了,就是针对目标接口对数据源进行兼容修饰。
抽象类BaseAdapter,省略了其他代码,这里只列出两个方法:

public abstract class BaseAdapter implements ListAdapter,SpinnerAdapter {
    // ... ...
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        return getView(position, convertView, parent);
    }
    public boolean isEmpty() {
        return getCount() == 0;
    }
}

ArrayAdapter对List< T >进行封装成ListAdapter的实现,满足ListView的调用:

public class ArrayAdapter < T > extends BaseAdapter implements Filterable {
    private List < T > mObjects;
    //我只列出这一个构造函数,大家懂这个意思就行
    public ArrayAdapter(Context context, int textViewResourceId, T[] objects) {
        init(context, textViewResourceId, 0, Arrays.asList(objects));
    }

    private void init(Context context, int resource, int textViewResourceId, List < T > objects) {
        mContext = context;
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mResource = mDropDownResource = resource;
        mObjects = objects; //引用对象,也是表达了组合优于继承的意思
        mFieldId = textViewResourceId;
    }
    public int getCount() {
        return mObjects.size();
    }

    public T getItem(int position) {
        return mObjects.get(position);
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        return createViewFromResource(position, convertView, parent, mResource);
    }
    // ... ...
}

我们就如此成功的把List< T > 作为数据源以ListView想要的目标接口的样子传给了ListView。这其实也是对象适配器的一种。

3.代理模式(Proxy Pattern)

释义:

通过引入一个新的对象来实现对真实对象的操作或将新的对象作为真实对象的一个替身,这样的实现机制是代理模式(为其他对象提供一种代理以控制对这个对象的访问).

故事理解:

校园代理是为他对应的上司作代理,而这个校园代理的工作就是访问校园中的学生,例如对学生进行问卷之类的事。学生就是官方说法中的其他对象,校园代理的上司就通过控制这个校园代理来控制对学生的访问。

常见实例

ActivityManagerProxy类就是一个代理,它是ActivityManagerNative的代理,也就是说ActivityManagerProxy是所说的Proxy类,而ActivityManagerNative就相当于“上司角色“类,它们都有一个共有的接口IActivityManager。ActivityManager,它相当于代理模式的client。在这个类中,可以看到大量的getxxx函数,这些函数,都会调用到ActivityManagerNative类的getDefault()方法,而该方法会获得一个共用的单例的IActivityManager引用,然后通过多态来调用代理中的实现

适用场景

代理模式的应用场合分几种:远程代理,虚拟代理,安全代理等,具体可见为别人做嫁衣----代理模式请看下面一张结构图(网上看到的一个细致生动的例子):

代理模式

Subject类:定义了RealSubject和Proxy的共用接口,这样就可以在任何 使用RealSubject的地方都可以用Proxy。
RealSubject类:定义Proxy所代表的真实实体。
Proxy类:保存一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体。
接下来,我们来实现该模式:

1.Subject类 :Image.java

/**
 * Subject类
 */
public abstract class Image {

    public abstract void displayImage();

}

2.RealSubject类:RealImage.java

import com.andyidea.patterns.subject.Image;

/**  
 * RealSubject类  
 */
public class RealImage extends Image {
    private String filename;
    public RealImage(String filename) {
        this.filename = filename;
        loadImageFromDisk();
    }

    private void loadImageFromDisk() {

        System.out.println("Loading   " + filename);
    }

    @Override public void displayImage() {

        System.out.println("Displaying " + filename);
    }

}

3.proxy类:ProxyImage.java

import com.andyidea.patterns.realsubject.RealImage;
import com.andyidea.patterns.subject.Image;

/**
 * Proxy类
 */
public class ProxyImage extends Image {
    private String filename;
    private Image image;

    public ProxyImage(String filename) {
        this.filename = filename;
    }

    @Override public void displayImage() {

        if (image == null) {
            image = new RealImage(filename);
        }
        image.displayImage();
    }

}

4.客户端测试类:ProxyClient.java

import com.andyidea.patterns.proxy.ProxyImage;
import com.andyidea.patterns.subject.Image;

/**
 * 代理模式客户端测试类
 */
public class ProxyClient {

    public static void main(String[] args) {
        System.out.println("Welcome to my Blog!" + "\n" + "Proxy Patterns." + "\n" + "-------------------------------");

        Image mImage1 = new ProxyImage("My.Image1");
        Image mImage2 = new ProxyImage("My.Image2");

        mImage1.displayImage();
        mImage2.displayImage();
    }
}

运行结果如下:

Welcome to my Blog! 
Proxy Patterns 
-------------------------------  
Loading My.Image1  
Displaying My.Image1  
Loading My.Image2  
Displaying My.Image2

总结来说,代理类就是对实体保存一个引用,同时也实现了实体一样的接口方法,这样,就可以替代实体了!!

以上就是观察者模式,适配器模式,代理模式的认识,接下来会在 Android 常用设计模式(二)里,继续对余下的三种模式进行阐述。

如果觉得此文不错,麻烦帮我点下“喜欢”。么么哒!

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

推荐阅读更多精彩内容