常见设置模式(单例模式&普通工厂模式)

本文探讨了设计模式的重要性,并深入讲解了单例模式和工厂模式。单例模式确保内存中只有一个实例,常用于配置文件管理,但也存在潜在问题。介绍了多种单例模式实现方式,包括饿汉模式、懒汉模式及其改进版。工厂模式则用于集中控制对象创建,增加设计灵活性,如Spring的IOC容器就是其应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1. 为什么需要学习设计模式

二、常见设置模式

2.1 单例模式

2.1.1 概念

2.1.2 使用场景

2.1.3 优缺点

2.1.4 示例

2.2 工厂模式

2.2.1 概念

2.2.2 使用场景

2.2.3 工厂方法


1. 为什么需要学习设计模式

设计模式(Design pattern)代表了最佳的实践,是很多优秀的软件开发人员的经验总结,是解决特定问题的解决方案。它并不是语法规定,也不拘泥于特定语言。 恰当的使用设计模式可以代码的可复用性,可维护性,可扩展性,健壮性及安全性,这些都是系统非常重要的非功能性需求。

设计模式的广泛使用起始于1995年,GOF(四人帮)出版的《设计模式:可复用面向对象软件基础》。

本部分相关的示例代码:

https://gitee.com/lisenaq/patterndemo.git

二、常见设置模式

2.1 单例模式

2.1.1 概念

保证在内存中只用一个实例

2.1.2 使用场景

比如:系统配置文件的管理,这些配置文件只要使用一个单例对象进行读写即可,系统总其他地方需要使用配置信息时,只要使用该单例对象进行获取就可以了,这样便于统一管理配置信息。

2.1.3 优缺点

🟡优点:

  • 在内存中只有一个对象,节省内存空间;
  • 避免频繁的创建销毁对象,可以提高性能;
  • 避免对共享资源的多重占用,简化访问;
  • 为整个系统提供一个全局访问点。


🟣缺点:

  • 不适用于变化频繁的对象;
  • 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;

2.1.4 示例

1.饥饿模式:直接实例化对象

package org.lisen.patterndemo.singleton;

/**
 * 单例模式,饥饿加载
 */
public class SingletonDemo01 {

    //1. 需要有一个私有的构造函数,防止该类通过new的方式创建实例
    private SingletonDemo01(){}

    //2. 饥饿模式,首先生成一个实例
    private static final SingletonDemo01 instance = new SingletonDemo01();

    //3. 静态方法,用于获取已经生成的实例
    public static SingletonDemo01 getInstance() {
        return instance;
    }

    public String hello(String name) {
        return "hello " + name;
    }

    /*
     * 测试多线程下单例模式是否安全
     * @param args
     */
    public static void main(String[] args) {
        /*for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(SingletonDemo01.getInstance().hashCode());
            }).start();
        }*/
        SingletonDemo01 s = SingletonDemo01.getInstance();
        String hello_world = s.hello("hello world");
        System.out.println(hello_world);
    }

}

这种直线方式简单,且是线程安全的。

2.懒汉模式:调用方法时进行实例化对象。

  • 第一种写法:
/**
 * 单例模式: 懒汉式
 */
public class SingletonDemo02 {

    private SingletonDemo02(){

      //模拟构造函数的运行耗时
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }    

    private static SingletonDemo02 singletonDemo02 = null;

    public static SingletonDemo02 getInstance() {

        if (singletonDemo02 == null) {
            singletonDemo02 = new SingletonDemo02();
        }

        return singletonDemo02;
    }

    public String hello(String name) {
        return "hello " + name;
    }

}

注意: 这种方式在多线程访问时会有问题。

  • 第二种写法:

在上一个方法的基础上增加了同步锁synchronized

package org.lisen.patterndemo.singleton;

/**
 * 单例模式: 懒汉式, 有线程问题
 */
public class SingletonDemo03 {

    private SingletonDemo03(){
        //模拟构造函数的运行耗时
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static SingletonDemo03 singletonDemo02 = null;

    public static synchronized SingletonDemo03 getInstance() {

        if (singletonDemo03 == null) {

            singletonDemo03 = new SingletonDemo03();
        }

        return singletonDemo03;
    }

    public String hello(String name) {
        return "hello " + name;
    }

    /*
     * 测试多线程下单例模式是否安全
     * @param args
     */
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(SingletonDemo03.getInstance().hashCode());
            }).start();
        }
    }

}
  • 第三种写法
public class SingletonDemo04 {

    private SingletonDemo04() {
    }

    private static SingletonDemo04 singletonDemo04 = null;

    public static  SingletonDemo04 getInstance(){
        //系统减小同步块来提升性能,可以吗?
        if(singletonDemo04 == null) {
            synchronized (SingletonDemo04.class) {
                singletonDemo04 = new SingletonDemo04();
            }
        }
        return singletonDemo04;
    }

    public String hello(String name) {
        return "hello " + name;
    }

}

该方式依然会有线程安全问题

  • 第四种写法

在上个方法基础上进行双重检查  首先判断是否为空 如果为空则加上同步锁   之后再 第二次判断

package org.lisen.patterndemo.singleton;

/**
 * 单例模式: 懒汉式,线程安全,但性能较低
 */
