23种设计模式,毕生心血,咳咳吐血去了

(一)什么是设计模式

1. 基本定义:设计模式(Design pattern)是一套被反复使用的代码设计经验的总结。使用设计模式的目的是为了可重用代码让代码更容易被他人理解。设计模式是是软件工程的基石脉络,如大厦的结构一样。

2. Design pattern的四大要素:模式名(Name),问题(Question),解决方案(Solution),效果(Efftive)。

3. OO(面向对象)的六大原则:单一职责原则,开闭原则,里氏替换原则,依赖倒置原则,接口隔离原则,迪米特原则。

单一职责原则:一个类中应该是一组相关性很高的函数,数据的封装。两个完全不一样的功能就不应该放在一个类中。

开闭原则:对修改封闭,对扩展放开。里氏替换原则:抽象和继承;所有引用基类的地方必须能透明的使用其子类的对象。

里氏替换原则:抽象和继承;所有引用基类的地方必须能透明的使用其子类的对象。

依赖倒置原则:抽象不应该依赖细节,细节应该依赖抽象。

接口隔离原则:将大接口改成多个小接口。

迪米特原则:也称为最少知识原则,一个对象应该对另一个对象有最少的了解。

(二)设计模式的分类

(1)创建型模式5种:

单例模式,抽象工厂模式,工厂模式,原型模式,建造者模式。(口诀:单原建造者,东西二厂)

(2)结构型模式7种:

适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式。(口诀:一器一桥一代理;装饰组合外观)

(3)行为型模式11种:

观察者模式,中介者模式,访问者模式,解释器模式,迭代器模式,备忘录模式,责任链模式,状态模式,策略模式,命令模式,模板模式。(口诀:三者两器、一录一链一模板,状态策略命令)

单利模式

单利模式有一下特点:

1. 单例类只能有一个实例。

2. 单例类必须自己创建自己的唯一实例。

3. 单例类必须给所有其他对象提供这一实例。

首先说一下经常用的

1.饿汉式

public class Singleton {

    private Singleton() {

        //构造方法为private,防止外部代码直接通过new来构造多个对象

    }

    //在类初始化时,已经自行实例化,所以是线程安全的。

    private static final Singleton single  = new Singleton();

    //通过getInstance()方法获取实例对象

    public static Singleton getInstance() {

        return single;

    }

}

优点:写法简单,线程安全

缺点:没有懒加载效果,如果没有使用过的话会造成内存浪费

2.懒汉式(线程不安全)

public class Singleton {

    private static Singleton singleton = null;

    public static Singleton getInstance() {

        if (singleton == null){

            //在第一次调用getInstance()时才实例化,实现懒加载,所以叫懒汉式

            singleton = new Singleton();

        }

        return singleton;

    }

    private Singleton() {

    }

}

优点:实现了懒加载效果

缺点:线程不安全

3.懒汉式(线程安全)

public class Singleton {

    private static Singleton singleton = null;

    //加上synchronized同步

    public static synchronized Singleton getInstance() {

        if (singleton == null){

            //在第一次调用getInstance()时才实例化,实现懒加载,所以叫懒汉式

            singleton = new Singleton();

        }

        return singleton;

    }

    private Singleton() {

    }

}

优点:实现了懒加载,线程也安全了

缺点:使用了synchronized会造成不必要的开销,而且大部分时候我们是用不到同步的

4.双重检查锁定(DCL)

public class Singleton {

    //volatile 能够防止代码的重排序,保证得到的对象是初始化过

    private volatile static Singleton singleton = null;

    private Singleton() {

    }

    public static Singleton getInstance() {

        //第一次检查,避免不必要的同步

        if (singleton == null){

            //同步

            synchronized (Singleton.class){

                //第二次检查,为null时才创建

                if (singleton == null){

                    singleton = new Singleton();

                }

            }

        }

        return singleton;

    }

}

优点:懒加载,线程安全,效率高

缺点:volatile影响一点性能,高并发下有一定的缺陷,某些情况下DCL会失效,虽然概率小

5.静态内部类

public class Singleton {

    private Singleton() {

    }

    public static Singleton getInstance() {

        //第一次调用getInstance方法时才加载SingletonHolder并初始化sInstance

        return SingletonHolder.sInsstance;

    }

    //静态内部类

    private static class SingletonHolder{

        private static final Singleton sInsstance = new Singleton();

    }

}

优点:懒加载,线程安全,推荐使用

缺点:个人还没发现

6.枚举单例

public enum Singleton {

    //定义一个枚举的元素,它就是Singleton的一个实例

    INSTANCE;

    public void doSomething(){

    }

}

优点:线程安全,写法简单,能防止反序列化重新创建对象

缺点:可读性不高,枚举会比静态常量多那么一丁点的内存

7.使用容器实现单例模式

//单例管理类

public class SingletonManager {

    private static Map<String,Object> objectMap = new HashMap<String, Object>();

