别学23种了!Java项目中最常用的6个设计模式,附案例

大家好,我是大华。 设计模式对于很多老开发来说也已经是很熟悉了,但有时候写代码还是:new 对象() ,if-else 套八层。

我以前也是,什么单例、工厂、观察者,名字听着高大上,但实际用起来总觉得别扭。 后来在实际项目中慢慢体会,才发现这些模式用对了能省不少事。

下面写几个在日常开发中最常用的设计模式和例子。

1. 单例模式:一个类只有一个实例

什么是单例模式?

简单说,单例就是保证一个类只有一个实例,并提供一个全局访问点。

什么场景用?

  • 需要控制资源使用的场景,比如数据库连接池
  • 全局配置信息
  • 日志记录器

实际案例

比如在订单系统里,有个负责短信通知的服务。这个服务如果每次都新建实例,不仅浪费资源,还可能导致重复发送短信(想想用户收到10条相同的订单确认短信是什么体验😂)。

java 复制代码
public class SMSService {
    // 私有静态实例
    private static SMSService instance;
    
    // 私有构造方法
    private SMSService() {
        // 初始化短信配置
    }
    
    // 公共访问点
    public static synchronized SMSService getInstance() {
        if (instance == null) {
            instance = new SMSService();
        }
        return instance;
    }
    
    public void sendOrderConfirmation(String phoneNumber, String orderInfo) {
        // 发送订单确认短信
        System.out.println("向" + phoneNumber + "发送订单确认短信: " + orderInfo);
    }
}

使用时:

java 复制代码
// 获取短信服务实例
SMSService smsService = SMSService.getInstance();
// 发送订单确认短信
smsService.sendOrderConfirmation("13800138000", "订单#12345已确认");

小贴士:上面是最基础的单例实现,实际项目中可能会用双重检查锁定或静态内部类方式来优化性能。

2. 工厂模式:对象创建的"流水线"

什么是工厂模式?

工厂模式就是把创建对象的过程封装起来,调用者不需要关心具体实现类,只需要告诉工厂"我要这种类型的对象"。

什么场景用?

  • 不确定需要创建哪种类型的对象
  • 对象的创建逻辑比较复杂
  • 想要隐藏具体实现类,只暴露接口

实际案例

假设我们在开发一个支付系统,需要支持微信、支付宝、银行卡等多种支付方式。每种支付方式的处理逻辑不同,但基本流程类似。

java 复制代码
// 支付接口
public interface PaymentMethod {
    void processPayment(double amount);
}

// 微信支付
public class WeChatPay implements PaymentMethod {
    @Override
    public void processPayment(double amount) {
        System.out.println("使用微信支付" + amount + "元");
        // 微信支付具体逻辑
    }
}

// 支付宝
public class AliPay implements PaymentMethod {
    @Override
    public void processPayment(double amount) {
        System.out.println("使用支付宝支付" + amount + "元");
        // 支付宝支付具体逻辑
    }
}

// 银行卡支付
public class BankCardPay implements PaymentMethod {
    @Override
    public void processPayment(double amount) {
        System.out.println("使用银行卡支付" + amount + "元");
        // 银行卡支付具体逻辑
    }
}

// 支付工厂
public class PaymentFactory {
    public static PaymentMethod getPaymentMethod(String type) {
        if ("wechat".equals(type)) {
            return new WeChatPay();
        } else if ("alipay".equals(type)) {
            return new AliPay();
        } else if ("bankcard".equals(type)) {
            return new BankCardPay();
        }
        throw new IllegalArgumentException("不支持的支付方式: " + type);
    }
}

使用时:

java 复制代码
// 用户选择了支付方式
String paymentType = "wechat"; // 可能来自用户选择
double amount = 99.8; // 订单金额

// 获取对应的支付处理器并处理支付
PaymentMethod payment = PaymentFactory.getPaymentMethod(paymentType);
payment.processPayment(amount);

这样,当我们需要增加新的支付方式(比如信用卡)时,只需要新增一个实现类并修改工厂,而不需要改动使用支付的代码。

3. 策略模式:可替换的算法家族

什么是策略模式?

策略模式定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。

什么场景用?

  • 有多种相似的处理方式,需要根据条件动态选择
  • 避免使用大量的if-else
  • 算法可能经常变化

实际案例