public class SingletonDemo05 {

    private SingletonDemo05() {
        //模拟构造函数的运行耗时
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static SingletonDemo05 singletonDemo05 = null;

    public static  SingletonDemo05 getInstance(){
        //系统减小同步块来提升性能,可以吗?
        if(singletonDemo05 == null) {
            ///....
            synchronized (SingletonDemo05.class) {
                if(singletonDemo05 == null) {
                    singletonDemo05 = new SingletonDemo05();
                }
            }
        }
        return singletonDemo05;
    }

    public String hello(String name) {
        return "hello " + name;
    }


    /*
     * 测试多线程下单例模式是否安全
     * @param args
     */
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(SingletonDemo05.getInstance().hashCode());
            }).start();
        }
    }

}
  • 第五种写法

写一个静态的内部类 在静态的内部类里面声明一个静态的实例(是一个单线程没有线程安全问题)

package org.lisen.patterndemo.singleton;

/**
 * 单例模式: 懒加载, 线程安全
 */
public class SingletonDemo04 {

    //阻止外部实例化
    private SingletonDemo04() {
        //模拟构造函数运行耗时
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static {

    }

    //使用静态内部类来使用一个SingletonDemo04对象
    private static class SingletonDemoHolder {
        private final static SingletonDemo04 instance = new SingletonDemo04();
    }

    public static SingletonDemo04 getInstance() {
        return SingletonDemoHolder.instance;
    }

    public String hello(String name) {
        return "hello " + name;
    }


    /*
     * 测试多线程下单例模式是否安全
     * @param args
     */
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(SingletonDemo04.getInstance().hashCode());
            }).start();
        }
    }
}
  • 第六种写法

 enum 枚举型(没有构造函数) 可以保证单例,且线程安全

package org.lisen.patterndemo.singleton;

public enum  SingletonDemo05 {

    INSTANCE;

    public String hello(String name) {
        return "hello " + name;
    }

    /*
     * 测试多线程下单例模式是否安全
     * @param args
     */
    public static void main(String[] args) {

        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(SingletonDemo05.INSTANCE.hashCode());
            }).start();
        }
    }
}

注意:单例模式既要保证懒加载也要保证线程安全

2.2 工厂模式

2.2.1 概念

用于产生对象的方法或者式类,称之为工厂。 上面所讲到的单例模式也可以看作为一个特殊的工厂。

2.2.2 使用场景

为什么需要工作模式,原来使用new的方式感觉也很简单,且好懂?

🔍使用工厂的原因是我们可以通过工厂模式,来集中控制对象的创建过程,这样可以给设计带来更多的灵活性。比如:spring的IOC容器就是工厂模式的经典实现。

2.2.3 工厂方法

用于生产指定系列的对象。已鸭子为例,鸭子有真的鸭子,橡皮鸭,电子玩具鸭等。如何能方便的创建出各种鸭子,并将创建过程控制起来,以便于以后的维护和扩展?

类图:

 抽象鸭子父类Duck

public abstract class Duck {
 
    abstract public void quack();
 
}

RubberDuck鸭子子类,继承Duck抽象父类

public class RubberDuck extends Duck {
 
    @Override
    public void quack() {
        System.out.println("我是橡皮鸭,");
    }
 
}

 WildDuck鸭子子类,继承Duck抽象父类

public class WildDuck extends Duck {
 
    @Override
    public void quack() {
        System.out.println("我是真鸭子");
    }
 
}

 DonaldDuck鸭子子类,继承Duck抽象父类

public class DonaldDuck extends Duck {
 
    @Override
    public void quack() {
        System.out.println("我是唐老鸭");
    }
 
}

 编写控制鸭子类创建的工厂DuckFactory 

public class DuckFactory {
 
    //私有化构造方法
    private DuckFactory(){}
 
    //static 饿汉模式实例出一个类型为鸭子工厂类的对象
    private static DuckFactory duckFactory = new DuckFactory();
    
    //定义图中的几种子类的鸭子类,类型用数字区分,可以手动增加
    public static final int WILD_DUCK = 1;
 
    public static final int RUBBER_DUCK = 2;
 
    public static final int DONALD_DUCK = 3;
 
    public static final int PJ_DUCK = 4;
    
    //依据鸭子类型得到鸭子实例的方法
    public static Duck getInstance(int duckType) {
        switch (duckType) {
            case WILD_DUCK:
                //返回直接实例化好的鸭子子类
                return new WildDuck();
            case RUBBER_DUCK:
                return new RubberDuck();
            case DONALD_DUCK:
                return new DonaldDuck();
            case PJ_DUCK:
                return new PJDuck();
            default:
                return null;
        }
    }
 
}

测试

public class Main {
 
    public static void main(String[] args) {
 
        //DuckFactory可以生产鸭子对象
        Duck donaldDuck = DuckFactory.getInstance(DuckFactory.DONALD_DUCK);
        donaldDuck.quack();
 
        Duck wildDuck = DuckFactory.getInstance(DuckFactory.WILD_DUCK);
        wildDuck.quack();
 
        Duck pjDuck = DuckFactory.getInstance(DuckFactory.PJ_DUCK);
        pjDuck.quack();
    }
 
}

以上就是关于常见设置模式01的内容📖

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值