为了便于理解Golang的设计模式,将其与Java的设计模式对比,可以为之前熟悉Java的同学提供一个便利的思路; Golang 和 Java 在实现单例模式、工厂模式时,因语言特性(如面向对象模型、并发机制、类型系统等)的差异,呈现出不同的设计思路和实现方式。以下从两种模式分别对比分析:
一、单例模式
单例模式的核心是确保一个类型只有唯一实例,并提供全局访问点。两者的实现差异主要源于语言的并发模型、对象创建方式和访问控制机制。
1. Java 的单例模式实现
Java 是纯面向对象语言,基于类和对象模型,单例通常通过私有构造函数 + 静态方法实现,需重点解决线程安全和反射 / 序列化破坏单例的问题。常见实现方式:
饿汉式:类加载时初始化实例,天然线程安全(JVM 类加载机制保证),但可能提前占用资源。
public class Singleton {
// 类加载时初始化
private static final Singleton INSTANCE = new Singleton();
// 私有构造函数,禁止外部创建
private Singleton() {}
// 全局访问点
public static Singleton getInstance() {
return INSTANCE;
}
}
-
懒汉式(双重检查锁定):延迟初始化,通过
synchronized
和volatile
保证线程安全(避免指令重排序导致的空指针)。public class Singleton { // volatile防止指令重排序 private static volatile Singleton INSTANCE; private Singleton() {} public static Singleton getInstance() { if (INSTANCE == null) { // 第一次检查(无锁,提高效率) synchronized (Singleton.class) { if (INSTANCE == null) { // 第二次检查(加锁,确保唯一) INSTANCE = new Singleton(); } } } return INSTANCE; } }
-
枚举单例:利用枚举的天然单例特性(JVM 保证),可防止反射和序列化破坏,是最安全的实现。
public enum Singleton { INSTANCE; // 唯一实例 // 业务方法 public void doSomething() {} }
2. Golang 的单例模式实现
Golang 无 “类” 概念,基于结构体(struct) 和包级变量,单例实现依赖语言的并发原语(如sync.Once
、sync.Mutex
) 和访问控制(首字母大小写)。核心是保证并发安全的初始化。
-
sync.Once
实现:Golang 推荐方式,sync.Once
确保初始化代码仅执行一次,天然线程安全(适用于 goroutine 并发场景)。package singleton import "sync" // 结构体首字母小写,包外不可直接访问 type singleton struct{} var ( instance *singleton once sync.Once // 保证初始化只执行一次 ) // 全局访问点(首字母大写,包外可调用) func GetInstance() *singleton { once.Do(func() { // 初始化逻辑仅执行一次 instance = &singleton{} }) return instance }
-
包级变量实现:Golang 包初始化在
main
函数前执行,且是单线程的(天然线程安全),适合无需延迟初始化的场景。package singleton type Singleton struct{} // 包初始化时创建实例,包外通过变量直接访问 var Instance = &Singleton{}
-
互斥锁实现:类似 Java 的双重检查,但 Golang 的
sync.Mutex
更轻量,适合需要手动控制初始化时机的场景。package singleton import "sync" type singleton struct{} var ( instance *singleton mu sync.Mutex initialized bool ) func GetInstance() *singleton { if !initialized { // 第一次检查(无锁) mu.Lock() defer mu.Unlock() if !initialized { // 第二次检查(加锁) instance = &singleton{} initialized = true } } return instance }
单例模式核心差异
维度 | Java 特点 | Golang 特点 |
---|---|---|
实现基础 | 基于类和对象,依赖私有构造函数限制创建 | 基于结构体和包,依赖首字母小写限制访问 |
线程安全机制 | 依赖synchronized 、volatile 或枚举特性 | 依赖sync.Once (推荐)、sync.Mutex 或包初始化机制 |
延迟初始化 | 需手动实现(如双重检查) | 天然支持(sync.Once + 函数调用) |
防破坏机制 | 需额外处理反射 / 序列化(枚举单例除外) | 无需考虑(无反射破坏单例的常见场景) |
二、工厂模式
工厂模式的核心是隐藏对象创建逻辑,通过 “工厂” 统一生成实例,分为简单工厂、工厂方法、抽象工厂。两者的差异源于类型系统(接口实现方式、继承支持)和函数特性。
1. Java 的工厂模式实现
Java 是强类型面向对象语言,支持类继承和接口显式实现,工厂模式通常通过 “工厂类 + 接口” 实现,结构更严谨。
-
简单工厂:一个工厂类通过静态方法根据参数创建不同子类实例(违反开闭原则,但简单直观)。
// 产品接口 public interface Product { void use(); } // 具体产品 public class ProductA implements Product { @Override public void use() { /* 实现 */ } } public class ProductB implements Product { @Override public void use() { /* 实现 */ } } // 简单工厂类 public class SimpleFactory { public static Product createProduct(String type) { if ("A".equals(type)) return new ProductA(); if ("B".equals(type)) return new ProductB(); throw new IllegalArgumentException("无效类型"); } } // 使用 Product p = SimpleFactory.createProduct("A");
-
工厂方法:每个产品对应一个工厂类,工厂类实现统一的工厂接口(遵循开闭原则,扩展方便)。
// 工厂接口 public interface Factory { Product create(); } // 具体工厂 public class FactoryA implements Factory { @Override public Product create() { return new ProductA(); } } public class FactoryB implements Factory { @Override public Product create() { return new ProductB(); } } // 使用 Factory factory = new FactoryA(); Product p = factory.create();
-
抽象工厂:创建一系列相关产品,工厂接口定义多个产品创建方法,具体工厂实现所有方法。
// 抽象产品族 public interface ProductA {} public interface ProductB {} // 具体产品 public class ConcreteProductA1 implements ProductA {} public class ConcreteProductB1 implements ProductB {} // 抽象工厂 public interface AbstractFactory { ProductA createProductA(); ProductB createProductB(); } // 具体工厂(生产一套产品) public class Factory1 implements AbstractFactory { @Override public ProductA createProductA() { return new ConcreteProductA1(); } @Override public ProductB createProductB() { return new ConcreteProductB1(); } }
2. Golang 的工厂模式实现
Golang 无类继承,但支持接口隐式实现(无需implements
关键字),且函数是一等公民,工厂模式通常通过函数(而非工厂类) 实现,更简洁灵活。
-
简单工厂:通过一个函数根据参数返回不同结构体实例(结构体隐式实现同一接口)。
// 产品接口 type Product interface { Use() } // 具体产品 type ProductA struct{} func (a *ProductA) Use() { /* 实现 */ } type ProductB struct{} func (b *ProductB) Use() { /* 实现 */ } // 简单工厂函数 func NewProduct(t string) (Product, error) { switch t { case "A": return &ProductA{}, nil case "B": return &ProductB{}, nil default: return nil, fmt.Errorf("无效类型") } } // 使用 p, _ := NewProduct("A") p.Use()
-
工厂方法:通过 “工厂函数类型” 或 “结构体方法” 实现,无需定义工厂接口(依赖隐式匹配)。
// 工厂函数类型(返回Product的函数) type Factory func() Product // 具体工厂函数 func NewProductA() Product { return &ProductA{} } func NewProductB() Product { return &ProductB{} } // 使用 var factory Factory = NewProductA p := factory()
-
抽象工厂:通过一组相关的工厂函数实现,返回不同接口的实例(模拟产品族)。
// 产品族接口 type ProductA interface { DoA() } type ProductB interface { DoB() } // 具体产品 type ConcreteA1 struct{} func (a *ConcreteA1) DoA() {} type ConcreteB1 struct{} func (b *ConcreteB1) DoB() {} // 抽象工厂(一组函数) type AbstractFactory struct { NewA func() ProductA NewB func() ProductB } // 具体工厂(初始化函数组) var Factory1 = AbstractFactory{ NewA: func() ProductA { return &ConcreteA1{} }, NewB: func() ProductB { return &ConcreteB1{} }, } // 使用 a := Factory1.NewA() b := Factory1.NewB()
工厂模式核心差异
维度 | Java 特点 | Golang 特点 |
---|---|---|
实现载体 | 基于工厂类(实现工厂接口) | 基于工厂函数(或函数组),无需工厂类 |
接口实现 | 显式声明implements ,编译期严格检查 | 隐式实现(结构匹配接口即可),更灵活 |
扩展性 | 依赖类继承,扩展需新增工厂类 | 依赖函数或结构体组合,扩展更轻量 |
产品族支持(抽象工厂) | 通过接口定义多个创建方法,结构严谨 | 通过函数组或结构体字段组合,更简洁 |