    public static void registerService(String key,Object instance){

        if (!objectMap.containsKey(key)){

            objectMap.put(key,instance);//添加单例

        }

    }

    public static Object getService(String key){

        return objectMap.get(key);//获取单例

    }

}

优点:方便管理

缺点:写法稍微复杂

注意事项

1.使用反射会破坏单例模式,所以应该慎用反射

Constructor con = Singleton.class.getDeclaredConstructor();

con.setAccessible(true);

Singleton singleton1 = (Singleton) con.newInstance();

Singleton singleton2 = (Singleton) con.newInstance();

//结果为false,singeton1和singeton2将是两个不同的实例

System.out.println(singleton1 == singleton2);

可以通过当第二次调用构造函数是抛出异常来防止反射破坏单例,以懒汉式为例:

public class Singleton {

    private static boolean flag = true;

    private static Singleton single = null;

    private Singleton() {

        if (flag){

            flag = ! flag;

        }else {

            throw new RuntimeException("单例模式被破坏");

        }

    }

    public static Singleton getInstance(){

        if (single == null){

            single = new Singleton();

        }

        return single;

    }

}

2.反序列化时也能破坏单例模式,可以重写readResolve方法避免,以饿汉式为例:

public class Singleton implements Serializable {

    private Singleton() {

    }

    private static final Singleton single = new Singleton();

    public static Singleton getInstance() {

        return single;

    }

    //重写readResolve()

    private Object readResolve() throws ObjectStreamException {

        return single;

    }

}

应用场景

1.频繁访问数据库或文件对象。

2.工具类对象

3.创建对象时耗时过多或耗费资源过多,但经常用到的对象

优点

1.内存中只存在一个对象,节省了系统资源

2.避免对资源的多重占用,例如一个文件操作,由于只有一个实例存在内存中,避免对统一资源文件的同时操作

缺点

1.获取对象时不能用new

2.单例对象如果持有Context,那么很容易引发内存泄露。

3.单例模式一般没有接口,扩展很困难,若要扩展,只能修改代码来实现

建造者模式

定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

介绍

1.建造者模式属于创建型模式

2.建造者模式主要用来创建复杂的对象,用户可以不用关心其建造过程和细节

3.例如:当要组装一台电脑时,我们选择好CPU、内存、装机师傅就把电脑给组装起来,我们不需要关心是怎么拼装起来的

UML类图

角色说明

Product(产品类) :要创建的复杂对象。在本类图中,产品类是一个具体的类,而非抽象类。实际编程中,产品类可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成,也是可以由多个抽象类与他们的实现组成。

Builder(抽象建造者):创建产品的抽象接口,一般至少有一个创建产品的抽象方法和一个返回产品的抽象方法。引入抽象类,是为了更容易扩展。

ConcreteBuilder(实际的建造者):继承Builder类,实现抽象类的所以抽象方法。实现具体的建造过程和细节。

Director(指挥者类):分配不同的建造者来创建产品,统一组装流程。

实现

1.定义具体的产品(Product):电脑

public class Computer {

    private String mCPU;

    private String mMemory;

    private String mHD;

    public void setmCPU(String mCPU) {

        mCPU = mCPU;

    }

    public void setmMemory(String mMemory) {

        mMemory = mMemory;

    }

    public void setmHD(String mHD) {

        mHD = mHD;

    }

}

2.定义抽象建造者(Builder):组装电脑的过程

public abstract class Builder {

    //组装CPU

    public abstract void buildCUP(String cpu);

    //组装内存

    public abstract void buildMemory(String memory);

    //组装硬盘

    public abstract void buildeHD(String hd);

    //返回组装好的电脑

    public abstract Computer create();

}

3.创建具体的建造者(ConcreteBuilder):装机人员

public class ConcreteBuilder extends Builder {

    //创建产品实例

    private Computer mComputer = new Computer();

    //组装CPU

    @Override

    public void buildCUP(String cpu) {

        mComputer.setmCPU(cpu);

    }

    //组装内存

    @Override

    public void buildMemory(String memory) {

        mComputer.setmMemory(memory);

    }

    //组装硬盘

    @Override

    public void buildeHD(String hd) {

        mComputer.setmHD(hd);

    }

    //返回组装好的电脑

    @Override

    public Computer create() {

        return mComputer;

    }

}

4.定义指挥者类(Director):老板委派任务给装机人员

public class Director {

    private Builder mBuilder = null;

    public Director(Builder mBuilder) {

        this.mBuilder = mBuilder;

    }

    //指挥装机人员组装电脑

    public void Construct(String cpu, String memory, String hd){

        mBuilder.buildCUP(cpu);

        mBuilder.buildMemory(memory);

        mBuilder.buildeHD(hd);

    }

}

5.测试方法

public void CreatComputer() {

    Builder builder = new ConcreteBuilder();

    Director director = new Director(builder);

    director.Construct("i7-6700","三星DDR4","希捷1T");

}

