单例模式+观察者模式+抽象工厂模式+工厂方法模式

单例模式


  • 定义:确保一个类只有一个实例,并且自动实例化为系统提供这个实例。
  • 要点:
    • 私有构造器
    • 通过静态方法或者枚举类返回单例对象
    • 确保多线程环境下单例只有一个
    • 确保单例对象在反序列化时不会重新构建对象
  • 代码

    • 饿汉方式。指全局的单例实例在类装载时构建。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      public class Singleton {
      private static final Singleton singleton = new Singleton();
      //限制产生多个对象
      private Singleton(){
      }
      //通过该方法获得实例对象
      public static Singleton getSingleton(){
      return singleton;
      }
      //类中其他方法,尽量是static
      public static void doSomething(){
      }
      }
    • 懒汉方式。指全局的单例实例在第一次被使用时构建。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      public class Singleton {
      private static volatile Singleton INSTANCE = null;
      // Private constructor suppresses
      // default public constructor
      private Singleton() {}
      //thread safe and performance promote
      public static synchronized Singleton getInstance() {
      if(INSTANCE == null){
      INSTANCE = new Singleton();
      }
      return INSTANCE;
      }
      }

      懒汉方式的优点:单例只有在需要使用的时候才会实例化,一定程度节约资源。缺点:第一次加载时需要及时进行实例化,反应稍慢,最大的问题是每次调用时都进行同步,造成不必要的同步开销。

    • DCL (Double Check Lock)实现单例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      public class Singleton {
      private static volatile Singleton INSTANCE = null;
      // Private constructor suppresses
      // default public constructor
      private Singleton() {}
      //thread safe and performance promote
      public static Singleton getInstance() {
      if(INSTANCE == null){
      synchronized(Singleton.class){
      //when more than two threads run into the first null check same time, to avoid instanced more than one time, it needs to be checked again.
      if(INSTANCE == null){
      INSTANCE = new Singleton();
      }
      }
      }
      return INSTANCE;
      }
      }

      第一层判断是为了避免不必要的同步,第二层判断为了在null的情况下创建实例。
      但是由于INSTANCE = new Singleton();不是原子的,大致有以下三步:

    • 1)给singleton的实例分配内存
    • 2)调用singleton的构造函数,初始化成员变量
    • 3)将INSTANCE对象指向分配的内存空间

      由于jdk1.5之前JMM(java内存模型)中cache,寄存器到主内存回写顺序的规定,2)和3)顺序先后是无法保证的。如果在3执行完毕,2执行之前切换到另一个线程就会导致单例未实例化。即DCL失效问题。

      在jdk1.5之后sun调整了JVM,将INSTANCE的定义改成 private volatile static Singleton INSTANCE=null就可以保证单例每次都是从内存中读取。

      DCL的优点:资源利用率高,第一次获取单例的时候才会实例化,效率高。缺点:第一次加载的时候反应稍慢,也由于java内存模型的原因偶尔会失败。高并发环境下也有一定缺陷,虽然发生概率较小。
      DCL是使用最多的单例模式。

    • 静态内部类实现单例:推荐,线程安全以及延迟单例的实例化。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      public class Singleton {
      private Singleton(){}
      //通过该方法获得实例对象
      public static Singleton getSingleton(){
      return SingletonHolder.singleton;
      }
      private static class SingletonHolder{
      private static final Singleton singleton=new Singleton();
      }
      }
    • 枚举单例:优点在于写法简单,线程安全,同时反序列化的时候不会创建新的对象。

      1
      2
      3
      4
      5
      6
      public enum SingletonEnum{
      INSTANCE;
      public void doSomething(){
      }
      }

      上述方法中除了枚举外要防止反序列化时重新生成对象就必须加入以下方法:

      1
      2
      3
      private Object readResolve() throws ObjectStreamExecption{
      return INSTANCE;
      }
    • 容器实现单例:多种单例类型注入到一个同意的管理类中,并通过key获取。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      public class SingletonManager{
      private static Map<String,Object> objMap=new HashMap<String,Object>();
      private SingletonManager(){}
      public static void registerService(String key,Object INSTANCE){
      if(!objMap.containsKey(key)){
      objMap.put(key,INSTANCE);
      }
      }
      public static Object getService(String key){
      return objMap.get(key);
      }
      }

Android中较为推荐的是双重检验锁和静态内部类实现的单例模式。

Adapter中的观察者模式

什么是观察者模式

