策略模式-解决同一类行为有多种实现、且需动态切换的具体实现

一  需求分析

直接上代码

根据图中代码我们可以看到作者是根据配置文件WeightParseVersion这个节点判断获取不同的数据,全代码我分析过每个if 对应一个解析方案,最终目的就是为了兼容在不同医院解析数据的需求那么有什么问题呢?

一、维护性灾难

  1. 新增 / 删除版本时必须改代码
    每加一个医院(如 WEIGHT_PARSE_NEW_HOSPITAL),都要在方法里新增 else if 分支,违反开闭原则(对扩展开放、对修改关闭)。
    示例:想支持 “广州某医院”,得改 GetWeight 方法,重新编译、部署,风险高。

  2. 分支逻辑臃肿
    现有分支已多到难以快速定位逻辑,后续版本越多,方法会变成 “几千行的屎山”,新人接手直接崩溃。

二、可读性极差

  1. 逻辑平铺直叙
    所有解析逻辑(如 GetWeightMain()/GetWeightMDJVer())混在分支里,无法直观区分策略(解析算法)调用逻辑

三、扩展性为零

  1. 无法动态加载策略
    若需运行时新增解析器(如热更新医院规则),硬编码分支完全不支持,必须重启服务改代码。

  2. 复用性为零
    解析逻辑(GetWeightBJDLO() 等)与 GetWeight 强耦合,无法在其他模块复用(如另一个服务也要解析 “电力医院” 数据)。

四、风险极高

  1. 改一处牵全身
    修改某医院解析逻辑(如 GetWeightBJDLO() 内部),需担心是否影响其他分支,测试成本指数级上升。

  2. 异常处理混乱
    若某分支抛异常(如 GetWeightSDJNYXFSTY() 解析失败),无法统一捕获、处理,易导致整个服务崩溃。

二  如何使用策略模式解决上述问题

一 抽象公共方法

1 将解析方法抽象出来调用者只需调用抽象方法无需再关心具体实现

public interface  IWeightParse
{
	string Version { get; } // 版本号
	string Parse(byte[] rawData); // 执行解析,返回体重字符串
}

2 新增实现类每个实现类都继承接口实现解析方法-策略实现

public class MuDanJiang1Parser:IWeightParse
{
	public string Version => "1"; // 版本号
	public string Parse(byte[] rawData)
	{
		string weightResult = string.Empty;
		if (rawData == null || rawData.Length == 0)
		{
			throw new WeightParsingException("原始数据不能为空。");
		}
		string res = Encoding.ASCII.GetString(rawData);
		// 使用正则表达式匹配体重数据
		var match = Regex.Match(res, @"wn(\d+\.\d+)kg");

		if (match.Success)
		{
			if (double.TryParse(match.Groups[1].Value, out double weight))
			{
				// 更新UI显示,保留一位小数
				weightResult = weight.ToString("F1");
			}
			else
			{
				throw new WeightParsingException("体重数据格式错误,无法解析为数字。");
			}
		}
		else
		{
			throw new WeightParsingException("体重数据格式错误,未找到匹配的体重信息。");

		}
		return weightResult;
	}

3 新增策略工厂提供具体策略实现类

public class ParserFactory
{
	public readonly IEnumerable<IWeightParse> _parsers;
	public ParserFactory(IEnumerable<IWeightParse> parsers)
	{
		_parsers = parsers ?? throw new ArgumentNullException(nameof(parsers), "解析器集合不能为空。");
	}

	/// <summary>
	/// 根据版本获取解析器
	/// </summary>
	/// <param name="brand"></param>
	/// <returns></returns>
	/// <exception cref="NotSupportedException"></exception>
	public IWeightParse GetParserByVersion(string version)
	{
		// 处理默认值版本
		if (version == "")
			version = "0"; // 默认值与主线版本保持一致
		return _parsers.FirstOrDefault(p => p.Version.Equals(version, StringComparison.OrdinalIgnoreCase))
		  ?? throw new NotSupportedException($"暂不支持的体重解析版本:{version}");
	}
}

4 封装调用入口-version 就是原始代码里的配置文件

/// <summary>
/// 解析协议
/// </summary>
/// <param name="rawData"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public string ParseData(byte[] rawData)
{
	if (rawData.Length <= 0)
	{
		throw new ArgumentException("原始数据不能为空。");
	}
	var parser = _parserFactory.GetParserByVersion(_pageConfig.Version);
	return parser.Parse(rawData);
}

5 策略注入

public static class WeightParseRegistrar
{
	public static IServiceCollection AddWeightParse(this IServiceCollection services)
	{
		// 1 注册解析工厂
		services.AddSingleton<ParserFactory>();
		//2 注册所有解析器集合
		var parserTypes = new[]
		{
			typeof(MainParser),
			typeof(MuDanJiang1Parser),
			typeof(MuDanJiang2Parser),
			typeof(BeiJingDianLiParser)
			//可以继续添加其他解析器类型 
		};
		// 遍历所有解析器类型并注册到服务集合中
		foreach (var type in parserTypes)
		{
			services.AddSingleton(typeof(IWeightParse), type);
		}
		return services;
	}
}

这样几部下来我们就用策略模式实现了刚才那个复杂的if else 逻辑那么这样做有什么好处呢?

策略模式三要素验证

  1. 抽象策略接口
    IWeightParse 定义了统一方法 Parse(byte[] rawData) 和属性 Version,符合抽象策略角色(封装算法的统一调用契约)。

  2. 具体策略类
    BeijingDianLiParser(及其他如 MainParser/MuDanJiang1Parser 等)实现 IWeightParse,各自封装不同版本的体重解析算法,符合具体策略角色(不同算法的实现)。

  3. 上下文 / 策略工厂
    ParserFactory 通过依赖注入收集所有 IWeightParse 实现,提供 GetParserByVersion 动态选择策略,符合上下文 / 策略工厂角色(解耦客户端与具体策略,负责策略的创建与调用)。

模式价值体现

  • 算法可插拔:新增体重解析版本(如 NewVersionParser)只需实现 IWeightParse 并注册,不影响现有逻辑。
  • 客户端解耦:调用方通过 ParserFactory 获取策略,无需关心具体解析逻辑(如 BeijingDianLiParser 的复杂字符串处理)。
  • 符合开闭原则:扩展新策略(解析算法)时,无需修改已有工厂或客户端代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值