在Java中应用设计模式--Singleton

本文深入探讨了设计模式中的Singleton模式,分析了其功能和用途,并详细解释了三种常见的实现方法,包括使用静态方法、静态变量及注册器机制。
在Java中应用设计模式--Singleton

作者:刘湛    来自:IBM

  本文介绍了设计模式中 Singleton 的基本概念,对其功能和用途进行了简单的分析,列出了通常实现 Singleton 的几种方法,并给出了详细的Java 代码.

  基本概念

  Singleton 是一种创建性模型,它用来确保只产生一个实例,并提供一个访问它的全局访问点.对一些类来说,保证只有一个实例是很重要的,比如有的时候,数据库连接或 Socket 连接要受到一定的限制,必须保持同一时间只能有一个连接的存在.再举个例子,集合中的 set 中不能包含重复的元素,添加到set里的对象必须是唯一的,如果重复的值添加到 set,它只接受一个实例.JDK中正式运用了Singleton模式来实现 set 的这一特性,大家可以查看java.util.Collections里的内部静态类SingletonSet的原代码.其实Singleton是最简单但也是应用最广泛的模式之一,在 JDK 中随处可见.

  简单分析

  为了实现 Singleton 模式,我们需要的是一个静态的变量,能够在不创建对象的情况下记忆是否已经产生过实例了.静态变量或静态方法都可以在不产生具体实例的情况下直接调用,这样的变量或方法不会因为类的实例化而有所改变.在图1的结构中可以看到,uniqueInstance 就是这个独立的静态变量,它可以记忆对象是否已经实例化了,在静态方法 Instance 中对这个变量进行判断,若没有实例化过就产生一个新的对象,如果已经实例化了则不再产生新的对象,仍然返回以前产生的实例.


图1: Singleton 模式结构

  具体实施

  实现 Singleton 模式的办法通常有三种.

  一. 用静态方法实现 Singleton

  这种方法是使用静态方法来监视实例的创建.为了防止创建一个以上的实例,我们最好把构造器声明为 private.

  这样可以防止客户程序员通过除由我们提供的方法之外的任意方式来创建一个实例,如果不把构造器声明为private,编译器就会自作聪明的自动同步一个默认的friendly构造器.这种实现方法是最常见的,也就是图1中结构的标准实现.

        public class Singleton {
            private static Singleton s;
            private Singleton(){};
        /**
             * Class method to access the singleton instance of the class.
         */
        public static Singleton getInstance() {
         if (s == null)
            s = new Singleton();
          return s;
        }
}

// 测试类
class singletonTest {
  public static void main(String[] args) {
    Singleton s1 = Singleton.getInstance();
    Singleton s2 = Singleton.getInstance();
    if (s1==s2)
      System.out.println("s1 is the same instance with s2");
    else
      System.out.println("s1 is not the same instance with s2");
  }
}

  singletonTest运行结果是:
  s1 is the same instance with s2
  这证明我们只创建了一个实例.

  二. 以静态变量为标志实现 Singleton

  在类中嵌入一个静态变量做为标志,每次都在进入构造器的时候进行检查.

  问题在于构造器没有返回类型,如果确定创建一个实例成功与否.一个方法是调用一个函数来检查创建是否成功,然后简单的返回一个来自静态变量的值,但是这样做是不优雅的,而且容易发生错误.比较好的做法是创建一个当创建了一个以上的实例时可以抛出异常的类,这个类仅仅是调用父类方法,好处是用了自己命名的异常类型,错误信息更加清晰:

class SingletonException extends RuntimeException {
  public SingletonException(String s) {
    super(s);
  }
}

class Singleton {
  static boolean instance_flag = false; // true if 1 instance
  public Singleton() {
    if (instance_flag)
      throw new SingletonException("Only one instance allowed");
    else
      instance_flag = true; // set flag for 1 instance
  }
}

// 测试类

