Android 常用设计模式

2021-11-22 11:13:10 浏览数 (1)

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

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

1.观察者模式(Observer Pattern)

释义

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

故事理解

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

常见实例

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

适用场景

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

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

代码语言:javascript复制
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()两个方法,前者是告知数据改变,后者是发送信号通知观察者。

观察者

代码语言:javascript复制
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方法,当观察者收到 (被观察者)的通知信号,就会执行该动作。

2.适配器模式(Adapter Pattern)

释义:

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

故事理解:

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

常见实例

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

适用场景

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

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

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

代码语言:javascript复制
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,省略了其他代码,这里只列出两个方法:

代码语言:javascript复制
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的调用:

代码语言:javascript复制
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

代码语言:javascript复制
/**
 * Subject类
 */
public abstract class Image {

    public abstract void displayImage();

}

2.RealSubject类:RealImage.java

代码语言:javascript复制
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

代码语言:javascript复制
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

代码语言:javascript复制
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();
    }
}

运行结果如下:

代码语言:javascript复制
Welcome to my Blog! 
Proxy Patterns 
-------------------------------  
Loading My.Image1  
Displaying My.Image1  
Loading My.Image2  
Displaying My.Image2

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

以上就是观察者模式,适配器模式,代理模式的认识

0 人点赞