应用场景

1.创建一些复杂的对象时,对象内部的构键过程存在复杂变化。

2.相同的构建过程,不同的执行顺序,产生不同结果时。

3.不同配置的构建对象,产生不同结果时。

优点

1.封装性良好,隐藏内部构建细节。

2.易于解耦,将产品本身于产品创建过程进行解耦,可以使用相同的创建过程来得到不同的产品,也就说细节依赖抽象。

3.易于扩展,具体的建造者类之间相互独立,增加新的具体建造者无需修改原有类库的代码。

4.易于精确控制对象的创建,由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。

缺点

1.产生多余的Build对象以及Dirextor类。

2.建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

3.如果产品的内部复杂,可能会导致需要定义很多具体建造者来实现这种变化,导致系统变得很庞大。

Android中的源码分析

Android中的AlertDialog.Builder就是使用了Builder模式来构建AlertDialog的。

AlertDialog.Builder的简单用法

//创建一个builder对象

AlertDialog.Builder builder = new AlertDialog.Builder(this);

builder.setIcon(R.drawable.aa);

builder.setTitle("标题");

builder.setMessage("信息");

builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {

    @Override

    public void onClick(DialogInterface dialog, int which) {

    }

});

//创建AlertDialog对象

AlertDialog alertDialog = builder.create();

//展示AlertDialog

alertDialog.show();

通过Builder对象来构建lcon、Title、Message等,讲AlertDialog的构建过程和细节隐藏了起来

AlertDialog相关源码分析

public class AlertDialog extends AppCompatDialog implements DialogInterface {

final AlertController mAlert;

protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {

    super(context, resolveDialogTheme(context, themeResId));

//创建AlertController对象

    this.mAlert = new AlertController(this.getContext(), this, this.getWindow());

  }

//设置Title

public void setTitle(CharSequence title) {

    super.setTitle(title);

//保存在AlertController对象中

    this.mAlert.setTitle(title);

}

//设置Message

public void setMessage(CharSequence message) {

//保存在AlertController对象中

    this.mAlert.setMessage(message);

}

//设置Icon

public void setIcon(int resId) {

//保存在AlertController对象中

    this.mAlert.setIcon(resId);

}

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

//安装AlertDialog的内容

    this.mAlert.installContent();

}

//AlertDialog其他代码略

public static class Builder {

//构建AlertDialog对象所需要的参数都存放在P中

private final AlertParams P;

public Builder(@NonNull Context context) {

    this(context, AlertDialog.resolveDialogTheme(context, 0));

}

public Builder(@NonNull Context context, @StyleRes int themeResId) {

//初始化AlertParams对象

    this.P = new AlertParams(new ContextThemeWrapper(context, AlertDialog.resolveDialogTheme(context, themeResId)));

    this.mTheme = themeResId;

}

public Context getContext() {

    return this.P.mContext;

}

public AlertDialog.Builder setTitle(@Nullable CharSequence title) {

//保存title到P中

    this.P.mTitle = title;

    return this;

}

public AlertDialog.Builder setMessage(@Nullable CharSequence message) {

//保存message

    this.P.mMessage = message;

    return this;

}

public AlertDialog.Builder setIcon(@DrawableRes int iconId) {

//保存IconId

    this.P.mIconId = iconId;

    return this;

}

//Builder其他代码略

public AlertDialog create() {//构建AlertDialog

AlertDialog dialog = new AlertDialog(this.P.mContext, this.mTheme);

//将P中的参数设置到AlertController中

this.P.apply(dialog.mAlert);

//其他设置代码略

return dialog;

}

}

}

//Dialog源码

public class Dialog implements DialogInterface, Window.Callback, KeyEvent.Callback, View.OnCreateContextMenuListener, Window.OnWindowDismissedCallback {

        //其他代码略

        public void show() {

            //前面代码略

            if (!mCreated) {

//分发onCreate

                dispatchOnCreate(null);

            } else {

                final Configuration config=mContext.getResources().getConfiguration();

                mWindow.getDecorView().dispatchConfigurationChanged(config);

            }

    //调用onStart()

            onStart();

            mDecor = mWindow.getDecorView();

            //设置参布局参数略

            mWindowManager.addView(mDecor, l);//添加到WindowManager

            mShowing = true;

            sendShowMessage();

        }

        //分发onCreate

        void dispatchOnCreate(Bundle savedInstanceState) {

            if (!mCreated) {

//调用AlertDialog的onCreate方法,创建AlertDialog视图

  onCreate(savedInstanceState);

                mCreated = true;

            }

        }

    }

//AlertController源码

public class AlertController {

//其他代码略

public void installContent() {//安装内容

//选择合适的布局

int contentView = selectContentView();

//布局添加到Window中

mWindow.setContentView(contentView);

//把dialog.mAlert对象中需要构建的元素逐个添加设置到Window上,即构建我们设置的布局发生在这一步中

setupView();

      }

}