public class singletonTest {
  static public void main(String argv[]) {
    Singleton s1, s2;
    // create one incetance--this should always work
    System.out.println("Creating one instance");
    try {
      s1 = new Singleton();
    } catch (SingletonException e) {
      System.out.println(e.getMessage());
    }
    // try to create another spooler --should fail
    System.out.println("Creating two instance");
    try {
      s2 = new Singleton();
    } catch (SingletonException e) {
      System.out.println(e.getMessage());
    }
  }
}

  singletonTest运行结果是:
  Creating one instance
  Creating two instance
  Only one instance allowed
  可以看出,第一个实例顺利创建,第二个实例创建实抛出了我们自定义的异常.

  三. 用注册器机制来创建 Singleton

  首先用集合中的Hashtable 和Enumeration来实现addItem(Object key, Object value),getItem(Object key), ,removeItem(Object key)等方法实现一个管理器,将key和value一一关联起来,客户程序员创建实例前首先用addItem方法进行注册,再用getItem方法获取实例.Hashtable中的key是唯一的,从而保证创建的实例是唯一的,具体实现限于篇幅不再细说,在Prototype模型的应用一文中我将会给出一个实现注册器的代码.用注册器机制来创建 Singleton模式的好处是易于管理,可以同时控制多个不同类型的Singleton 实例.

  小结

  1. Singleton模式可以方便的进行扩充,产生指定数目的实例.

  2. 在The Design Patterns Java Companion 一书中曾提到过用静态类的方式来实现 Singleton模式,并指出java.lang.Math就是一个例子,这里我并不表示赞同,因为Math并不是一个真正的对象,我们只是直接调用Math类所包装的静态方法而已,根本就没有创建实例的过程,又从何说起只产生一个实例呢?这个问题我曾到Javaranch的论坛上发过帖子,所有回帖的人也都是对这一观点持否定态度.

  3. 在多线程的程序中,singleton可能会变的不可靠,可能会出现多个实例,解决的办法很简单,加个同步修饰符: public static synchronized Singleton getInstance(). 这样就保证了线程的安全性.

  4. 最后要说的是大家可能会看见一些其他实现Singleton模式的方法,因为模式在具体的应用时是灵活的,不是一成不变的,并没有一个固定的做法,但大都是上面几种方法的变形.