电商系统中的优惠策略就是典型例子。比如我们有满减、折扣、直减等多种优惠方式,用户可以选择一种应用到订单上。

java 复制代码
// 优惠策略接口
public interface DiscountStrategy {
    double calculateDiscount(double totalAmount);
}

// 满减策略
public class FullReductionStrategy implements DiscountStrategy {
    private double threshold; // 满多少
    private double reduction; // 减多少
    
    public FullReductionStrategy(double threshold, double reduction) {
        this.threshold = threshold;
        this.reduction = reduction;
    }
    
    @Override
    public double calculateDiscount(double totalAmount) {
        if (totalAmount >= threshold) {
            return reduction;
        }
        return 0;
    }
}

// 打折策略
public class PercentageDiscountStrategy implements DiscountStrategy {
    private double percentage; // 折扣比例
    
    public PercentageDiscountStrategy(double percentage) {
        this.percentage = percentage;
    }
    
    @Override
    public double calculateDiscount(double totalAmount) {
        return totalAmount * (1 - percentage);
    }
}

// 直减策略
public class DirectReductionStrategy implements DiscountStrategy {
    private double reduction; // 直减金额
    
    public DirectReductionStrategy(double reduction) {
        this.reduction = reduction;
    }
    
    @Override
    public double calculateDiscount(double totalAmount) {
        return reduction;
    }
}

// 订单类
public class Order {
    private List<Item> items; // 订单商品
    private DiscountStrategy discountStrategy; // 优惠策略
    
    public Order(List<Item> items) {
        this.items = items;
    }
    
    // 设置优惠策略
    public void setDiscountStrategy(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }
    
    // 计算订单总价
    public double calculateTotal() {
        double total = 0;
        for (Item item : items) {
            total += item.getPrice() * item.getQuantity();
        }
        
        // 应用优惠
        if (discountStrategy != null) {
            double discount = discountStrategy.calculateDiscount(total);
            total -= discount;
        }
        
        return total;
    }
}

使用时:

java 复制代码
// 创建订单
List<Item> items = Arrays.asList(
    new Item("手机", 2999, 1),
    new Item("保护壳", 49, 1)
);
Order order = new Order(items);

// 根据活动选择优惠策略
// 例如:满3000减300
if (isFullReductionPromotion()) {
    order.setDiscountStrategy(new FullReductionStrategy(3000, 300));
}
// 或者:全场8折
else if (isPercentageDiscountPromotion()) {
    order.setDiscountStrategy(new PercentageDiscountStrategy(0.2));
}
// 或者:直减50元
else if (isDirectReductionPromotion()) {
    order.setDiscountStrategy(new DirectReductionStrategy(50));
}

// 计算最终价格
double finalPrice = order.calculateTotal();
System.out.println("订单最终价格: " + finalPrice);

这样设计的好处是,我们可以随时增加新的优惠策略,比如"第二件半价",而不需要修改订单处理的核心逻辑。

4. 观察者模式:事件通知机制

什么是观察者模式?

观察者模式定义了对象之间的一对多依赖关系,当一个对象状态改变时,它的所有依赖者都会收到通知并自动更新。

什么场景用?

  • 一个对象状态改变需要通知其他多个对象
  • 发布-订阅场景
  • 事件处理系统

实际案例

一个很常见的例子是订单状态变更通知。当订单状态发生变化时,需要通知多个相关系统,如库存系统、物流系统、短信通知等。

java 复制代码
// 观察者接口
public interface OrderObserver {
    void update(Order order);
}

// 具体观察者:库存系统
public class InventorySystem implements OrderObserver {
    @Override
    public void update(Order order) {
        if ("PAID".equals(order.getStatus())) {
            System.out.println("库存系统收到通知:订单#" + order.getId() + "已支付,准备扣减库存");
            // 扣减库存逻辑
        } else if ("CANCELLED".equals(order.getStatus())) {
            System.out.println("库存系统收到通知:订单#" + order.getId() + "已取消,恢复库存");
            // 恢复库存逻辑
        }
    }
}

// 具体观察者:物流系统
public class LogisticsSystem implements OrderObserver {
    @Override
    public void update(Order order) {
        if ("PAID".equals(order.getStatus())) {
            System.out.println("物流系统收到通知:订单#" + order.getId() + "已支付,准备发货");
            // 安排发货逻辑
        } else if ("CANCELLED".equals(order.getStatus())) {
            System.out.println("物流系统收到通知:订单#" + order.getId() + "已取消,取消配送");
            // 取消配送逻辑
        }
    }
}