结构
Alt text
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式所涉及的角色有:

  • 抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。

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

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

  • 具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。


  • 观察者模式的python代码实现:
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    class AbstractSubject(object):
    def register(self, listener):
    raise NotImplementedError("Must subclass me")
    def deregister(self, listener):
    raise NotImplementedError("Must subclass me")
    def notify_listeners(self, event):
    raise NotImplementedError("Must subclass me")
    class Listener(object):
    def __init__(self, name, subject):
    self.name = name
    subject.register(self)
    def notify(self, event):
    print self.name, "received event", event
    class Subject(AbstractSubject):
    def __init__(self):
    self.listeners = []
    self.data = None
    def getUserAction(self):
    self.data = raw_input('Enter something to do:')
    return self.data
    # Implement abstract Class AbstractSubject
    def register(self, listener):
    self.listeners.append(listener)
    def deregister(self, listener):
    self.listeners.remove(listener)
    def notify_listeners(self, event):
    for listener in self.listeners:
    listener.notify(event)
    if __name__=="__main__":
    # make a subject object to spy on
    subject = Subject()
    # register two listeners to monitor it.
    listenerA = Listener("<listener A>", subject)
    listenerB = Listener("<listener B>", subject)
    # simulated event
    subject.notify_listeners ("<event 1>")
    # outputs:
    # <listener A> received event <event 1>
    # <listener B> received event <event 1>
    action = subject.getUserAction()
    subject.notify_listeners(action)
    #Enter something to do:hello
    # outputs:
    # <listener A> received event hello
    # <listener B> received event hello

    在listview中,我们知道,当数据变化时,listview呈现的内容是会更新的。这实际上是用到了adapter中的观察者模式(当然listview中利用了adapter模式,这个以后再讲)。adapter内部有一个可观察者类,listview则作为他的一个观察者,将adapter设置给listview时,listview会被注册到这个观察者对象中。接下来我们就从adapter的源码入手,分析一下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @override
    public void setAdapter(ListAdapter adapter){
    resetList();
    //清空视图缓存mRecycler
    mRecycler.clear();
    if (mAdapter!=null) {
    mDataSetObserver=new AdapterDataSetObserver();
    mAdapter.registerDataSetObserver(mDataSetObserver);
    }else {
    //代码省略
    }
    requestLayout();
    }

    从以上程序中我们可以看出,设置adapter时创建了一个AdapterDataSetObserver对象,并注册到adapter中。刚才不是说listview是观察者吗?这会儿怎么成了AdapterDataSetObserver了。我们先放下这个疑问继续往下看。
    首先我们看常用的adapter基类BaseAdapter,部分代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public abstract class BaseAdapter implements ListAdapter,SpinnerAdapter{
    private final DataSetObservable mDataSetObservable=new DataSetObservable();
    public void registerDataSetObserver(DataSetObserver dataSetObserver){
    mDataSetObservable.registerObserver(dataSetObserver);
    }
    public void notifyDataSetChanged(){
    mDataSetObservable.notifyChanged();
    }
    //代码省略
    }

    从以上可以看出,注册观察者实际上调用了DataSetObeservable对应的函数。DataSetObeservable拥有一个观察者集合,当可观察者改变时,就会通知观察者做出相应的处理。
    当adapter的数据变化时,我们会调用adapter的notifyDataSetChanged函数,该函数又会调用DataSetObeservable对象的notifyChanged()函数通知所有观察者数据发生了变化,使观察者进行相应的操作。代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class DataSetObeservable extends Observable<DataSetObeserver>{
    public void notifyChanged(){
    synchronized (mObservers){
    for (int i=mObservers.size()-1;i>=0;i--){
    mObservers.get(i).onChanged();//调用观察者的onChanged()函数
    }
    }
    }
    }

    对listview来说这个观察者就是AdapterDataSetObeserver对象,该类声明在AdapterView中,也是listview的一个父类。AdapterDataSetObeserver代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //AdapterView的内部类AdapterDataSetObeserver中
    class AdapterDataSetObeserver extends DataSetObserver{
    @Override
    public void onChanged() {
    mDataChanged=true;
    mOldItemCount=mItemCount;
    //获取元素个数
    mItemCount=getAdapter().getCount();
    //代码省略
    checkFocus();
    //重新布局
    requestLayout();
    }
    //代码省略
    }

    在AdapterDataSetObeserver的onChanged()函数中会调用viewGroup的requestLayout()进行重新策略,布局,绘制整个listview的item view,执行完之后,整个listview的元素就发生了变化。

    现在我们回到之前的额问题,就是listview不是观察者,而AdapterDataSetObeserver才是真正的观察者的问题。在AdapterDataSetObeserver的onChanged()函数中实际调用的是AdapterView中的方法来完成功能,所以AdapterDataSetObeserver只是在外层做了一层封装,真正核心的功能应该是AdapterView。listview就是通过Adapter模式,观察者模式,item view复用机理实现了高效列表显示。
    这里对于item view的复用机理介绍一下:
    在Adapter中要重写四个函数:

  • getCount()获取数据个数
  • getItem(int)获取指定位置的数据
  • getItemId(int)获取position位置的id,一般就返回position即可
  • getView(int,View,ViewGroup)获取position上的ItemView视图。
    当处理数据量较大时,对于滚出屏幕外的item view会进入listview的一个Recycler中,Recycler将视图缓存。当屏幕滑动加载新的itemview时,如果该视图存在,则直接从缓存中取出,如果过不存在就直接创建新的视图。这也就是为什么用viewholder可以优化listview的原因。
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder viewHolder=null;
    if(convertView==null){
    viewHolder=new ViewHolder();
    convertView=mInflater.inflate(R.layout.medal_item,null);
    viewHolder.medal_item_tv= (TextView) convertView.findViewById(R.id.medal_tv_item);
    convertView.setTag(viewHolder);
    }else{
    viewHolder= (ViewHolder) convertView.getTag();
    }
    viewHolder.medal_item_tv.setText(data.get(position));
    return convertView;
    }
    public final class ViewHolder{
    public TextView medal_item_tv;
    }

    抽象工厂模式


    • 定义:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
    • UML类图:

    • 代码示例:
      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
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      public abstract class AbstractCreator {
      //创建A产品家族
      public abstract AbstractProductA createProductA();
      //创建B产品家族
      public abstract AbstractProductB createProductB();
      }
      public class Creator1 extends AbstractCreator {
      //只生产产品等级为1的A产品
      public AbstractProductA createProductA() {
      return new ProductA1();
      }
      //只生产铲平等级为1的B产品
      public AbstractProductB createProductB() {
      return new ProductB1();
      }
      }
      public class Creator2 extends AbstractCreator {
      //只生产产品等级为2的A产品
      public AbstractProductA createProductA() {
      return new ProductA2();
      }
      //只生产铲平等级为2的B产品
      public AbstractProductB createProductB() {
      return new ProductB2();
      }
      }
      public class Client {
      public static void main(String[] args) {
      //定义出两个工厂
      AbstractCreator creator1 = new Creator1();
      AbstractCreator creator2 = new Creator2();
      //产生A1对象
      AbstractProductA a1 = creator1.createProductA();
      //产生A2对象
      AbstractProductA a2 = creator2.createProductA();
      //产生B1对象
      AbstractProductB b1 = creator1.createProductB();
      //产生B2对象
      AbstractProductB b2 = creator2.createProductB();
      /*
      * 然后在这里就可以为所欲为了...
      */
      }
      }

    工厂方法模式


    • 定义:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。
    • UML类图:

    • 举例:女娲造人(详情见《设计模式之禅》第八章)

      Alt text

      这里重点给出工厂的代码
      1
      2
      3
      public abstract class AbstractHumanFactory {
      public abstract < T extends Human> T createHuman(Class< T> c);
      }

    这里T必须满足:是class类型,并且是human的实现类。
    工厂子类如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class HumanFactory extends AbstractHumanFactory {
    public < T extends Human> T createHuman(Class< T> c){
    //定义一个生产的人种
    Human human=null;
    try {
    //产生一个人种
    human = (T)Class.forName(c.getName()).newInstance();
    } catch (Exception e) {
    System.out.println("人种生成错误!");
    }
    return (T)human;
    }
    }

    • 完整的实现代码模板如下:

      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
      33
      34
      35
      36
      37
      38
      //抽象产品类
      abstract class Product{
      public void method1(){}//公共方法
      public abstract void method2();//抽象方法
      }
      //具体产品类
      class CreateProduct1 extends Product{
      public void method2(){}
      }
      class CreateProduct2 extends Product{
      public void method2(){}
      }
      //抽象工厂
      abstract class Creator{
      public abstract < T extends Product> T createProduct(Class< T> c);
      }
      //具体工厂
      class CreateCreator extends Creator{
      public < T extends Product> T createProduct(Class< T> c){
      Product p = null;
      try{
      p = (Product) Class.forName(c.getName()).newInstance();
      }catch(Exception e){
      }
      return (T)p;
      }
      }
      //场景类
      class Client{
      public static void main(String[] args){
      Creator creator = new CreateCreator();
      Product p = creator.createProduct(CreateProduct1.class);
      }
      }
    • 优点:

      • 首先:良好的封装性,降低模块间的耦合。比如创建一个产品对象,只需要知道其类名或者约束字符串就可以了。
      • 其次:扩展性好。增加产品类时只需要适当修改工厂具体类或者新建工厂类就可以拥抱变化。
      • 再次:屏蔽产品类。产品类的实现如何变化,调用者都不用关心,只要求产品接口保持不变。例如:使用JDBC连接数据库,数据库从MySQL切换到Oracle,只需要改动一下驱动的名称。
      • 最后:工厂方法模式是典型的解耦框架。高层模块只需要知道产品的抽象类,其他的实
        现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依
        赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类
    • 使用场景

      • 工厂方法模式是new一个对象的替代品,所以在需要new 对象的地方都可以使用。但需慎重考虑是否要增加一个工厂类进行管理,以免增加代码复杂度。
      • 需要灵活的、可扩展的框架时,可以考虑。ex:设计一个连接邮件服务器的框架,POP3、IMAP、HTTP三协议的连接方式作为产品类来处理。当某些邮件服务器扩展了webservice协议时,只需要增加webservice的产品类
      • 可以用在异构项目中。
        ex:通过webservice与一个非java的项目交互,虽然webServcie号称是可以做到异构系统的同构化,但是在实际的开发中,会碰到类型问题、WSDL文件的支持问题等。从WSDL中产生的对象都认为是一个产品,然后由具体工厂管理,减少与外围的耦合。
      • 可以使用在测试框架中。ex:测试类A,就需要把与A有联系的B也同时产生出来。可以使用工厂方法模式把B虚拟出来,避免A与B 的耦合。可对比JMock、EasyMock。
    • 扩展
      • 简单工厂模式:一个模块只需要一个工厂类,没必要new工厂实例出来,只需要使用静态方法即可。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //具体工厂
    class CreateCreator {
    public static < T extends Product> T createProduct(Class< T> c){
    Product p = null;
    try{
    p = (Product) Class.forName(c.getName()).newInstance();
    }catch(Exception e){
    }
    return (T)p;
    }
    }
    //场景类
    class Client{
    public static void main(String[] args){
    Product p = CreateCreator.createProduct(CreateProduct1.class);
    }
    }
    • 多工厂类:项目比较复杂,初始化一个对象比较费力(设定初始值),所有产品类都放在一个工厂方法中进行初始化会使代码结构不清晰。为每个产品定义一个创造者,然后由调用者自己去选择与哪个工厂方法关联。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      //具体工厂1
      class CreateCreator1 extends Creator{
      public Product createProduct(){
      return new CreateProduct1();
      }
      }
      //具体工厂2
      class CreateCreator2 extends Creator{
      public Product createProduct(){
      return new CreateProduct2();
      }
      }
      //场景类
      class Client{
      public static void main(String[] args){
      Product cp1 = (new CreateCreator1()).createProduct();
      Product cp2 = (new CreateCreator2()).createProduct();
      }
      }
    • 替代单例模式.不让通过正常方式new一个产品对象,而是通过工厂用反射的方法创建对象。项目中可以建一个单例构造器,只要输入单例的类型就可以获得唯一的实例。通过获得类构造器,然后设置访问权限,生成一个对象,然后提供外部访问,保证内存
      中的对象唯一。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      class Singleton{
      private Singleton(){}
      public void doSomething(){}
      }
      class SingletonFactory{
      private static Singleton single;
      static{
      try {
      Class cls = Class.forName(Singleton.class.getName());
      Constructor constructor = cls.getDeclaredConstructor();
      constructor.setAccessible(true);
      single = (Singleton) constructor.newInstance();
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      public static Singleton getSingleton(){
      return single;
      }
      }
    • 延迟初始化:一个对象被消费完毕,不立即释放,工厂类保持其初始状态,等待再次被使用。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      public class ProductFactory {
      // 缓存容器
      private static final Map< String, Product> prMap = new HashMap< String, Product>();
      public static synchronized Product createProduct(String type) throws Exception {
      Product product = null;
      if (prMap.containsKey(type)){
      product = prMap.get(type);
      } else {
      if (type.equals("Product1")) {
      product = new ConcreteProduct1();
      } else {
      product = new ConcreteProduct2();
      }
      // 同时把对象放到缓存容器中
      prMap.put(type, product);
      }
      return product;
      }
      }

    延迟加载框架是可以扩展的,例如限制某一个产品类的最大实例化数量,可以通过判断
    Map中已有的对象数量来实现,这样的处理是非常有意义的,例如JDBC连接数据库,都会
    要求设置一个MaxConnections最大连接数量,该数量就是内存中最大实例化的数量。