简单流程说明

1.通过AlertDialog.Builder设置各种属性后(如:setTitle()),这些属性信息会保存在P变量中,P变量的类型为AlertController.AlertParams。

2.调用builder.create()即可返回一个AlertDialog对象。

2.1builder.create()方法中首先会创建一个AlertDialog对象,AlertDialog对象构造时会初始化WindowManager和Window。

2.2builder.create()创建完AlertDialog对象后,会调用P.apply(dialog.mAlert);即把P变量中所存储的用来构建AlertDialog对象的元素设置到了dialog.mAlert中,dialog.mAlert的类型为AlertController。

3.调用AlertDialog的show()方法,展示界面。

3.1show()方法中会调用dispatchOnCreate(null),dispatchOnCreate(null)调起onCreate(),onCreate()会调起mAlert.installContent();即安装AlertDialog的内容。

3.2installContent()中会调用mWindow.setContentView(mAlertDialogLayout);即把mAlertDialogLayout这个布局加到Window中去。

3.3 调完mWindow.setContentView(mAlertDialogLayout)后会调用setupView(),setupView()中会把dialog.mAlert对象中需要构建的元素逐个添加设置到mWindow上。

3.4 最后通过把view添加到mWindowManager上展示出来。

总结

1.builder模式隐藏了这种复杂的构建过程,只需几行简单的代码就把AlertDialog给展示出来了。

2.AlertDialog的builder中并没有抽象建造者(Builder)、Director(指挥者类)等角色。AlertDialog.Builder同时扮演了Builder、ConcreteBuilder、Director等角色,这是Android中的一种简化,也值得我们去学习使用。

简单工厂模式

定义

定义一个用于创建对象的接口,让子类决定实例化那个类

介绍

简单工厂模式属于创建模式

简单工厂模式又叫做静态工厂模式

UML类图

角色说明

Product(抽象产品类):要创建的复杂对象,定义对象的公共接口。

ConcreteProduct(具体产品类):实现Product接口。

Factory(工厂类):返回ConcreteProduct实例。

实现

1.创建抽象产品类,定义公共接口:

//抽象产品类

public abstract class Product {

    public abstract void show();

}

2.创建具体产品类,继承Product类:

//具体产品类A

public class ProductA extends Product {

    @Override

    public void show() {

        System.out.println("product A");

    }

    //具体产品类B

    public class ProductB extends Product{

        @Override

        public void show() {

            System.out.println("product B");

        }

    }

}

3.创建具体工厂类,创建具体的产品:

public class Factory {


    public static Product create(String productName) {

        Product product = null;

        //通过switch语句控制生产那种商品

        switch (productName) {

            case "A":

                product = new ProductA();

                break;

            case "B":

                product = new ProductB();

                break;

        }

        return product;

    }

}

4.测试方法:

private void test() {

    //生产ProductA

    Factory.create("A").show();

    //生产ProductB

    Factory.create("B").show();

    try{

        //生产ProductC

        Factory.create("C").show();

    }catch (NullPointerException e){

        //没有ProductC,会报错

        System.out.println("没有ProductC");

    }

}

应用场景

生成复杂对象时,确定只有一个工厂类,可以使用简单工厂模式。否则有多个工厂类的话,使用工厂模式方法。

优点

代码解耦,创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建。

缺点

1.违背开放封闭原则,若需添加新产品则必须修改工厂类逻辑,会造成工厂逻辑过于复杂

2.简单工厂模式使用静态工厂犯法,因此静态方法不能被继承和重写

3.工厂类包含所有实例(产品)的创建逻辑,若工厂类出错,则会造成整个系统都会受到影响

工厂方法模式与简单工厂模式比较

1.工厂方法模式有抽象工厂类,简单工厂模式没有抽象工厂类且其工厂类的工厂方法是静态的

2.工厂方法模式新增产品时只需要新建一个工厂类即可,符合开放封闭原则;而简单工厂模式需要直接修改工厂类,违反了开放封闭原则

简单工厂模式的优化

由于简单工厂模式新增产品是需要直接修改工厂类,违反了开放封闭原则。因此可以使用反射来创建实例对象,确保能够遵循开放封闭原则

反射实现工厂类

public class Factory {

    public static <T extends Product> T create(Class<T> clz){

        Product product = null;

        try{

            //反射出实例

          product = (Product) Class.forName(clz.getName()).newInstance();

        }catch (Exception e){

e.printStackTrace();

        }

        return (T) product;

    }

}

测试方法

public void test(){

    //生产ProductA

    Factory.create(ProductA.class).show();

    //生产ProductB

    Factory.create(ProductB.class).show();

}

总结

使用反射来实现工厂类,新增产品时无需修改工厂类,但是使用反射来创建实例对象的话会比正常使用new来创建的要慢。

