设计模式实战:从硬编码到灵活扩展的单位转换器

前言

通过重构单位转换器案例,揭示如何运用设计模式打造高扩展性系统,告别if-else地狱!

原始版本

在初始实现中,单位转换逻辑都堆积在一个方法里,典型的"万能上帝类"模式:

public static UnitValue convert(UnitValue unitValue, String targetUnit) {
    int value = unitValue.getValue();
    String unit = unitValue.getUnit();
    
    // 重量转换
    if (unit.equals("斤")) {
        if (targetUnit.equals("公斤")) {
            return new UnitValue(value / 2, targetUnit);
        }
    }
    if (unit.equals("公斤")) {
        if (targetUnit.equals("斤")) {
            return new UnitValue(value * 2, targetUnit);
        }
    }
    
    // 长度转换
    if (unit.equals("厘米")) {
        if (targetUnit.equals("米")) {
            return new UnitValue(value / 100, targetUnit);
        }
    }
    if (unit.equals("米")) {
        if (targetUnit.equals("厘米")) {
            return new UnitValue(value * 100, targetUnit);
        }
    }
    
    throw new IllegalArgumentException();
}

使用示例:

UnitValue result = convert(new UnitValue(1, "公斤"), "斤");
// 输出: 2斤

❌ 存在问题:

  1. 修改风险高:新增转换规则需修改核心方法
  2. 职责不单一:违反单一职责原则
  3. 可读性差:大量if-else嵌套
  4. 扩展困难:每加新单位需手动修改代码

第一次重构:应用策略模式

通过策略模式将每种转换规则封装成独立类:

// 策略接口
public interface UnitConverter {
	UnitValue convert(UnitValue unitValue, String targetUnit);
}

// 具体策略:公斤转斤
public class GongJinToJinConverter implements UnitConverter {
	@Override
	public UnitValue convert(UnitValue unitValue, String targetUnit) {
		if (unitValue.getUnit().equals("公斤") && targetUnit.equals("斤")) {
			int targetValue = unitValue.getValue() * 2;
			return new UnitValue(targetValue, targetUnit);
		}
		return null;
	}
}

// 具体策略:斤转公斤
public class JinToGongJinConverter implements UnitConverter {
    @Override
    public UnitValue convert(UnitValue unitValue, String targetUnit) {
		if (unitValue.getUnit().equals("斤") && targetUnit.equals("公斤")) {
			int targetValue = unitValue.getValue() / 2;
			return new UnitValue(targetValue, targetUnit);
		}
        return null;
    }
}

✅ 优化点:

  1. 职责分离:每个转换器负责单一转换逻辑
  2. 开闭原则:新增转换器不影响现有代码
  3. 解耦合:转换逻辑与调度逻辑分离

第二次重构:引入组合模式

通过组合模式创建可动态管理的转换器集合:

public class ComposableUnitConverter implements UnitConverter, ConverterManager {
    private List<UnitConverter> converters = new ArrayList<>();
 
    public ComposableUnitConverter() {
		addDefaultConverters(this);
	}

    // 添加默认转换器
	public static void addDefaultConverters(ConverterManager converterManager) {
		converterManager.addConverter(new GongJinToJinConverter());
		converterManager.addConverter(new JinToGongJinConverter());
		converterManager.addConverter(new LiMiToMiUnitConverter());
		converterManager.addConverter(new MiToLiMiUnitConverter());
	}

    @Override
    public UnitValue convert(UnitValue unitValue, String targetUnit) {
        for (UnitConverter converter : converters) {
            UnitValue result = converter.convert(unitValue, targetUnit);
            if (result != null) return result;
        }
        throw new IllegalArgumentException("不支持的单位转换");
    }

    // 动态管理转换器
    @Override
    public void addConverter(UnitConverter converter) {
        converters.add(converter);
    }

    @Override
    public boolean removeConverter(UnitConverter converter) {
        return converters.remove(converter);
    }
}

✅ 架构优势:

  1. 灵活扩展:运行时动态增删转换器
  2. 统一接口:使用者无需关心内部实现
  3. 便捷维护:默认转换器集中管理
  4. 支持扩展:轻松添加新单位(如温度、货币)

使用示例:

UnitConverter converter = new ComposableUnitConverter();
UnitValue result = converter.convert(new UnitValue(1, "公斤"), "斤");
// 输出: 2斤

假设需要新增公里与英里的转换:

// 新增策略类
public class KmToMileConverter implements UnitConverter {
    @Override
    public UnitValue convert(UnitValue unitValue, String targetUnit) {
        if ("公里".equals(unitValue.getUnit()) && "英里".equals(targetUnit)) {
            return new UnitValue((int)(unitValue.getValue() * 0.6214), targetUnit);
        }
        return null;
    }
}

使用示例:

// 动态扩展系统
ComposableUnitConverter converter = new ComposableUnitConverter();
converter.addConverter(new KmToMileConverter());

UnitValue result = converter.convert(new UnitValue(10, "公里"), "英里");
// 输出: 6英里

扩展成本:仅需新增一个策略类+一行注册代码,核心架构零修改

关键设计模式解析

  1. 策略模式(核心)

    • 每个转换器实现统一接口
    • 算法可互相替换
    • 符合开闭原则
  2. 组合模式

    • 树形结构管理转换器
    • 统一处理单个和组合对象
    • 支持递归组合
  3. 工厂方法模式

    • addDefaultConverters()隐藏对象创建细节
    • 集中管理默认配置

最佳实践总结

  1. 识别变化点:将频繁变更的逻辑(单位转换规则)独立封装
  2. 面向接口编程:依赖抽象(UnitConverter)而非具体实现
  3. 组合优于继承:通过对象组合动态扩展功能
  4. 开闭原则:新增功能通过扩展而非修改实现

通过这个案例可见,恰当运用设计模式能将硬编码的脆弱系统蜕变为高度灵活的框架。关键在于识别变化方向,运用模式封装变化点,最终实现配置即扩展的优雅架构。

本文举例重点在于设计模式的说明,实际上,代码依然有很多优化空间,比如使用转换器缓存,设计基于原有单位转换器的反向转换等等。

posted @ 2025-07-06 12:54  减瓦~  阅读(16)  评论(0)    收藏  举报