设计模式,作为一套广泛认可的解决方案,专门用于应对特定的编程难题。它们将复杂逻辑优雅地解耦,增强代码的灵活性和可维护性。本文将详细介绍设计模式的核心理念、它们的显著优点,以及五种关键的创建型模式。
设计模式概念
设计模式是在软件设计中反复出现的问题的经验性解决方案。这些解决方案是经过时间测试和验证的,可以在特定的上下文中应用,以解决通常会在软件设计和开发过程中遇到的一些常见问题。设计模式提供了一种通用的、可重用的解决方案,有助于构建更灵活、可维护和可扩展的软件系统。
设计模式并不是一种具体的代码实现,而是一种抽象的思想和指导原则,开发者可以根据这些原则来构建满足特定需求的具体代码。设计模式的使用有助于提高代码的可读性、可维护性和可重用性,同时可以促使开发者以更系统化和标准化的方式进行软件设计。
常见的设计模式可以分为三大类:
- 创建型模式(Creational Patterns)(5种): 关注对象的创建过程,包括单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式等。
- 结构型模式(Structural Patterns)(7种): 关注如何组合类和对象以构建更大的结构,包括适配器模式、装饰者模式、代理模式、桥接模式、组合模式、外观模式、享元模式等。
- 行为型模式(Behavioral Patterns)(10种): 关注对象之间的通信和责任分配,包括观察者模式、策略模式、命令模式、状态模式、责任链模式、访问者模式、中介者模式、备忘录模式、解释器模式、迭代器模式等。
设计模式优点
- 代码重用性: 设计模式提供了经过验证和优化的解决方案,可以在不同的项目和场景中重复使用。这有助于减少代码重复,提高代码的可维护性和可扩展性。
- 代码可读性: 使用设计模式可以使代码更易于理解和阅读。设计模式通常使用一种标准化的方式来解决特定类型的问题,使得代码结构更加清晰,降低了代码的复杂性。
- 团队协作: 设计模式提供了一种共享的设计语言,可以促进团队之间的沟通。当团队成员熟悉常见的设计模式时,他们能够更容易地理解和交流彼此的设计选择。
- 可维护性: 使用设计模式可以提高代码的可维护性。由于设计模式强调松耦合和模块化,当需要修改或扩展功能时,只需关注特定的模块而不是整个代码库。
- 问题解决能力: 学习设计模式有助于培养抽象思维和问题解决的能力。设计模式提供了一种框架,可以应对各种常见的软件设计问题,帮助开发人员更好地理解和解决复杂的系统设计挑战。
学习设计模式是提高软件设计和开发水平的有效途径,可以帮助开发人员写出更健壮、可维护和可扩展的代码。
创建型模式(Creational Patterns)
关注对象的创建过程,旨在提供一种灵活的方法来实例化对象。这些模式都涉及到将对象的创建和初始化过程封装起来,使系统更加灵活、松散耦合,同时更容易扩展。
单例模式(Singleton)
- 目的:确保一个类只有一个实例,并提供一个全局访问点。
- 应用场景: 用于需要共享资源的场景,如数据库连接、日志记录等。
- 案例:假设有一个配置管理器类ConfigManager,它负责加载和维护一些系统配置。由于配置信息通常在整个应用程序中是统一且唯一的,因此这个类的实例应该只有一个,以确保整个应用程序使用的是相同的配置信息。
- Java 实现
// ConfigManager.java
public class ConfigManager {
// 创建一个ConfigManager类型的私有静态实例
private static ConfigManager instance;
// 私有化构造函数,防止外部通过new创建多个实例
private ConfigManager() {
// 初始化配置信息
}
// 提供一个公共的静态方法,用于获取唯一的实例
public static ConfigManager getInstance() {
// 双重检查锁定
if (instance == null) {
synchronized (ConfigManager.class) {
if (instance == null) {
instance = new ConfigManager();
}
}
}
return instance;
}
// 其他业务方法
public void loadConfig() {
// 加载配置信息的方法
}
}
// Main.java
// 使用方式
public class Main {
public static void main(String[] args) {
ConfigManager configManager = ConfigManager.getInstance();
configManager.loadConfig();
// todo 从这里开始,可以使用配置信息
// 通过单例模式获取两个实例
// ConfigManager instance1 = ConfigManager.getInstance();
// ConfigManager instance2 = ConfigManager.getInstance();
// // 检查两个实例的引用是否相同
// if (instance1 == instance2) {
// System.out.println("instance1 和 instance2 是同一个实例。");
// } else {
// System.out.println("instance1 和 instance2 不是同一个实例。");
// }
}
}
- golang实现
package singleton
import (
"sync"
)
// 定义单例类型
type singleton struct {
// 这里可以包含单例的状态
}
// 私有的单例变量
var instance *singleton
var once sync.Once
// 获取单例对象的公共方法
func GetInstance() *singleton {
// 只执行一次的确保单例实例化
once.Do(func() {
instance = &singleton{}
})
return instance
}
// 使用方式
package main
import (
"fmt"
"singleton"
)
func main() {
// 获取单例实例
instance1 := singleton.GetInstance()
instance2 := singleton.GetInstance()
// 检查两个实例是否相同
if instance1 == instance2 {
fmt.Println("instance1 和 instance2 是同一个实例。")
} else {
fmt.Println("instance1 和 instance2 不是同一个实例。")
}
}
工厂方法模式(Factory Method)
- 目的:定义一个用于创建对象的接口,让子类决定实例化哪一个类。
- 应用场景:当一个类希望其子类来指定创建的对象时。
- 案例:设计一个日志记录器的应用,它可以将日志记录到不同的地方,比如文件、数据库或者网络。定义一个日志记录器接口和一系列实现这个接口的具体日志记录器。使用工厂方法模式来创建具体的日志记录器实例,而不是直接在代码中实例化这些记录器。
- 案例对象图
- Java实现
// Logger.java
// 日志记录器接口
public interface Logger {
void log(String message);
}
// FileLogger.java
// 文件日志记录器
public class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("File logger: " + message);
}
}
// DatabaseLogger.java
// 数据库日志记录器
public class DatabaseLogger implements Logger {
@Override
public void log(String message) {
System.out.println("Database logger: " + message);
}
}
// NetworkLogger.java
// 网络日志记录器
public class NetworkLogger implements Logger {
@Override
public void log(String message) {
System.out.println("Network logger: " + message);
}
}
// LoggerFactory.java
// 日志记录器工厂接口
public abstract class LoggerFactory {
private Logger logger = null;
// 工厂方法
public abstract Logger createLogger();
// 工厂方法调用示例
public void logMessage(String message) {
// 如果logger未被创建,那么创建它
if (logger == null) {
logger = createLogger();
}
logger.log(message);
}
}
// FileLoggerFactory.java
// 文件日志记录器工厂
public class FileLoggerFactory extends LoggerFactory {
@Override
public Logger createLogger() {
// 创建文件日志记录器
return new FileLogger();
}
}
// DatabaseLoggerFactory.java
// 数据库日志记录器工厂
public class DatabaseLoggerFactory extends LoggerFactory {
@Override
public Logger createLogger() {
// 创建数据库日志记录器
return new DatabaseLogger();
}
}
// NetworkLoggerFactory.java
// 网络日志记录器工厂
public class NetworkLoggerFactory extends LoggerFactory {
@Override
public Logger createLogger() {
// 创建网络日志记录器
return new NetworkLogger();
}
}
// Main.java
// 使用
public class Main {
public static void main(String[] args) {
LoggerFactory factory;
factory = new FileLoggerFactory();
factory.logMessage("This is a file log message.");
factory = new DatabaseLoggerFactory();
factory.logMessage("This is a database log message.");
factory = new NetworkLoggerFactory();
factory.logMessage("This is a network log message.");
}
}
- golang实现
package logger
// Logger 是所有日志记录器必须实现的接口
type Logger interface {
Log(message string)
}
// FileLogger 是一个日志记录器的实现,它将日志写入文件
type FileLogger struct{}
func (l *FileLogger) Log(message string) {
// 这里将消息写入文件
fmt.Println("File logger: ", message)
}
// DatabaseLogger 是一个日志记录器的实现,它将日志写入数据库
type DatabaseLogger struct{}
func (l *DatabaseLogger) Log(message string) {
// 这里将消息写入数据库
fmt.Println("Database logger: ", message)
}
// NetworkLogger 是一个日志记录器的实现,它通过网络发送日志
type NetworkLogger struct{}
func (l *NetworkLogger) Log(message string) {
// 这里将消息发送到网络
fmt.Println("Network logger: ", message)
}
package logger
// LoggerType 定义了可能的日志记录器类型
type LoggerType int
const (
File LoggerType = iota
Database
Network
)
// GetLogger 是工厂方法,返回一个 Logger 接口
func GetLogger(t LoggerType) Logger {
switch t {
case File:
return &FileLogger{}
case Database:
return &DatabaseLogger{}
case Network:
return &NetworkLogger{}
default:
return nil
}
}
package main
import (
"logger"
)
func main() {
fileLogger := logger.GetLogger(logger.File)
fileLogger.Log("This is a file log message.")
dbLogger := logger.GetLogger(logger.Database)
dbLogger.Log("This is a database log message.")
netLogger := logger.GetLogger(logger.Network)
netLogger.Log("This is a network log message.")
}
抽象工厂模式(Abstract Factory)
-
目的:创建一系列相关或相互依赖的对象,而无需指定它们具体的类。
-
应用场景:当需要创建一系列相关的对象,且不想暴露其具体实现时。
-
案例:设计一个支付系统,不同的支付方式(例如信用卡、支付宝、微信支付)都有不同的实现方式,但系统需要对这些支付方式进行统一的处理。
-
案例对象图 -
-
Java实现
// PaymentProcessor.java
// 支付接口
public interface PaymentProcessor {
void processPayment(String amount);
}
// AlipayProcessor.java
// 支付宝支付
public class AlipayProcessor implements PaymentProcessor {
@Override
public void processPayment(String amount) {
System.out.println("Processing Alipay payment for: " + amount);
}
}
// WeChatProcessor.java
// 微信支付
public class WeChatProcessor implements PaymentProcessor {
@Override
public void processPayment(String amount) {
System.out.println("Processing WeChat payment for: " + amount);
}
}
// CreditCardProcessor.java
// 信用卡支付
public class CreditCardProcessor implements PaymentProcessor {
@Override
public void processPayment(String amount) {
System.out.println("Processing credit card payment for: " + amount);
}
}
// PaymentFactory.java
// 支付工厂接口
public interface PaymentFactory {
PaymentProcessor createProcessor();
}
// AlipayPaymentFactory.java
// 支付宝支付工厂
public class AlipayPaymentFactory implements PaymentFactory {
@Override
public PaymentProcessor createProcessor() {
return new AlipayProcessor();
}
}
// WeChatPaymentFactory.java
// 微信支付工厂
public class WeChatPaymentFactory implements PaymentFactory {
@Override
public PaymentProcessor createProcessor() {
return new WeChatProcessor();
}
}
// CreditCardPaymentFactory.java
// 信用卡支付工厂
public class CreditCardPaymentFactory implements PaymentFactory {
@Override
public PaymentProcessor createProcessor() {
return new CreditCardProcessor();
}
}
// PaymentService.java
public class PaymentService {
private final PaymentFactory paymentFactory;
public PaymentService(PaymentFactory paymentFactory) {
this.paymentFactory = paymentFactory;
}
public void makePayment(String amount) {
PaymentProcessor processor = paymentFactory.createProcessor();
processor.processPayment(amount);
}
}
// 使用
// Main.java
public class Main {
public static void main(String[] args) {
PaymentFactory creditCardFactory = new CreditCardPaymentFactory();
PaymentFactory alipayFactory = new AlipayPaymentFactory();
PaymentFactory weChatFactory = new WeChatPaymentFactory();
PaymentService creditCardService = new PaymentService(creditCardFactory);
creditCardService.makePayment("1000");
PaymentService alipayService = new PaymentService(alipayFactory);
alipayService.makePayment("2000");
PaymentService weChatService = new PaymentService(weChatFactory);
weChatService.makePayment("3000");
}
}
- golang实现
package main
import "fmt"
// PaymentProcessor 定义支付方式的接口
type PaymentProcessor interface {
ProcessPayment(amount string)
}
// Alipay 支付宝支付
type AlipayProcessor struct{}
func (p *AlipayProcessor) ProcessPayment(amount string) {
fmt.Println("Processing Alipay payment for:", amount)
}
// WeChatPay 微信支付
type WeChatProcessor struct{}
func (p *WeChatProcessor) ProcessPayment(amount string) {
fmt.Println("Processing WeChat payment for:", amount)
}
// CreditCard 信用卡支付
type CreditCardProcessor struct{}
func (p *CreditCardProcessor) ProcessPayment(amount string) {
fmt.Println("Processing credit card payment for:", amount)
}
// PaymentFactory 是创建支付方式的工厂接口
type PaymentFactory interface {
CreateProcessor() PaymentProcessor
}
// 具体的支付宝支付工厂
type AlipayFactory struct{}
type AlipayPaymentFactory struct{}
func (f AlipayPaymentFactory) CreateProcessor() PaymentProcessor {
return &AlipayProcessor{}
}
// 具体的微信支付工厂
type WeChatPaymentFactory struct{}
func (w *WeChatPaymentFactory) CreateProcessor() PaymentProcessor {
return &WeChatProcessor{}
}
// 具体的信用卡支付工厂
type CreditCardPaymentFactory struct{}
func (c *CreditCardPaymentFactory) CreateProcessor() PaymentProcessor {
return &CreditCardProcessor{}
}
type PaymentService struct {
factory PaymentFactory
}
func NewPaymentService(factory PaymentFactory) *PaymentService {
return &PaymentService{factory: factory}
}
func (s *PaymentService) MakePayment(amount string) {
processor := s.factory.CreateProcessor()
processor.ProcessPayment(amount)
}
func main() {
creditCardFactory := &CreditCardPaymentFactory{}
alipayFactory := &AlipayPaymentFactory{}
weChatFactory := &WeChatPaymentFactory{}
creditCardService := NewPaymentService(creditCardFactory)
creditCardService.MakePayment("1000")
alipayService := NewPaymentService(alipayFactory)
alipayService.MakePayment("2000")
weChatService := NewPaymentService(weChatFactory)
weChatService.MakePayment("3000")
}
建造者模式(Builder)
- 目的:将一个复杂对象的构建与其表示分离,以便相同的构建过程可以创建不同的表示。
- 应用场景:当创建复杂对象的算法应独立于该对象的组成部分以及它们的装配方式时。
- 案例:构建一个复杂的Computer对象,它包含多个部件,如CPU、RAM、硬盘和显卡等。这些部件在不同的计算机中可能会有不同的配置。使用建造者模式可以灵活地构建各种不同配置的计算机。
- Java实现
// Computer.java
public class Computer {
// Computer的各个部件
private String CPU;
private String RAM;
private String hardDisk;
private String graphicsCard;
// 私有的构造函数,防止直接实例化
private Computer(Builder builder) {
this.CPU = builder.CPU;
this.RAM = builder.RAM;
this.hardDisk = builder.hardDisk;
this.graphicsCard = builder.graphicsCard;
}
// 内部的静态Builder类
public static class Builder {
private String CPU;
private String RAM;
private String hardDisk;
private String graphicsCard;
public Builder CPU(String CPU) {
this.CPU = CPU;
return this;
}
public Builder RAM(String RAM) {
this.RAM = RAM;
return this;
}
public Builder hardDisk(String hardDisk) {
this.hardDisk = hardDisk;
return this;
}
public Builder graphicsCard(String graphicsCard) {
this.graphicsCard = graphicsCard;
return this;
}
public Computer build() {
return new Computer(this);
}
}
@Override
public String toString() {
return "Computer{" +
"CPU='" + CPU + '\'' +
", RAM='" + RAM + '\'' +
", hardDisk='" + hardDisk + '\'' +
", graphicsCard='" + graphicsCard + '\'' +
'}';
}
}
// Main.java
// 使用
public class Client {
public static void main(String[] args) {
Computer computer = new Computer.Builder()
.CPU("Intel Core i7")
.RAM("16GB")
.hardDisk("1TB SSD")
.graphicsCard("NVIDIA RTX 3080")
.build();
System.out.println(computer);
}
}
- golang实现
package main
// Computer 结构体代表要构建的复杂对象
type Computer struct {
CPU string
RAM string
HardDisk string
GraphicsCard string
}
// Builder 接口定义了构建Computer所需的步骤
type Builder interface {
SetCPU(cpu string) Builder
SetRAM(ram string) Builder
SetHardDisk(hd string) Builder
SetGraphicsCard(gc string) Builder
Build() Computer
}
// ComputerBuilder 是实现Builder接口的结构体
type ComputerBuilder struct {
cpu string
ram string
hardDisk string
graphicsCard string
}
func (b *ComputerBuilder) SetCPU(cpu string) Builder {
b.cpu = cpu
return b
}
func (b *ComputerBuilder) SetRAM(ram string) Builder {
b.ram = ram
return b
}
func (b *ComputerBuilder) SetHardDisk(hd string) Builder {
b.hardDisk = hd
return b
}
func (b *ComputerBuilder) SetGraphicsCard(gc string) Builder {
b.graphicsCard = gc
return b
}
func (b *ComputerBuilder) Build() Computer {
return Computer{
CPU: b.cpu,
RAM: b.ram,
HardDisk: b.hardDisk,
GraphicsCard: b.graphicsCard,
}
}
func main() {
builder := ComputerBuilder{}
computer := builder.SetCPU("Intel Core i7").
SetRAM("16GB").
SetHardDisk("1TB SSD").
SetGraphicsCard("NVIDIA RTX 3080").
Build()
fmt.Printf("Computer: %+v\n", computer)
}
原型模式(Prototype)
- 目的:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
- 应用场景:当需要创建的对象与现有对象相似,或者当创建对象的成本比直接创建一个类的实例更高时。
- 案例:假设有一个Car类,它有一些昂贵的初始化过程。如果我们需要创建一个与现有Car对象属性完全相同的新对象时,使用原型模式可以避免重新执行昂贵的初始化过程。
- Java实现
// Car.java
public class Car implements Cloneable {
private String make;
private String model;
public Car(String make, String model) {
this.make = make;
this.model = model;
// 假设这里有昂贵的初始化过程
}
public String getMake() {
return make;
}
public String getModel() {
return model;
}
// 实现clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Car{" +
"make='" + make + '\'' +
", model='" + model + '\'' +
'}';
}
}
// Main.java
// 使用
public class Main {
public static void main(String[] args) {
Car car1 = new Car("China", "Xiao mi");
try {
Car car2 = (Car) car1.clone();
System.out.println(car1);
System.out.println(car2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
- golang实现
package prototype
// Prototype 接口定义了克隆自身的方法
type Prototype interface {
Clone() Prototype
}
// Car 结构体实现了 Prototype 接口
type Car struct {
Make, Model string
}
// Clone 方法实现了深拷贝
func (c *Car) Clone() Prototype {
return &Car{
Make: c.Make,
Model: c.Model,
}
}
package main
import (
"fmt"
"prototype"
)
func main() {
car1 := &prototype.Car{Make: "China", Model: "Xiao mi"}
// 使用原型模式克隆 Car 实例
car2 := car1.Clone()
fmt.Println(car1) // 输出原始对象
fmt.Println(car2) // 输出克隆对象
}