观察者模式

定义

定义对象间的一种一个对多的依赖关系,当一个对象的状态发送改变时,所以依赖于它的对象都得到通知并被自动更新

介绍

1.观察者属于行为型模式

2.观察者模式又被称作发布/订阅模式

3.观察者模式主要用来解耦,将被观察者和观察者解耦,让他们之间没有没有依赖或者依赖关系很小

UML类图

角色说明

Subject(抽象主题):又叫抽象被观察者,把所有观察者对象的引用保存到一个集合里,每个主题都可以有任何数量的被观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

ConcreteSubject(具体主题):又叫具体被观察者,将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知。

Observer(抽象观察者):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

ConcreteObserver(具体观察者):实现抽象观察者定义的更新接口,当得到主题更改通知时更新自身的状态

实现

继续以送快递为例,快递员有时只是把快递拉到楼下,然后就通知收件人下楼取快递。

创建抽象观察者

定义一个接到通知的更新方法,即收件人收到通知后的反应:

//抽象观察者

public interface Observer {

    //更新方法

    public void update(String message);


}

创建具体观察者

实现抽象观察者中的方法,这里创建两个类,一个男孩类和一个女孩类,定义他们收到通知后的反应:

public class Boy implements Observer {

    //名字

    private String name;

    public Boy(String name) {

        this.name = name;

    }

    //男孩的具体反应

    @Override

    public void update(String message) {

      System.out.println(name + ",收到了信息:" + message+"屁颠颠的去取快递.");

    }

}

public class Girl implements Observer{

    //名字

    private String name;

    public Girl(String name) {

        this.name = name;

    }

    //女孩的具体反应

    @Override

    public void update(String message) {

      System.out.println(name + ",收到了信息:" + message+"让男朋友去取快递~");

    }

}

创建抽象主题

即抽象被观察者,定义添加,删除,通知等方法:

//抽象被观察者

public interface Observable {


    //添加观察者

    void add(Observer observer);

    //删除观察者

    void remove(Observer observer);

    //通知观察者

    void notify(String message);

}

创建具体主题

即具体被观察者,也就是快递员,派送快递时根据快递信息来通知收件人让其来取件:

//快递员

public class Postman implements Observable {

    //保存收件人(观察者)的信息

    private List<Observer> personList = new ArrayList<>();

    //添加收件人

    @Override

    public void add(Observer observer) {

        personList.add(observer);

    }

    //移除收件人

    @Override

    public void remove(Observer observer) {

        personList.remove(observer);

    }

    //逐一通知收件人(观察者)

    @Override

    public void notify(String message) {

        for (Observer observer : personList){

            observer.update(message);

        }

    }

}

客户端测试

public void test() {

    Postman postman = new Postman();


    Observer boy1 = new Boy("路飞");

    Observer boy2 = new Boy("路飞");

    Observer girl = new Girl("娜美");


    postman.add(boy1);

    postman.add(boy2);

    postman.add(girl);


    postman.notify("快递到了,请下楼领取");

}

说明

实际上,JDK内部也内置了Observable(抽象被观察者),Observer(抽象观察者)这两个类,我们也可以直接拿来有,其代码如下:

//抽象观察者

public interface Observer {

    //更新方法

    void update(Observable observable, Object arg);

}

//抽象被观察者

public class Observable {

    //定义改变状态,默认为false

    private boolean changed = false;

    //定义一个观察者list

    private final ArrayList<Observer> observers;

    //构造函数,初始化一个观察者list来保存观察者

    public Observable() {

        observers = new ArrayList<>();

    }

    //添加观察者,带同步字段的,所以是线程安全的

    public synchronized void addObserver(Observer o) {

        if (o == null)

            throw new NullPointerException();

        if (!observers.contains(o)) {

            observers.add(o);

        }

    }

    //删除观察者

    public synchronized void deleteObserver(Observer o) {

        observers.remove(o);

    }

    //通知所以观察者,无参数

    public void notifyObservers() {

        notifyObservers(null);

    }

    //通知所有观察者,带参数

    public void notifyObservers(Object arg) {

        Observer[] arrLocal;

        //加synchronized字段,保证多线程下操作没有问题

        synchronized (this) {

            //这里做了是否发生改变的判断,是为了防止出现无意义的更新

            if (!hasChanged())

                return;

  //ArrayList转换成一个临时的数组,这样就防止了通知,添加,移除同时发生可能导致的异常

            arrLocal = observers.toArray(new Observer[observers.size()]);

            //清除改变状态,设置为false

            clearChanged();

        }

        //遍历逐一通知

        for (int i = arrLocal.length-1; i>=0; i--)

            arrLocal[i].update(this,arg);

    }

    //清楚所有观察者

    public synchronized void deleteObservers() {

        observers.clear();

    }

    //设置被观察者为改变状态,设置为true

    protected synchronized void setChanged() {

        changed = true;

    }