// 具体观察者:短信通知系统
public class SMSNotificationSystem implements OrderObserver {
    @Override
    public void update(Order order) {
        System.out.println("短信系统收到通知:向用户" + order.getUserPhone() + "发送订单状态变更短信");
        // 发送短信逻辑
    }
}

// 被观察者:订单类
public class Order {
    private String id;
    private String status;
    private String userPhone;
    private List<OrderObserver> observers = new ArrayList<>();
    
    public Order(String id, String userPhone) {
        this.id = id;
        this.userPhone = userPhone;
        this.status = "CREATED";
    }
    
    // 添加观察者
    public void addObserver(OrderObserver observer) {
        observers.add(observer);
    }
    
    // 移除观察者
    public void removeObserver(OrderObserver observer) {
        observers.remove(observer);
    }
    
    // 通知所有观察者
    private void notifyObservers() {
        for (OrderObserver observer : observers) {
            observer.update(this);
        }
    }
    
    // 更新订单状态
    public void setStatus(String status) {
        this.status = status;
        System.out.println("订单#" + id + "状态变更为: " + status);
        notifyObservers();
    }
    
    // getter方法
    public String getId() { return id; }
    public String getStatus() { return status; }
    public String getUserPhone() { return userPhone; }
}

使用时:

java 复制代码
// 创建订单
Order order = new Order("12345", "13800138000");

// 添加观察者
order.addObserver(new InventorySystem());
order.addObserver(new LogisticsSystem());
order.addObserver(new SMSNotificationSystem());

// 订单支付后,状态变更
order.setStatus("PAID");

// 输出:
// 订单#12345状态变更为: PAID
// 库存系统收到通知:订单#12345已支付,准备扣减库存
// 物流系统收到通知:订单#12345已支付,准备发货
// 短信系统收到通知:向用户13800138000发送订单状态变更短信

这种设计的好处是,当我们需要在订单状态变更时增加新的处理(比如添加一个积分系统),只需要创建新的观察者并注册到订单对象,而不需要修改订单类的代码。

5. 装饰器模式:动态扩展功能

什么是装饰器模式?

装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。

什么场景用?

  • 需要动态地给对象添加功能,而且这些功能可以组合使用
  • 不想使用继承来扩展功能
  • 需要在运行时选择不同功能组合

实际案例

咖啡订单系统是个很好的例子。一杯基础咖啡可以添加各种配料(牛奶、糖、巧克力等),每种配料都会影响最终价格。

java 复制代码
// 咖啡接口
public interface Coffee {
    String getDescription();
    double getCost();
}

// 基础咖啡类
public class SimpleCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "简单咖啡";
    }
    
    @Override
    public double getCost() {
        return 10.0; // 基础价格
    }
}

// 装饰器基类
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }
    
    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }
    
    @Override
    public double getCost() {
        return decoratedCoffee.getCost();
    }
}

// 具体装饰器:牛奶
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + " + 牛奶";
    }
    
    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 2.0; // 牛奶加2元
    }
}

// 具体装饰器:糖
public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + " + 糖";
    }
    
    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 1.0; // 糖加1元
    }
}

// 具体装饰器:巧克力
public class ChocolateDecorator extends CoffeeDecorator {
    public ChocolateDecorator(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + " + 巧克力";
    }
    
    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 3.0; // 巧克力加3元
    }
}

使用时:

java 复制代码
// 创建一杯简单咖啡
Coffee coffee = new SimpleCoffee();
System.out.println(coffee.getDescription() + " 价格: " + coffee.getCost());

// 加牛奶
coffee = new MilkDecorator(coffee);
System.out.println(coffee.getDescription() + " 价格: " + coffee.getCost());

// 加糖
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getDescription() + " 价格: " + coffee.getCost());

// 加巧克力
coffee = new ChocolateDecorator(coffee);
System.out.println(coffee.getDescription() + " 价格: " + coffee.getCost());

// 输出:
// 简单咖啡 价格: 10.0
// 简单咖啡 + 牛奶 价格: 12.0
// 简单咖啡 + 牛奶 + 糖 价格: 13.0
// 简单咖啡 + 牛奶 + 糖 + 巧克力 价格: 16.0

