• 设计模式简介(转)


    一 设计模式简介

      设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 

    二 设计模式分类

    总体来说设计模式分为三类

    创建型模式,共五种:工厂方法模式抽象工厂模式单例模式、建造者模式、原型模式。

    意义:创建对象时,不再直接实例化对象,而是根据特定场景,有程序确定创建对象的方式,从而保证更大的性能、更好的性能优势。

    结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

    意义:用于帮助将多个对象组织成更大的结构。

    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    其实还有两类:并发型模式和线程池模式。

    意义:用于帮助系统间各对象的通信,如何控制复杂系统的流程。

     三 设计模式的六大原则

    1 单一职责原则:不要存在多于一个导致类变更的原因,即一个类只需实现一项职责,主要可以实现高内聚、低耦合。

    2 开放封闭原则:对于扩展是开放的,对于修改是关闭的。(目的)

    3 里氏替换原则:任何基类可以出现的地方,子类一定可以出现。(实现)

    4 依赖倒置原则:高层次的模块、低层次的模块与具体实现都应该依赖于抽象,也可以说应该依赖于接口编程。(方法)

    5 接口隔离原则:接口应该只处理一类定制的方法。

    6 迪米特法则:又叫最少知道原则,就是说一个对象应当对其他对象有尽可能少的了解,也可以说一个类应该对自己需要耦合或调用的类知道得最少。

    四 单例模式介绍

    1 单例模式概念:单例模式确保某一个类有且仅有有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。

    2 常见实现方法:

    1. 懒汉式,线程不安全
    2. 懒汉式,线程安全
    3. 饿汉式
    4. 饿汉式,变种
    5. 静态内部类
    6. 双重检验锁
    7. 登记式
    1)懒汉式,线程不安全,代码如下:

    public classSingleton {

      private static Singleton instance;

      private Singleton() {

      }

      public static Singleton getInstance(){

        if (instance == null) {

        instance = new Singleton();

      }

      return instance;

    }

    }

    上述代码很简单,但是是线程不安全的,多线程情况下不能正常工作,会创建多个实例。
     
     
    2)懒汉式,线程安全,代码如下:
    public classSingleton {
    
      private static Singleton instance;
    
      private Singleton(){
    
      }
    
      public static synchronized Singleton getInstance() {//同步锁
    
        if (instance== null) {#1
    
        instance = new Singleton();#2
    
      }
    
      return instance;#3
    
      }
    
    }
    当系统有两个线程线程1和线程2,两线程到达#1前,若线程1(或线程2)抢到锁,执行#2,#3后释放锁,若线程2(或线程1)抢到锁,会执行#2,由于instance为静态类变量,此时的instance已由线程1实例化了,因此线程2没有执行#2,因此系统只有一个实例化对象。所以该代码是线程安全的。缺点:尽管该代码是线程安全的,能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是效率低,99%情况下不需要加锁、解锁的同步操作。

     
    3)饿汉式,代码如下:
    public class Singleton {
    
       private static Singleton instance = new Singleton();
    
       private Singleton (){
    
       }
    
        public static Singleton getInstance() {
    
         return instance;
    
       }
    
    }

    上述代码在类一加载便实例化instance,因此称为饿汉式

    4)饿汉式,变种,代码如下:
    public class Singleton { 
    
      private Singleton instance = null; 
    
      static {
    
        instance = new Singleton(); 
    
      }
    
      private Singleton (){} 
    
      public static Singleton getInstance() { 
    
        return this.instance; 
    
    } 
    
    }
    上述代码是3)的变种,其实是一样的。
     
     
    5)静态内部类,代码如下:
    public class Singleton {
    
      private static class SingletonHolder {
    
      private static final Singleton INSTANCE = newSingleton();
    
      }
    
      private Singleton (){}
    
      public static final Singleton getInstance(){
    
        return SingletonHolder.INSTANCE;
    
     }
    
    }

    相对于第三种方式,这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显式通过调用getInstance方法时,才会显式装载SingletonHolder类,从而实例化instance。例如,如果实例化instance很消耗资源,我想让其延迟加载,另一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。

     

    6)双重校验锁:“双重检查加锁”的方式可以既实现线程安全,又能够使性能不受到很大的影响。

    所谓双重检查加锁机制指的是:并不是每次进getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。

    重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。(对于volatile修饰的变量,系统内的线程所有的write都将先行发生于read)

    说明:由于volatile关键字可能会屏蔽掉虚拟机中的一些必要的代码优化,所以运行效率并不是很高。因此一般建议,没有特别的需要,不要使用。也就是说,虽然可以使用“双重检查加锁”机制来实现线程安全的单例,但并不建议大量采用,可以根据情况来选用。

    public class Singleton {
    
      private volatile static Singleton singleton;
    
      private Singleton (){}
    
      public static Singleton  getInstance() { 
    
        if (singleton == null) {#2
    
        synchronized (Singleton.class) {
    
          if (singleton == null) { 
    
          singleton = new Singleton(); 
    
          } 
    
        } 
    
        }  
      return singleton; 
    
      } 
    
    } 

    注:很显然上述代码是线程安全的,然而细心的朋友可以发现,如果把去掉第一重判断#2,此时的代码也是线程安全的,与单例第二种实现方式没有本质的区别,那么为什么加上#2呢,好处有:提高代码运行效率,当线程1实例了singleton后,此时线程2判断singleton不为空,那么synchronized同步代码并需要执行,因为加锁、解锁很大的影响系统性能,尽量少用。

    7)登记式,可继承:
    细心的朋友可以看出懒汉式或者恶汉式的单例模式并不能被继承,并没有体现面向对象的优势,看到网上有博友实现的登记式代码很长很繁琐,因此贴出相对简洁的可继承的登记式单例模式的代码是很有必要的,我也是才学习到,是腾讯大神分享给我的。
    下面代码是Google公司自己写的代码,不愧是牛逼公司,代码简洁,代码如下:
    public abstract class Singleton<T> {
    
    private T mInstance;
    
        protected abstract T create();
    
        public final T get() {
    
            synchronized (this) {
    
                if (mInstance == null) {
    
                    mInstance = create();
    
                }
    
                return mInstance;
    
            }
    
        }
    
    }
     
    由于静态类是需要继承才能实现起内部方法,是不是so easy!
    如何继承呢?
     
    public class AManager {
    
      private static final Singleton<AManager> sInstance = new Singleton<AManager>()
    
      @Override
    
      protected AManager create() {
    
        return new AManager();
    
      }
    
    };
    
      public static AManager getInstance(){
    
        return sInstance.get();
    
      }
      
    private AManager(){     System.out.println("AManager created!");   } }

    参考:http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html

         http://blog.csdn.net/u014539776/article/details/50775470

  • 相关阅读:
    AC自动机+全概率+记忆化DP UVA 11468 Substring
    java POI技术之导出数据优化(15万条数据1分多钟)
    验证IP端与数据库Ip端是否重复!!!
    JAVA中IP和整数相互转化(含有掩码的计算)
    Nginx搭建反向代理服务器过程详解
    session原理及实现共享
    Linux部署多个tomcat
    linux下怎么修改mysql的字符集编码
    linux yum 安装mysql
    VM虚拟机下的Linux不能上网
  • 原文地址:https://www.cnblogs.com/zhuanzhuruyi/p/6541382.html
Copyright © 2020-2023  润新知