精华内容
下载资源
问答
  • Android中常见设计模式有哪些

    千次阅读 2017-05-21 16:29:24
    android中常见设计模式有哪些? 建造者模式 建造者模式最明显的标志就是Build类,而在Android中最常用的就是Dialog的构建,Notification的构建也是标准的建造者模式。 建造者模式很好理解,如果一个类的构造...

     

    android中常见的设计模式有哪些?

     

    建造者模式

    建造者模式最明显的标志就是Build类,而在Android中最常用的就是Dialog的构建,Notification的构建也是标准的建造者模式。

    建造者模式很好理解,如果一个类的构造需要很多参数,而且这些参数并不都是必须的,那么这种情况下就比较适合Builder。

    比如构建一个AlertDialog,标题、内容、取消按钮、确定按钮、中立按钮,你可能只需要单独设置几个属性即可;另外在我的OkHttpPlus项目中,构造一个Http请求也是这样的,有可能你只需要设置URL,有可能需要添加请求参数、Http Header等,这个时候建造者模式也是比较合适的。

    单例模式

    单例在Android开发中经常用到,但是表现形式可能不太一样。

    以ActivityManager等系统服务来说,是通过静态代码块的形式实现单例,在首次加载类文件时,生成单例对象,然后保存在Cache中,之后的使用都是直接从Cache中获取。

    class ContextImpl extends Context {
    
        static {
            registerService(ACTIVITY_SERVICE, new ServiceFetcher() {
                    public Object createService(ContextImpl ctx) {
                        return new ActivityManager(ctx.getOuterContext(),       ctx.mMainThread.getHandler());
                    }});
        }
    }
    

    当然,还有更加明显的例子,比如AccessibilityManager内部自己也保证了单例,使用getInstance获取单例对象。

     public static AccessibilityManager getInstance(Context context) {
            synchronized (sInstanceSync) {
                if (sInstance == null) {
    
                   ......
    
                    IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
                    IAccessibilityManager service = iBinder == null
                            ? null : IAccessibilityManager.Stub.asInterface(iBinder);
                    sInstance = new AccessibilityManager(context, service, userId);
                }
            }
            return sInstance;
        }
    

    除此之外,还有一些伪单例,比如Application,默认情况下在一个进程中只存在一个实例,但是Application不能算是单例,因为它的构造方法未私有,你可以生成多个Application实例,但是没有用,你没有通过attach()绑定相关信息,没有上下文环境。

    public Application() {
            super(null);
        }
    

    单例的使用场景也很简单,就是一个App只需要存在一个类实例的情况,或者是类的初始化操作比较耗费资源的情况。在很多开源框架中,我们只需要一个对象即可完成工作,比如各种网络框架和图片加载库。

    除此之外,因为单例的实现方式很多,比如懒汉式、饿汉式、静态内部类、双重锁检查、枚举等方式,所以要清楚每种实现方式的主要特点和使用场景。

    原型模式

    原型模式在开发中使用的并不多,但是在源码中却有所体现。

    书中以Intent介绍了原型模式,是通过实现Cloneable接口来做的

    public class Intent implements Parcelable, Cloneable {
        @Override
            public Object clone() {
             return new Intent(this);
            }
        }
    

    其实这样来看的话,原型模式也比较好理解,就是你想更快的获取到一个相同属性的对象,那么就可以使用原型模式,比如这里就获取到了一个Intent对象,Intent里面的属性与被clone的相同,但是两者并无关联,可以单独使用。

    除了实现Cloneable接口,你完全可以自己定义一个方法,来获取一个对象。我这里以PhoneLayoutInflater为例子介绍。

    PhoneLayoutInflater是LayoutInflater的子类,如果我们在Activity中获取LayoutInflate的话,是通过下面方法

     @Override public Object getSystemService(String name) {
            if (LAYOUT_INFLATER_SERVICE.equals(name)) {
                if (mInflater == null) {
                    mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
                }
                return mInflater;
            }
            return getBaseContext().getSystemService(name);
        }
    

    可以看到,如果为null,就会调用cloneInContext(),这个方法在LayoutInflate是抽象方法,具体实现在PhoneLayoutInflater中

      public LayoutInflater cloneInContext(Context newContext) {
            return new PhoneLayoutInflater(this, newContext);
        }
    

    可以看到,这也是一个原型模式,所以我们不要太纠结于形式,更重要的是理解这样做的好处。

    除了在源码中可以看到原型模式,在开源框架中也可以看到,比如OkHttpClient中就存在着下面的方法

    /** Returns a shallow copy of this OkHttpClient. */
      @Override public OkHttpClient clone() {
        return new OkHttpClient(this);
      }
    

    可以看到,实现和前面的完全相同,也是new了一个对象返回,因为OkHttpClient的构造过程比较复杂,参数众多,所以用这种方式来直接生成新对象,成本很低,而且能保留之前对象的参数设置。

    工厂方法模式

    书中对于工厂方法模式的一个观点很新奇,就是Activity.onCreate()可以看做是工厂方法模式,来生成不同的View对象填充界面。

    但是我对这个说法不太苟同,原因有两点:一是这种形式不太符合工厂方法,没有抽象,没有实现,不符合一般格式,也不是静态方法,不可看做是静态工厂方法;二是没有以生成对象为结果,即不是return view来生成对象,只是通过setContentView()来设置了属性而已。这就像是给一个Activity设置了背景颜色一样。当然,设计模式这东西一个人有一个人的看法。

    静态工厂方法在Android中比较明显的例子应该就是BitmapFactory了,通过各种decodeXXX()就可以从不同渠道获得Bitmap对象,这里不再赘述。

    策略模式

    在书中策略模式讲得非常好,结合动画的插值器用法,我们可以很好的理解策略模式的形式和用法。

    在我看来,策略模式就相当于一个影碟机,你往里面插什么碟子,就能放出什么电影。

    同样,在OkHttpPlus的封装中,为了对网络返回值进行解析,我使用了策略模式。当然我写代码的时候还不知道策略模式,是写完了之后突然想到,这就是策略模式啊!

    策略模式的精髓就在于,你传入一个类,后面的处理就能按照这个类的实现去做。以动画为例,设置不同的插值器对象,就可以得到不同的变化曲线;以返回值解析为例,传入什么样的解析器,就可以把二进制数据转换成什么格式的数据,比如String、Json、XML。

    责任链模式

    书中对于责任链模式选取的例子非常有代表性,那就是Android的触摸机制,这个看法让我从另一个维度去理解Android中的触摸事件传递。

    我在这里提到这个模式,并不想说太多,只是简单的推荐你读一下这一章的内容,相信你也会有收获的。

    观察者模式

    Android中的观察者模式应该是用的非常频繁的一种模式了,对于这个模式的使用场景就一句话:你想在某个对象发生变化时,立刻收到通知。

    书中介绍观察者模式使用的是ListView的Adapter为例子,我之前知道Adapter属于适配器模式,不知道这里还有观察者模式的身影,学到了。

    Android里面的各种监听器,也都属于观察者模式,比如触摸、点击、按键等,ContentProvider和广播接收者也有观察者模式的身影,可以说是无处不在。

    除此之外,现在很多基于观察者模式的第三方框架也是非常多,比如EventBus、RxJava等等,都是对观察者模式的深入使用,感兴趣的同学可以研究一下。

     

    适配器模式

     

    Android

    在Android上提到适配器模式就会想到最常用的ListView和BaseAdapter

    在这个功能的使用中,类似于适配器模式的对象适配器

    例如在ListView中想用一个getView()方法,但是不同的数据,不同的需求,会有不同的getView()结果,所以getView()不能写死了,那么可能就想到了用适配器模式

    所以ListView里面包含了一个ListAdapter的成员变量,实际上是ListView继承了AbsListView,ListAdapter变量是在AbsListView中声明的

    然后BaseAdapter是实现了ListAdapter的,那么我们自定义的***Adapter继承了BaseAdapter,其实也就相当于间接实现了ListAdapter,并且***Adapter会具体去实现getView()方法

    那么ListView就可以通过调用setAdapter(ListAdapter adapter)方法,将***Adapter传进去,从而实现了我们想要的结果

     

    Java

    适配器模式有两种:

    1. 类适配器
    2. 对象适配器

    场景:

    假如A类想用M方法,X类有M方法,但是M方法的结果不一定完全符合A类的需求

    那么X类就是写死了,不好用,这样设计不好

    那就把X类换成一个接口,弄出一些B,C,D,E.....类中间类出来,让他们都有一个方法来处理M方法的东西,再给A类用

     

    类适配器:

    设计一个接口I,让他也有M方法

    然后设计一个B类,写好符合A类需求的specialM方法

    然后让A类继承B类,并实现I接口的M方法

    最后在A类的M方法中以super的方式调用B类的specialM方法

     

    对象适配器:(更多是用对象适配器)

    设计一个接口I,让他也有M方法

     

    然后设计一个B类,写好符合A类需求的specialM方法

    然后在A类中声明一个B类变量,并且A类实现I接口,那么A类也就有了M方法

    最后在A类的M方法中,如果需要,就可以选择调用B类的specialM方法

     

    或者设计一个B类,实现I接口的M方法

    然后在A类中声明一个I类变量,再直接调用I接口的M方法

    在调用A类的M方法之前,通过例如setAdapter(I Adapter)这样的方法,将B类设置成A类的成员变量 

    这样就保证了A类和I接口不变,适配不同情况的时候,写一个类似B类的中间类进行适配就可以了

     

    总之,两端不变,通过不同的选择方式,选择不同的中间类,也就是适配器模式了

     

    模板方法模式 

    这个模式我之前见的比较少,但是理解之后,就会发现这个模式很简单。

    我觉得,模板方法模式的使用场景也是一句话:流程确定,具体实现细节由子类完成。

    这里要关注一下『流程』这个关键字,随便拿一个抽象类,都符合"具体实现细节由子类完成"的要求,关键就在于是否有流程,有了流程,就叫模板方法模式,没有流程,就是抽象类的实现。

    书中讲这个模式用的是AsyncTask,各个方法之间的执行符合流程,具体实现由我们完成,非常经典。

    另外一个方面,Activity的生命周期方法可以看做是模板方法模式,各个生命周期方法都是有顺序的,具体实现我们可以重写,是不是和前面的要求很符合?关于这方面的理解,可以参考我的这篇文章:对Activity生命周期的理解

    除了Android里面的模板方法模式,在其他开源项目中也存在着这个模式的运用。比如鸿洋的OkHttp-Utils项目,就是模板方法模式的典型实现。将一个Http请求的过程分割成几部分,比如获取URL,获取请求头,拼接请求信息等步骤,这几个步骤之前有先后顺序,就可以这样来做。

    代理模式和装饰器模式

    之所以把这两个放在一起说,是因为这两种模式很像,所以这里简单介绍下他们之间的区别,主要有两点。

    1. 装饰器模式关注于在一个对象上动态的添加方法,而代理模式关注于控制对对象的访问
    2. 代理模式,代理类可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。而当我们使用装饰器模式的时候,通常的做法是将原始对象作为一个参数传给装饰者的构造器

    这两句话可能不太好理解,没关系,下面看个例子。

    代理模式会持有被代理对象的实例,而这个实例一般是作为成员变量直接存在于代理类中的,即不需要额外的赋值。

    比如说WindowManagerImpl就是一个代理类,虽然名字上看着不像,但是它代理的是WindowManagerGlobal对象。从下面的代码中就可以看出来。

    public final class WindowManagerImpl implements WindowManager {
        private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
        private final Display mDisplay;
        private final Window mParentWindow;
    
        ......
    
        @Override
        public void addView(View view, ViewGroup.LayoutParams params) {
            mGlobal.addView(view, params, mDisplay, mParentWindow);
        }
    
        @Override
        public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
            mGlobal.updateViewLayout(view, params);
        }
    
        @Override
        public void removeView(View view) {
            mGlobal.removeView(view, false);
        }
    
        @Override
        public void removeViewImmediate(View view) {
            mGlobal.removeView(view, true);
        }
    
        @Override
        public Display getDefaultDisplay() {
            return mDisplay;
        }
    }
    

    从上面的代码中可以看出,大部分WindowManagerImpl的方法都是通过WindowManagerGlobal实现的,而WindowManagerGlobal对象不需要额外的赋值,就存在于WindowManagerImpl中。另外,WindowManagerGlobal中其实有大量的方法,但是通过WindowManagerImpl代理之后,都没有暴露出来,对开发者是透明的。

    我们再来看一下装饰器模式。装饰器模式的目的不在于控制访问,而是扩展功能,相比于继承基类来扩展功能,使用装饰器模式更加的灵活。

    书中是以Context和它的包装类ContextWrapper讲解的,也非常的典型,我这里就不在赘述了,贴出一些代码来说明装饰器模式的形式。

    public class ContextWrapper extends Context {
        Context mBase;
    
        public ContextWrapper(Context base) {
            mBase = base;
        }
    }
    

    但是还有一个问题,就是在ContextWrapper中,所有方法的实现都是通过mBase来实现的,形式上是对上号了,说好的扩展功能呢?

    功能扩展其实是在ContextWrapper的子类ContextThemeWrapper里面。

    在ContextWrapper里面,获取系统服务是直接通过mBase完成的

    @Override
        public Object getSystemService(String name) {
            return mBase.getSystemService(name);
        }
    

    但是在ContextThemeWrapper里面,对这个方法进行了重写,完成了功能扩展

    @Override public Object getSystemService(String name) {
            if (LAYOUT_INFLATER_SERVICE.equals(name)) {
                if (mInflater == null) {
                    mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
                }
                return mInflater;
            }
            return getBaseContext().getSystemService(name);
        }
    

    当然,如果不存在功能扩展就不算是装饰器模式了吗?其实设计模式本来就是『仁者见仁,智者见智』的事情,只要你能理解这个意思就好。

    外观模式

    外观模式可能看到的比较少,但是其实不经意间你就用到了。

    这里以我的一个开源项目KLog来说吧,在最开始写这个类的时候,就只有KLog这一个类,完成基本的Log打印功能,后来又添加了JSON解析、XML解析、Log信息存储等功能,这个时候一个类就不太合适了,于是我把JSON、XML、FILE操作相关的代码抽取到单独的类中,比如JSON打印的代码

    public class JsonLog {
    
        public static void printJson(String tag, String msg, String headString) {
    
            String message;
    
            try {
                if (msg.startsWith("{")) {
                    JSONObject jsonObject = new JSONObject(msg);
                    message = jsonObject.toString(KLog.JSON_INDENT);
                } else if (msg.startsWith("[")) {
                    JSONArray jsonArray = new JSONArray(msg);
                    message = jsonArray.toString(KLog.JSON_INDENT);
                } else {
                    message = msg;
                }
            } catch (JSONException e) {
                message = msg;
            }
    
            Util.printLine(tag, true);
            message = headString + KLog.LINE_SEPARATOR + message;
            String[] lines = message.split(KLog.LINE_SEPARATOR);
            for (String line : lines) {
                Log.d(tag, "║ " + line);
            }
            Util.printLine(tag, false);
        }
    }
    

    代码很简单,就一个方法,但是在使用的时候,无论打印哪种格式,都是这样使用的

    //普通打印
     KLog.d(LOG_MSG);
     //JSON格式打印
     KLog.json(JSON);
     //XML格式打印
     KLog.xml(XML);
    

    可以看到,虽然功能不同,但是都通过KLog这个类进行了封装,用户只知道用KLog这个类能完成所有需求即可,完全不需要知道代码实现是几个类完成的。

    实际上,在KLog内部,是多个类共同完成打印功能的。

     private static void printLog(int type, String tagStr, Object... objects) {
    
            if (!IS_SHOW_LOG) {
                return;
            }
    
            String[] contents = wrapperContent(tagStr, objects);
            String tag = contents[0];
            String msg = contents[1];
            String headString = contents[2];
    
            switch (type) {
                case V:
                case D:
                case I:
                case W:
                case E:
                case A:
                    BaseLog.printDefault(type, tag, headString + msg);
                    break;
                case JSON:
                    JsonLog.printJson(tag, msg, headString);
                    break;
                case XML:
                    XmlLog.printXml(tag, msg, headString);
                    break;
            }
        }
    

    但是通过外观模式,这些细节对用户隐藏了,这样如果以后我想更换JSON的解析方式,用户的代码不需要任何改动,这也是这个设计模式的优势所在。

    总结

    唠唠叨叨的,总算是把这几种设计模式介绍完了,看完这篇文章,你应该就会发现其实Android中的设计模式确实到处都存在,不是缺少设计模式,而是缺少一双发现的眼睛。

    当然,设计模式的提出是为了解决特定的问题,当我们遇到类似问题的时候,可以从设计模式的角度思考和解决问题,这应该是我最大的收获吧。

    原地址:http://www.cnblogs.com/sjm19910902/p/6386542.html

     

     

     

     

    展开全文
  • 对于设计模式,相信大多数人都了解,或为了面试,或为了实际开发,但是对于大多数人来说,实际开发中,真正用设计模式的地方,少之又少。最主要的原因,还是因为我们对设计模式并未真正的理解。那么,如何理解设计...

    对于设计模式,相信大多数人都有了解,或为了面试,或为了实际开发,但是对于大多数人来说,实际开发中,真正用设计模式的地方,少之又少。最主要的原因,还是因为我们对设计模式并未真正的理解。那么,如何理解设计模式呢?

    设计模式就是那些大佬经验设计的总结(情境中遇到问题而经过证实的一个解决方案)。针对某些特定的场景,去使用特定的设计模式,能让代码更简洁,也可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式有23种,总体来说,设计模式分为三大类:

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

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

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

    以上标红的设计模式,是需要我们特别掌握的,面试最容易问到。那么,实际开发过程中,你用了什么设计模式呢?

    1)实际开发中,我使用了单例模式,比如系统要求一个对象只能产生一次,比如设计一个单例类,负责所有数据表的映射处理。

    意图(核心):保证一个类仅有一个实例,并提供一个访问该实例的全局访问点。

    主要解决:一个全局使用的类频繁地创建与销毁。

    何时使用:当您想控制实例数目,节省系统资源的时候。

    如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

    关键代码:构造函数是私有的。

    常见应用场景:
    1)在项目种加载配置文件的类,一般也只是一个对象,没有必要重复加载。

    2)项目中的数据库连接池,一般也是单例模式。

    3)Spring中的每个bean默认也是单例,优点是方便Spring容器管理。

    4)Springmvc中,控制器对象也是单例。

    5)在Servlet编程中,每个servlet也是单例。

    6)

    优点:单例模式只生成一个实例,减少了系统开销。

     




     

     

    展开全文
  • 在实际的编程开发中,单例设计模式是我们谈得最多但用...可以说我到现在都没有使用上(在实际公司工作中),为什么会这样呢,我们来看看单例设计模式有哪些常见的应用场景 window 的控制面板、任务管理器、回收站 ...

    在实际的编程开发中,单例设计模式是我们谈得最多但用得最少的设计模式。单例设计模式的4种实现方式:

    饿汉式、懒汉式、静态内部类式、枚举式,我这里就不说了。详情请参考我的另一篇博客

    java使用枚举、饿汉式、懒汉式、内部类实现单例设计模式

    可以说我到现在都没有使用上(在实际公司工作中),为什么会这样呢,我们来看看单例设计模式有哪些常见的应用场景

    1. window 的控制面板、任务管理器、回收站
    2. 网站的计数器
    3. 应用程序的日志应用:log4j、slf4j、logkback
    4. 项目中配置文件的读取
    5. 线程池(管理多个线程):java 自带线程池
    6. 数据库连接池(管理多个数据库连接):c3po 等
    7. 文件系统

    从以上使用场景我们可以看出 ,其实那些所谓的常用场景,只要写一次,后期就基本上不会去改动了。所以我们用得非常少,但为什么我们总是谈起单例模式呢,就是那些面试官总是问这些问题,真不明白为什么老是要问这些,面试时造飞机大炮,工作时写HTML,不知道各位有没有同感。

    在Java中,运行时类也就是Runtime类,被设计成是单例的饿汉式,spring 中的bean 和spring mvc 中的controller、service、dao层中通过@autowire的依赖注入对象默认都是单例的,使用单例的目的当然是节约内存节省资源。

    其实我也就发发牢骚,不知道各位有什么看法,欢迎下方评论!

    展开全文
  • JAVA设计模式之单例模式

    万次阅读 多人点赞 2014-04-16 06:51:34
     java中单例模式是一种常见设计模式,单例模式的写法好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。  单例模式以下特点:  1、单例类只能一个实例。  2、单例类必须自己创建自己的...

    本文继续介绍23种设计模式系列之单例模式。

    概念:
      java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
      单例模式有以下特点:
      1、单例类只能有一个实例。
      2、单例类必须自己创建自己的唯一实例。
      3、单例类必须给所有其他对象提供这一实例。
      单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。


    一、懒汉式单例

     

    //懒汉式单例类.在第一次调用的时候实例化自己 
    public class Singleton {
        private Singleton() {}
        private static Singleton single=null;
        //静态工厂方法 
        public static Singleton getInstance() {
             if (single == null) {  
                 single = new Singleton();
             }  
            return single;
        }
    }

     

    Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。

    (事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)

    但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全,如果你第一次接触单例模式,对线程安全不是很了解,可以先跳过下面这三小条,去看饿汉式单例,等看完后面再回头考虑线程安全的问题:

     

    1、在getInstance方法上加同步

     

    public static synchronized Singleton getInstance() {
             if (single == null) {  
                 single = new Singleton();
             }  
            return single;
    }

     

     

     

    2、双重检查锁定

     

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

     

    3、静态内部类

     

    public class Singleton {  
        private static class LazyHolder {  
           private static final Singleton INSTANCE = new Singleton();  
        }  
        private Singleton (){}  
        public static final Singleton getInstance() {  
           return LazyHolder.INSTANCE;  
        }  
    }  

    这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。

     

     

     

     

     

     

    二、饿汉式单例

     

    //饿汉式单例类.在类初始化时,已经自行实例化 
    public class Singleton1 {
        private Singleton1() {}
        private static final Singleton1 single = new Singleton1();
        //静态工厂方法 
        public static Singleton1 getInstance() {
            return single;
        }
    }

    饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

     

     

     

     

    三、登记式单例(可忽略)

    //类似Spring里面的方法,将类名注册,下次从里面直接获取。
    public class Singleton3 {
        private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
        static{
            Singleton3 single = new Singleton3();
            map.put(single.getClass().getName(), single);
        }
        //保护的默认构造子
        protected Singleton3(){}
        //静态工厂方法,返还此类惟一的实例
        public static Singleton3 getInstance(String name) {
            if(name == null) {
                name = Singleton3.class.getName();
                System.out.println("name == null"+"--->name="+name);
            }
            if(map.get(name) == null) {
                try {
                    map.put(name, (Singleton3) Class.forName(name).newInstance());
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            return map.get(name);
        }
        //一个示意性的商业方法
        public String about() {    
            return "Hello, I am RegSingleton.";    
        }    
        public static void main(String[] args) {
            Singleton3 single3 = Singleton3.getInstance(null);
            System.out.println(single3.about());
        }
    }

     登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。 

    这里我对登记式单例标记了可忽略,我的理解来说,首先它用的比较少,另外其实内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在类被装载的时候就被实例化了。

     

    饿汉式和懒汉式区别

    从名字上来说,饿汉和懒汉,

    饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

    而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

    另外从以下两点再区分以下这两种方式:

     

    1、线程安全:

    饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

    懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。


     

    2、资源加载和性能:

    饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

    而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

    至于1、2、3这三种实现又有些区别,

    第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,

    第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗

    第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。

     

    什么是线程安全?

    如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

    或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。

     

    应用

    以下是一个单例类使用的例子,以懒汉式为例,这里为了保证线程安全,使用了双重检查锁定的方式:

     

    public class TestSingleton {
    	String name = null;
    
            private TestSingleton() {
    	}
    
    	private static volatile TestSingleton instance = null;
    
    	public static TestSingleton getInstance() {
               if (instance == null) {  
                 synchronized (TestSingleton.class) {  
                    if (instance == null) {  
                       instance = new TestSingleton(); 
                    }  
                 }  
               } 
               return instance;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public void printInfo() {
    		System.out.println("the name is " + name);
    	}
    
    }

    可以看到里面加了volatile关键字来声明单例对象,既然synchronized已经起到了多线程下原子性、有序性、可见性的作用,为什么还要加volatile呢,原因已经在下面评论中提到,

    还有疑问可参考http://www.iteye.com/topic/652440
    和http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

     

     

     

    public class TMain {
    	public static void main(String[] args){
    		TestStream ts1 = TestSingleton.getInstance();
    		ts1.setName("jason");
    		TestStream ts2 = TestSingleton.getInstance();
    		ts2.setName("0539");
    		
    		ts1.printInfo();
    		ts2.printInfo();
    		
    		if(ts1 == ts2){
    			System.out.println("创建的是同一个实例");
    		}else{
    			System.out.println("创建的不是同一个实例");
    		}
    	}
    }
    

     运行结果:

    结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。

    对于单例模式的几种实现方式,知道饿汉式和懒汉式的区别,线程安全,资源加载的时机,还有懒汉式为了实现线程安全的3种方式的细微差别。

    更多设计模式:23种设计模式系列

    作者:jason0539

    博客:http://blog.csdn.net/jason0539(转载请说明出处)

    展开全文
  • 常见设计模式类图

    千次阅读 2018-10-30 20:49:45
    概要 最近看了设计模式相关的书籍...常见设计模式类图 策略模式 代理模式 动态代理模式 命令模式 外观模式 装饰器模式 适配器模式 工厂模式 迭代器模式 组合模式 观察者模式 模版方法模式 状态模式 ...
  • 二、常用的设计模式有哪些? 1、单例模式(懒汉式、饿汉式) 步骤: 1、构造方法私有化,让除了自己类能创建,其他类都不能创建。 2、在自己的类中创建一个单实例(懒汉模式是在需要的时候才创建,饿汉模式是一开始...
  • 常见设计模式—单例模式(Singleton)

    千次阅读 多人点赞 2020-03-15 20:14:59
    对于常用的23种设计模式,这里笔者会根据自己学习和出现频率、重要程度进行学习记录吧。 单例模式 单例模式(Singleton Pattern)是设计模式中最简单的模式之一,属于创建型模式。这种设计模式主要是类的对象只有一...
  • 1.1 设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接...
  • 常见三种设计模式

    千次阅读 2019-06-11 14:43:31
    设计模式(Design pattern)代表了最佳的实践,通常被经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段...
  • 常见设计模式说明

    千次阅读 2020-08-24 23:23:34
    简单工厂模式:需要在工厂类中做判断,从而创造相应的产品。当增加新的产品时,就需要修改工厂类,但这违反了开放封闭原则(可以扩展,但是不可以修改) 工厂方法模式:是指定义一个用于创建对象的接口,让子类决定...
  • Java常见设计模式

    千次阅读 2019-06-16 14:57:47
    常见写法: 懒汉式 public class Singleton { /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */ private static Singleton instance = null; /* 私有构造方法,防止被实例化 */ ...
  • Java常见设计模式面试题及答案

    千次阅读 2021-03-29 06:12:47
    2. JDK 中常用的设计模式有哪些?3.单例模式是什么?请用 Java 写出线程安全的单例模式4.在 Java 中,什么叫观察者模式(observer design pattern)?5.使用工厂模式有哪些好处?说说它的应用场景?6.举一个 Java中 ...
  • Android四种常见设计模式说明

    千次阅读 2016-04-04 01:39:25
     Android开发的设计模式,基本设计思想源于java的设计模式,java的设计模式有N多种,据不完全统计,迄今为止,网络出现最频繁的大概23种。Java只是一门开发语言,学会并掌握这门语言进行代码编写,这是每个程序员...
  • 常用设计模式总结

    万次阅读 多人点赞 2019-07-31 19:13:12
    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,是可复用面向对象软件的基础。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 ...
  • 常见设计模式面试题

    万次阅读 2012-09-08 10:42:33
    1、写出常用的设计模式,如单例、工厂、装饰者、观察者等模式,分别介绍他们运用的场景 2、关于标准的JDK库中使用的一些设计模式 Decorator设计模式常被用于各种Java IO类中 Singleton模式常被用在运行环节中,...
  • 常用的设计模式有哪些

    万次阅读 2018-09-02 11:57:02
    常用的设计模式有哪些设计模式就是经过前人无数次的实践总结出的,设计过程中可以反复使用的、可以解决特定问题的设计方法。 单例(饱汉模式、饥汉模式) 1、构造方法私有化,除了自己类中能创建外其他地方都不...
  • 面向对象思想设计原则 单一职责原则 开闭原则 里氏替换原则 依赖注入原则 接口分离原则 迪米特原则 设计模式的分类 简单工厂模式 简单工厂模式概述 工厂方法模式 工厂方法模式概述 ...适配器设计模式
  • 常见设计模式笔试面试题

    万次阅读 多人点赞 2018-08-10 15:45:46
    设计模式一套被反复使用,多数人知晓的代码设计经验的总结,实现可重用代码,使代码更容易被理解,保证代码可靠性。 总体来说,设计模式分为三大类: 创建型模式(五种):工厂方法模式、抽象工厂模式、单例模式、...
  • 五种常见设计模式

    万次阅读 2017-01-22 22:37:03
    设计模式 一书将设计模式引入软件社区,该书的作者是 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides Design(俗称 “四人帮”)。所介绍的设计模式背后的核心概念非常简单。经过多年的软件开发实践...
  • 今题那站在这里和大家一起分享最近在一本书上看到的关于设计模式的内容,接下来要讲的设计模式有: 单例模式 简单工厂模式 工厂方法和抽象工厂 代理模式 命令模式 策略模式 门面模式 桥接模式 观察者模式 ...
  • Java常见设计模式面试题

    万次阅读 2016-10-15 17:05:27
    常见面试题 设计模式 工厂模式(Factory pattern) 观察者模式(Observer pattern) 重载 线程安全的单例模式
  • 对几种常见设计模式的理解

    千次阅读 2015-08-31 10:58:49
    对几种常见设计模式的理解 设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 最早提出“设计模式”概念的...
  • 如何理解这6种常见设计模式

    千次阅读 2020-09-10 14:06:17
    有哪些常见设计模式?如何合理运用?本文分享作者对工厂模式、单例模式、装饰模式、策略模式、代理模式和观察者模式的理解,介绍每种模式的模式结构、优缺点、适用场景、注意实现及代码实现。一 前言最近在改造...
  • 设计模式的几个重要原则 单一职责原则 开闭原则 依赖倒转原则 里氏代换原则 合成聚合复用原则:尽量使用组合/聚合代替继承 迪米特原则:降低耦合性 设计模式 类图 优点 缺点 ...
  • 一、概述本文主要介绍常见的单例模式和工厂模式,主要参考网址:https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md 与程杰电子书《大话设计模式》进行...
  • 一、设计模式的分类 设计模式是不分语言的;前辈们总结出来的设计模式分:3种类型及23种模式: 设计模式主要分三个类型:创建型、结构型和行为型。 其中创建型: 一、Singleton,单例模式:保证一个类只有一个...
  • Java企业级常见设计模式

    万次阅读 2020-08-06 09:02:28
    创建型模式 工厂模式:创建工厂接口类(规范),子类(工厂)实现业务。 抽象工厂模式:工厂的创建由子类决定,其子类的子类实现业务。(也称为超级工厂模式) 单例模式:系统中保持一个实例,实例方法私有,公开全局...
  • cocos2dx的几种常见设计模式

    千次阅读 2014-06-30 19:34:11
    cocos2dx中不少的设计模式,所以从本篇博客开始探讨一下cocos2dx中的设计模式,看看引擎都使用了哪些设计模式,我们今后写代码要怎样使用某种设计模式。本人菜鸟一枚,如果任何错误还请留言指教,互相探讨。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 444,221
精华内容 177,688
关键字:

常见的设计模式有哪些