<think>好的,我现在需要回答用户关于Java设计模式在实际项目中应用方式和案例的问题。用户已经提供了一些相关的引用内容,我需要结合这些引用和我的知识来构建一个结构清晰的回答。 首先,用户提到的引用[1]提到框架是设计模式的具体实现,比如Spring使用了工厂模式和单例模式。这可以作为工厂模式一个应用案例。引用[3]中的代码示例展示了策略模式与工厂模式的结合,用于电商平台的折扣策略。而引用[4]讨论了模板方法模式的结构,包括抽象类和具体子类的实现。 接下来,我需要确定常见的几种设计模式,并给出实际案例。根据用户的问题,可能需要包括工厂模式、策略模式、单例模式、观察者模式和模板方法模式。每个模式都需要说明应用场景、实现方式和项目案例,并引用提供的资料中的例子。 例如,工厂模式在Spring中的BeanFactory,引用[1]和引用[3]都提到了工厂模式应用。策略模式在电商折扣策略中的使用,引用[3]的代码示例可以直接引用。模板方法模式在JUnit中的setUp和tearDown方法,引用[4]中的模板方法结构可以对应到这里。 另外,用户可能想知道如何选择合适的设计模式,所以需要在最后加上选择建议,比如分析问题、避免过度设计等,这可以结合引用[2]中提到的工厂和策略模式结合使用的情况。 需要确保回答结构清晰,分点列出每个模式,并添加相关问题。同时,引用标注要正确,比如引用[1]、[3]、[4]等。还要检查是否符合系统级指令,比如数学公式的格式,但这个问题中可能不需要数学公式,所以可以忽略。最后生成的相关问题需要与设计模式应用相关,比如具体模式实现、优缺点比较、结合使用案例等。</think>### Java设计模式在实际项目中的应用方式与案例 设计模式是解决软件设计中常见问题的可复用方案,能提升代码的可维护性、扩展性和复用性。以下是几种常见设计模式在实际项目中的应用方式和案例: --- #### 一、**工厂模式(Factory Pattern)** **应用场景**:需要动态创建对象实例时,例如根据配置或用户输入选择不同实现类。 **实现方式**: 1. 定义接口或抽象类描述对象共性。 2. 创建具体子类实现接口。 3. 通过工厂类根据条件生成具体实例。 **项目案例**: - **Spring框架的BeanFactory**:通过配置文件或注解动态创建对象,例如根据`@Service`或`@Component`生成服务类实例[^1]。 - **电商平台支付方式选择**:根据用户选择的支付渠道(支付宝、微信)生成对应的支付处理器[^3]。 ```java // 示例:简单工厂模式实现支付方式选择 public class PaymentFactory { public static Payment createPayment(String type) { if ("Alipay".equals(type)) return new Alipay(); else if ("WeChat".equals(type)) return new WeChatPay(); else throw new IllegalArgumentException("不支持的支付类型"); } } ``` --- #### 二、**策略模式(Strategy Pattern)** **应用场景**:需要动态切换算法或业务规则时,例如促销活动中的折扣策略。 **实现方式**: 1. 定义策略接口声明算法。 2. 实现多个具体策略类。 3. 结合工厂模式动态选择策略。 **项目案例**: - **电商订单折扣计算**:根据活动类型(百分比折扣、满减)动态选择计算方式,引用中的案例通过工厂生成策略实例[^3]。 - **物流运费计算**:不同地区采用不同的运费计算规则。 ```java // 示例:策略模式结合工厂模式 public class DiscountStrategyFactory { public static DiscountStrategy getStrategy(String type) { if ("Percentage".equals(type)) return new PercentageDiscount(); else if ("Fixed".equals(type)) return new FixedDiscount(); else throw new IllegalArgumentException("未知策略类型"); } } ``` --- #### 三、**单例模式Singleton Pattern)** **应用场景**:需要全局唯一实例时,例如数据库连接池、日志管理器。 **实现方式**: 1. 私有化构造方法。 2. 提供静态方法获取唯一实例。 **项目案例**: - **Spring的Bean默认作用域**:单例模式确保Bean实例全局唯一[^1]。 - **配置管理器**:统一读取配置文件,避免重复加载。 ```java // 示例:双重校验锁实现线程安全单例 public class ConfigManager { private static volatile ConfigManager instance; private ConfigManager() {} public static ConfigManager getInstance() { if (instance == null) { synchronized (ConfigManager.class) { if (instance == null) { instance = new ConfigManager(); } } } return instance; } } ``` --- #### 四、**观察者模式(Observer Pattern)** **应用场景**:一对多依赖关系,例如事件通知、消息订阅。 **实现方式**: 1. 定义主题(Subject)和观察者(Observer)接口。 2. 主题维护观察者列表,状态变化时通知所有观察者。 **项目案例**: - **用户注册事件处理**:用户注册后触发邮件通知、积分发放等操作。 - **股票价格变动通知**:当股票价格变化时,通知所有订阅的用户。 ```java // 示例:用户注册事件观察者 public interface EventListener { void onEvent(Event event); } public class EmailService implements EventListener { @Override public void onEvent(Event event) { // 发送邮件逻辑 } } ``` --- #### 五、**模板方法模式(Template Method Pattern)** **应用场景**:算法流程固定但某些步骤可定制化,例如数据处理流水线。 **实现方式**: 1. 抽象类定义算法骨架和抽象方法。 2. 子类实现具体步骤。 **项目案例**: - **JUnit测试生命周期**:`setUp()`和`tearDown()`方法由子类实现,框架控制执行流程[^4]。 - **文件解析器**:固定解析流程(读取、验证、转换),子类实现不同文件类型的解析逻辑。 ```java // 示例:文件解析模板 public abstract class FileParser { public void parse(String file) { readFile(file); validate(); convert(); } protected abstract void readFile(String file); protected abstract void validate(); protected abstract void convert(); } ``` --- ### 设计模式的选择建议 1. **分析问题本质**:例如需要解耦对象创建时选择工厂模式,需要动态切换算法时选择策略模式。 2. **避免过度设计**:简单场景直接编码,复杂场景再引入模式。 3. **结合使用模式**:如策略模式+工厂模式实现动态策略生成[^3],或观察者模式+单例模式管理全局事件中心。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值