    //清除改变状态,设置为false

    protected synchronized void clearChanged() {

        changed = false;

    }

    //返回当前的改变状态

    public synchronized boolean hasChanged() {

        return changed;

    }

    //观察者数量

    public synchronized int countObservers() {

        return observers.size();

    }

}

应用场景

1.当一个对象的改变需要通知其他对象改变时,而且它不知道具体有多少个对象有待改变时。

2.当一个对象必须通知其它对象,而它又不能假定其它对象是谁。

3.跨系统的消息交换场景,如消息队列、事件总线的处理机制。

优点

1.解除观察者与主题之间的耦合。让耦合双方都依赖于抽象,而不是依赖具体。从而使得自己的变换都不会影响另一边的变换。

2.易于扩展,对同一主题新增观察者时无需修改原有代码。

缺点

1.依赖关系并未完全解除,抽象主题仍然依赖抽象观察者。

2.使用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。

3.可能会引起多余的数据通知。

Android中的源码分析

1.控件中的Listener监听方式

Android中我们遇到最常用的观察者模式就是各种控件的监听,如下:

Button button = findViewById(R.id.button);

button.setOnClickListener(new View.OnClickListener() {

    @Override

    public void onClick(View v) {

        Log.d(TAG, "onClick: ");

    }

});

上面的代码中,button就是具体的主题,也就是被观察者;new出来的View.OnClickListener对象就是具体的观察者;OnClickListener实际上就是个接口,也就是抽象观察者;通过setOnClickListener把观察者注册到被观察者中。

一旦button捕获的点击事件,即状态发生变化的时候,就会通过回调注册的OnClickListener观察者的onClick方法会来通知观察者,Button状态发生变化。

1.相关源码分析:

//抽象观察者

public interface OnClickListener {

//只有onClick这个方法

    void onClick(View v);

}

public void setOnClickListener(@Nullable OnClickListener l) {

    if (!isClickable()) {

  //设置为可点击

        setClickable(true);

    }

//把传入的 OnClickListener 对象赋值给了 getListenerInfo().mOnClickListener,即mListenerInfo的mOnClickListener持有OnClickListener对象的引用

    getListenerInfo().mOnClickListener = l;

}

//返回ListenerInfo对象,这里是一个单例模式

ListenerInfo getListenerInfo() {

    if (mListenerInfo != null) {

        return mListenerInfo;

    }

    mListenerInfo = new ListenerInfo();

    return mListenerInfo;

}

//执行点击事件

public boolean performClick() {

    final boolean result;

    final ListenerInfo li = mListenerInfo;

    if (li != null && li.mOnClickListener != null) {

        playSoundEffect(SoundEffectConstants.CLICK);

  //执行onClick方法,li.mOnClickListener即OnClickListener对象

        li.mOnClickListener.onClick(this);

        result = true;

    } else {

        result = false;

    }

    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

    notifyEnterOrExitForAutoFillIfNeeded(true);

    return result;

}

2.Adapter的notifyDataSetChanged()方法

当我们使用ListView时,需要更新数据时我们就会调用Adapter的notifyDataSetChanged()方法,那么我们来看看notifyDataSetChanged()的实现原理,这个方法是定义在BaseAdapter中具体代码如下:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {

//数据集被观察者

    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    public boolean hasStableIds() {

        return false;

    }

    //注册观察者

    public void registerDataSetObserver(DataSetObserver observer) {

        mDataSetObservable.registerObserver(observer);

    }

//注销观察者

    public void unregisterDataSetObserver(DataSetObserver observer) {

        mDataSetObservable.unregisterObserver(observer);

    }

    //数据集改变时,通知所有观察者

    public void notifyDataSetChanged() {

        mDataSetObservable.notifyChanged();

    }

}

//其他代码略

由上面的代码可以看出BaseAdapter实际上就是使用了观察者模式,BaseAdapter就是具体的被观察者。接下来mDataSetObservable.notifyChanged()的实现:

public class DataSetObservable extends Observable<DataSetObserver> {

    public void notifyChanged() {

        synchronized(mObservers) {

  //遍历所有观察者,并调用他们的onChanged()方法

            for (int i = mObservers.size() - 1; i >= 0; i--) {

                mObservers.get(i).onChanged();

            }

        }

    }

//其他代码略

}

现在我们看到了有观察者额影子,那么这些观察者时从哪里来的呢?实际上这些观察者是在ListView通过setAdapter()设置Adapter时产生的:

public class ListView extends AbsListView {

//其他代码略

public void setAdapter(ListAdapter adapter) {

//如果已存在Adapter,先注销该Adapter的观察者

    if (mAdapter != null && mDataSetObserver != null) {

        mAdapter.unregisterDataSetObserver(mDataSetObserver);

    }

//其他代码略

super.setAdapter(adapter);

if (mAdapter != null) {

    mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();

    mOldItemCount = mItemCount;

//获取Adapter中的数据的数量

  mItemCount = mAdapter.getCount();

  checkFocus();

//创建一个数据集观察者

    mDataSetObserver = new AdapterDataSetObserver();

//注册观察者

    mAdapter.registerDataSetObserver(mDataSetObserver);

//其他代码略

}

}

从上面的代码可以看到,观察者有了,那么这个观察者主要是干什么的呢?

class AdapterDataSetObserver extends DataSetObserver {

    private Parcelable mInstanceState = null;

    //观察者的核心实现

    @Override

    public void onChanged() {

        mDataChanged = true;

        mOldItemCount = mItemCount;

//获取Adapter中的数据的数量

        mItemCount = getAdapter().getCount();

        if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null

                && mOldItemCount == 0 && mItemCount > 0) {

            AdapterView.this.onRestoreInstanceState(mInstanceState);

            mInstanceState = null;

        } else {

            rememberSyncState();

        }

        checkFocus();

        //重新布局

        requestLayout();

    }

    //其他代码略

}

最终就是在AdapterDataSetObserver这个类里面的onChanged()方法中实现了布局的更新。

简单总结

当ListView的数据发生变化时,我调用Adapter的notifyDataSetChanged()方法,这个方法又会调用所以观察者(AdapterDataSetObserver)的onChanged()方法,onChanged()方法又会调用requestLayout()方法重新进行布局。

BroadcastReceiver

BroadcastReceiver作为Android的四大组件之一,实际上也是一个典型的观察者模式,通过sendBroadcast发送广播时,只有注册了相应的IntentFilter的BroadcastReceiver对象才会收到这个广播信息,其onReceive方法才会被调起,BroadcastReceiver的代码比较复杂

其他

另外,一些著名的第三方事件总线库,比如RxJava、RxAndroid、EventBus等等,也是使用了观察者模式,有兴趣的可以去看一下他们的源码。

工厂模式

定义

定义一个用于创建对象的接口,让子类决定实例化哪个类

介绍

1.工厂方法模式属于创键型模式。

2.工厂方法模式主要用来创建复杂的对象,简单对象能够使用new来创建就不用工厂方法模式来创建

UML类图

角色说明

Product(抽象产品类):要创建的复杂对象,定义对象的公共接口。

ConcreteProduct(具体产品类):实现Product接口。

Factory(抽象工厂类):该方法返回一个Product类型的对象。

ConcreteFactory(具体工厂类):返回ConcreteProduct实例。

实现

1.创建抽象产品类,定义公共接口:

//抽象产品类

public abstract class Product {

    public abstract void show();

}

2.创建具体产品类,继承Product类:

//具体产品类A

public class ProductA extends Product {

    @Override

    public void show() {

        System.out.println("product A");

    }

}

//具体产品类B

public class ProductB extends Product {

    @Override

    public void show() {

        System.out.println("product B");

    }

}

3.创建抽象工厂类,定义公共接口:

//抽象工厂类

public abstract class Factory {

    public abstract Product create();

}

4.创建具体工厂类,继承抽象工厂类,实现创建具体的产品:

//具体工厂类A

public class FactoryA extends Factory {

    @Override

    public Product create() {

        //创建ProductA

        return new ProductA();

    }

}

//具体工厂类B

public class FactoryB extends Factory {

    @Override

    public Product create() {

        //创建ProductB

        return new ProductB();

    }

}

测试方法:

public void test() {

    //产品A

    FactoryA factoryA = new FactoryA();

    Product productA = factoryA.create();

    productA.show();

    //产品B

    FactoryB factoryB = new FactoryB();

    Product productB = factoryB.create();

    productB.show();

}

应用场景

生成复杂对象时,无需知道具体类名,只需知道想应的工厂方法即可。

优点

1.符合开放封闭原则。新增产品时,只需增加相应的具体产品类和相应的工厂子类即可

2.符合单一职责原则。每个具体工厂类只负责创建对应的产品。

缺点

1.一个具体工厂只能创建一种具体产品。

2.增加新产品时,还需要增加相应的工厂类,系统类的个数将成对增加,增加了系统的复杂度和性能开销

3.引入的抽象类也会导致类结构的复杂化

Android中的源码分析

Android中的ThreadFactory就是使用了工厂方法模式来生成线程的,线程就是ThreadFactory的产品

ThreadFactory相关源码分析

//抽象产品:Runnable

public interface Runnable {

    public abstract void run();

}

//具体产品:Thread

public class Thread implements Runnable {

//构造方法

public Thread(Runnable target, String name) {

  init(null, target, name, 0);

}

@Override

//实现抽象产品的抽象方法

public void run() {

    if (target != null) {

            target.run();

    }

}

//其他代码略

}

//抽象工厂:ThreadFactory

public interface ThreadFactory {

    Thread newThread(Runnable r);

}

//具体工厂:AsyncTask中的实现