装饰器模式的优点是可以动态组合各种配料,而不需要为每种组合创建一个子类。想象一下,如果用继承,我们可能需要创建"加奶咖啡"、"加糖咖啡"、"加奶加糖咖啡"等多个类,而且组合越多,类就越多。

6. 适配器模式:兼容不同接口

什么是适配器模式?

适配器模式让原本由于接口不兼容而不能一起工作的类可以一起工作。

什么场景用?

  • 需要使用现有类,但其接口不符合你的需求
  • 需要统一多个类的接口
  • 需要复用一些现有的类,这些类的接口不符合系统的需求

实际案例

假设我们的系统需要集成多个第三方支付平台,但每个平台的API接口都不一样。我们可以使用适配器模式来统一这些接口。

java 复制代码
// 我们系统中定义的统一支付接口
public interface PaymentProcessor {
    boolean processPayment(String orderId, double amount, String currency);
    PaymentStatus checkStatus(String paymentId);
}

// 第三方支付平台A的原始接口
public class AlipaySDK {
    public String pay(String orderNo, double money, String type) {
        System.out.println("使用支付宝支付: " + orderNo + ", 金额: " + money);
        // 实际调用支付宝API
        return "ALI" + System.currentTimeMillis(); // 返回支付宝交易号
    }
    
    public int queryPayStatus(String tradeNo) {
        System.out.println("查询支付宝交易状态: " + tradeNo);
        // 实际查询支付宝交易状态
        return 1; // 1表示成功,0表示处理中,-1表示失败
    }
}

// 第三方支付平台B的原始接口
public class WeChatPayAPI {
    public Map<String, String> createPayment(Map<String, Object> payData) {
        String orderId = (String) payData.get("out_trade_no");
        Double amount = (Double) payData.get("total_fee");
        System.out.println("使用微信支付: " + orderId + ", 金额: " + amount);
        // 实际调用微信支付API
        Map<String, String> result = new HashMap<>();
        result.put("transaction_id", "WX" + System.currentTimeMillis());
        result.put("result_code", "SUCCESS");
        return result;
    }
    
    public Map<String, String> queryPaymentStatus(String transactionId) {
        System.out.println("查询微信支付状态: " + transactionId);
        // 实际查询微信支付状态
        Map<String, String> result = new HashMap<>();
        result.put("trade_state", "SUCCESS"); // SUCCESS, PROCESSING, FAILED
        return result;
    }
}

// 支付状态枚举
public enum PaymentStatus {
    SUCCESS, PROCESSING, FAILED
}

// 支付宝适配器
public class AlipayAdapter implements PaymentProcessor {
    private AlipaySDK alipaySDK;
    private Map<String, String> orderToPaymentMap = new HashMap<>();
    
    public AlipayAdapter() {
        this.alipaySDK = new AlipaySDK();
    }
    
