设计模式学习

http://c.biancheng.net/view/1324.html

1.单例模式

单例模式是一种常用的软件设计模式。

在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。

应用场景:如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。单例模式的特点是该类只能有一个对象(实例),使用者通过该类提供的静态方法获得这个实例,用于当某个类只需要或只能有一个实例时,如配置文件等。和静态类(静态方法)类似,两者的不同:静态类和单例模式区别

(1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。

(2)控制资源的情况下,方便资源之间的互相通信。如线程池等

1. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~

2. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

3. 网站的计数器,一般也是采用单例模式实现,否则难以同步。

4. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。

5. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。

6. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。

7. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。

8. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。

实现
单例模式的实现要点是:

将构造方法定义为私有,防止外部通过new创建实例
定义获取唯一实例的公有静态方法,一般名为:public static 类名 getInstance()
同时将实例变量定义为静态,以便静态的获取实例方法获取
单例模式的实现根据实例是在类加载时创建还是第一次调用时创建,可分为“饿汉式”和“懒汉式”。

在类加载时就创建实例的“饿汉式”缺点为没有lazy loading的效果,从而降低内存的使用率,而在获得实例的方法内部通过判空创建的一般“懒汉式”,对象实例有可能被多个线程多次创建,线程不安全。

所以推荐的实现方法有 使用静态内部类的懒汉模式 和 使用双重校验锁的懒汉模式。

使用静态内部类的懒汉模式
该模式通过建造静态内部类,并在内部类中定义静态对象获得实例,由于静态内部类的特性,只有在其被第一次引用的时候才会加载,所以实现了lazy loading,同时也是线程安全的。

public class Student {
 
    // 将构造方法定义为私有,防止外部通过new创建实例
    private Student() {}
    /**
     * 实例持有者(静态内部类)
     */
    public static class holder {
        // 实例声明为静态常量,引用不可更改
        private static final Student INSTANCE = new Student();
    }
    // 获取唯一实例的公有静态方法
    public static Student getInstance() {
        return holder.INSTANCE;
    }
    public String toString() {
        return "这是唯一的实例";
    }
    public static void main(String[] args) {
        // 因为构造方法为私有,下行语句无效
        // Student student =  new Student();
 
        //正确获取实例方法
        Student student = Student.getInstance();
         System.out.println(student.toString());
    }
}

使用双重校验锁的懒汉模式
该模式首先需要将实例变量加上volatile修饰符,volatile能保证对象的可见性,在工作内存中的内容更新能立即在主内存中可见。在获取实例方法中判空,并使用synchronized关键字加锁,加锁后再一次判空。

之所以进行两次判空,第一次是因为多数情况下实例已经被创建,总是进入加锁语句会多余浪费时间降低效率,第二次是为防止在第一次判空之后、加锁之前的空当资源被占用并new出对象,两次判空兼具了效率和安全。

public class Student {
 
    private volatile static Student instance;
 
    // 将构造方法定义为私有,防止外部通过new创建实例
    private Student() {}
 
    // 获取唯一实例的公有静态方法
    public static Student getInstance() {
        // 双重校验锁
        if (instance == null) {
            synchronized (Student.class) {
                if (instance == null) {
                    instance = new Student();
                }
            }
        }
        return instance;
    }
 
    public String toString() {
        return "这是唯一的实例";
    }
    
    public static void main(String[] args) {
        // 因为构造方法为私有,下行语句无效
        // Student student =  new Student();
 
        //正确获取实例方法
        Student student = Student.getInstance();
 
        System.out.println(student.toString());
    }
}

 2.工厂模式

工厂模式是为了解耦:把对象的创建和使用的过程分开。就是Class A 想调用Class B,那么只是调用B的方法,而至于B的实例化,就交给工厂类。

工厂模式可以降低代码重复。如果创建B过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。可以把这些创建对象B的代码放到工厂里统一管理。既减少了重复代码,也方便以后对B的维护。

工厂模式可以减少错误,因为工厂管理了对象的创建逻辑,使用者不需要知道具体的创建过程,只管使用即可,减少了使用者因为创建逻辑导致的错误。

工厂模式的一些适用场景:

对象的创建过程/实例化准备工作很复杂,需要很多初始化参数,查询数据库等;

类本身有好多子类,这些类的创建过程在业务中容易发生改变,或者对类的调用容易发生改变;

原文:https://blog.csdn.net/qq_39821316/article/details/82936992

原文地址:https://www.cnblogs.com/wwmiert/p/11203617.html