private static final ThreadFactory threadFactory = new ThreadFactory() {

    private final AtomicInteger mCount = new AtomicInteger(1);

    //实现抽象工厂的抽象方法

    @Override

    public Thread newThread(Runnable r) {

        //返回Thread这个产品

        return new Thread(r,"AsyncTask #"+mCount.getAndIncrement());

    }

};

总结

1.这里只要是介绍Android系统中工厂模式的应用,线程和AsyncTask的原理就不说了。

2.通过ThreadFactory,我们可以创建出不同的Thread来。

3.同样,我们可以创建另外类似的工厂,生产某种专门的线程,非常容易扩展。

责任链模式

定义

一个请求沿着一条“链”传递,直到该“链”上的某个处理者处理它为止。

介绍

1.责任链模式属于行为型模式。

2.多个对象中,每个对象都持有下一个对象的引用,这就构成了链这种结构。

3.一个请求通过链的头部,一直往下传递到链上的每一个结点,直到有某个结点对这个请求做出处理为止,这就是责任链模式。

4.责任链模式一般分为处理者与请求者。具体的处理者分别处理请求者的行为。

UML类图

角色说明

Handler(抽象处理者):抽象类或者接口,定义处理请求的方法以及持有下一个Handler的引用。

ConcreteHandler1,ConcreteHandler2(具体处理者):实现抽象处理类,对请求进行处理,如果不处理则转发给下一个处理者。

Client(客户端):即要使用责任链模式的地方。

实现

以送快递为例,单个快递员只负责某个片区的快递,若某个快递目的地不属于当前的片区,则交给下一个快递员来处理,直到有人处理为止。

创建抽象处理者类

定义处理请求的方法以及持有下一个Handler的引用:

//快递员抽象类

public abstract class Postman {

    //下一个快递员

    protected Postman nextPostman;

    //派送快递

    public abstract void handleCourier(String address);

}

创建具体处理者类

实现抽象处理者类中的方法:

//北京快递员

public class BeijingPostman extends Postman{

    @Override

    public void handleCourier(String address) {

        //北京地区的则派送

        if (address.equals("Beijing")){

            System.out.println("派送到北京");

            return;

        }else{

            //否则交给下一个快递员去处理

            nextPostman.handleCourier(address);

        }

    }

}

//上海快递员

public class ShanghaiPostman extends Postman{

    @Override

    public void handleCourier(String address) {

        if(address.equals("Shanghai")){

            System.out.println("派送到上海");

            return;

        }else{

            nextPostman.handleCourier(address);

        }

    }

}

//广州快递员

public class GuangzhouPostman extends Postman{

    @Override

    public void handleCourier(String address) {

        if(address.equals("Guangzhou")){

            System.out.println("派送到广州");

            return;

        }else{

            nextPostman.handleCourier(address);

        }

    }

}

客户端测试

public void test() {

    //创建不同的快递员对象

    BeijingPostman beijingPostman = new BeijingPostman();

    ShanghaiPostman shanghaiPostman = new ShanghaiPostman();

    GuangzhouPostman guangzhouPostman = new GuangzhouPostman();

    //创建下一个结点

    beijingPostman.nextPostman = shanghaiPostman;

    shanghaiPostman.nextPostman = guangzhouPostman;

    //处理不同地区的快递,都是从首结点北京快递员开始

    System.out.println("有一个上海快递需要派送:");

    beijingPostman.handleCourier("Shanghai");

    System.out.println("有一个广州快递需要派送:");

    beijingPostman.handleCourier("Guangzhou");

    System.out.println("有一个美国快递需要派送:");

    beijingPostman.handleCourier("America");

}

说明

1.上面的请求只是一个简单的地址字符串,如果是一些复杂的请求,可以封装成独立的对象。如:普通快递和生鲜快递,生鲜快递还需快递员做冷链处理等等。

2.请求实际上可以从责任链中的任意结点开始,即可以从上海快递员开始处理也行。

3.责任链中的结点顺序实际也可以调整,即北京->广州->上海的顺序也行。

4.责任链也可以过某些结点去处理请求,如北京->广州,越过了上海。

5.对于请求,只有两种结果:一个某个结点对其进行了处理,如上面例子上海、广州快递,这种叫纯的责任链;另一个则是所以结点都不进行处理,如美国的快递,这种叫不纯的责任链模式。我们所见到的基本都是不纯的责任链。

应用场景

1.多个对象处理同一请求时,但是具体由哪个对象去处理需要运行时做判断。

2.具体处理者不明确的情况下,向这组对象提交了一个请求。

优点

1.代码的解耦,请求者与处理者的隔离分开。

2.易于扩展,新增处理者往链上加结点即可。

缺点

1.责任链过长的话,或者链上的结点判断处理时间太长的话会影响性能,特别是递归循环的时候。

2.请求有可能遍历完链都得不到处理

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

推荐阅读更多精彩内容