    @Override
    public boolean processPayment(String orderId, double amount, String currency) {
        try {
            String paymentId = alipaySDK.pay(orderId, amount, "direct");
            orderToPaymentMap.put(orderId, paymentId);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    
    @Override
    public PaymentStatus checkStatus(String orderId) {
        String paymentId = orderToPaymentMap.get(orderId);
        if (paymentId == null) {
            return PaymentStatus.FAILED;
        }
        
        int status = alipaySDK.queryPayStatus(paymentId);
        switch (status) {
            case 1: return PaymentStatus.SUCCESS;
            case 0: return PaymentStatus.PROCESSING;
            default: return PaymentStatus.FAILED;
        }
    }
}

// 微信支付适配器
public class WeChatPayAdapter implements PaymentProcessor {
    private WeChatPayAPI weChatPayAPI;
    private Map<String, String> orderToPaymentMap = new HashMap<>();
    
    public WeChatPayAdapter() {
        this.weChatPayAPI = new WeChatPayAPI();
    }
    
    @Override
    public boolean processPayment(String orderId, double amount, String currency) {
        try {
            Map<String, Object> payData = new HashMap<>();
            payData.put("out_trade_no", orderId);
            payData.put("total_fee", amount);
            payData.put("fee_type", currency);
            
            Map<String, String> result = weChatPayAPI.createPayment(payData);
            if ("SUCCESS".equals(result.get("result_code"))) {
                orderToPaymentMap.put(orderId, result.get("transaction_id"));
                return true;
            }
            return false;
        } catch (Exception e) {
            return false;
        }
    }
    
    @Override
    public PaymentStatus checkStatus(String orderId) {
        String transactionId = orderToPaymentMap.get(orderId);
        if (transactionId == null) {
            return PaymentStatus.FAILED;
        }
        
        Map<String, String> result = weChatPayAPI.queryPaymentStatus(transactionId);
        String status = result.get("trade_state");
        switch (status) {
            case "SUCCESS": return PaymentStatus.SUCCESS;
            case "PROCESSING": return PaymentStatus.PROCESSING;
            default: return PaymentStatus.FAILED;
        }
    }
}

使用时:

java 复制代码
// 订单处理逻辑
public class OrderService {
    private PaymentProcessor paymentProcessor;
    
    public OrderService(PaymentProcessor paymentProcessor) {
        this.paymentProcessor = paymentProcessor;
    }
    
    public boolean processOrder(String orderId, double amount, String currency) {
        // 处理订单逻辑...
        
        // 调用统一的支付接口
        boolean paymentSuccess = paymentProcessor.processPayment(orderId, amount, currency);
        
        if (paymentSuccess) {
            System.out.println("订单支付处理中: " + orderId);
            // 其他订单处理逻辑...
            return true;
        } else {
            System.out.println("订单支付失败: " + orderId);
            return false;
        }
    }
    
    public void checkOrderPaymentStatus(String orderId) {
        PaymentStatus status = paymentProcessor.checkStatus(orderId);
        System.out.println("订单" + orderId + "的支付状态: " + status);
        
        // 根据支付状态处理订单...
    }
}

// 客户端代码
public static void main(String[] args) {
    // 根据配置或用户选择决定使用哪个支付方式
    String paymentMethod = "alipay"; // 或者 "wechat"
    
    PaymentProcessor paymentProcessor;
    if ("alipay".equals(paymentMethod)) {
        paymentProcessor = new AlipayAdapter();
    } else {
        paymentProcessor = new WeChatPayAdapter();
    }
    
    OrderService orderService = new OrderService(paymentProcessor);
    
    // 处理订单
    String orderId = "ORD" + System.currentTimeMillis();
    orderService.processOrder(orderId, 99.9, "CNY");
    
    // 稍后检查支付状态
    orderService.checkOrderPaymentStatus(orderId);
}

通过适配器模式,我们可以让不同的支付平台API都适配到我们系统定义的统一接口,这样订单处理逻辑就不需要关心具体使用的是哪个支付平台,大大提高了系统的灵活性和可维护性。

总结

在实际开发中,我们不要刻意去套用某个模式,而是应该根据实际问题选择合适的方案。当你发现自己的代码结构跟某个设计模式很像时,那就说明你已经在不知不觉中用上了设计模式。
上面介绍的几种设计模式是Java开发中最常用的,掌握它们对提高代码质量和可维护性很有帮助。当然,设计模式还有很多,比如建造者模式、代理模式、模板方法模式等,感兴趣的话可以留言继续深入学习。

希望这篇文章对你有所帮助,祝编码愉快!

我是大华,专注分享Java、Vue前后端的实战笔记。关注我,少走弯路,一起进步!

🚀觉得有用的话,点个小心心吧!

相关推荐
京东云开发者3 分钟前
库存平台稳定性建设实践
后端
wenb1n3 分钟前
SmartDB:AI与数据库的“翻译官”,开启无缝交互新时代!
后端
Warren986 分钟前
软件测试-Selenium学习笔记
java·javascript·笔记·学习·selenium·测试工具·安全
bobz96516 分钟前
Supervisord 自动重启子进程
后端
ezl1fe20 分钟前
RAG 每日一技(十八):手写SQL-RAG太累?LangChain的SQL智能体(Agent)前来救驾!
数据库·人工智能·后端
费益洲31 分钟前
Docker 核心技术:Linux Cgroups
后端
没有bug.的程序员32 分钟前
JVM 运行时数据区详解:内存模型与对象生命周期全景解析
java·jvm·运行时数据区·内存模型·对象生命周期
杨DaB33 分钟前
【SpringBoot】Dubbo、Zookeeper
spring boot·后端·zookeeper·dubbo·java-zookeeper
一语长情1 小时前
Netty流量整形:保障微服务通信稳定性的关键策略
java·后端·架构
冲鸭ONE1 小时前
java数据类型与语句结构
后端