一、设计模式中的工厂方法
在软件设计中,工厂方法模式 (Factory Method)是应用最广泛的创建型设计模式之一。它定义了一个创建对象的接口,但由子类决定实例化哪一个类,从而将对象的创建过程延迟到子类中执行。
工厂方法模式主要包含以下角色:
- 产品接口(Product):定义公共操作规范
- 具体产品(Concrete Product):实现产品接口的具体类
- 工厂接口(Factory):声明返回产品类型的方法
- 具体工厂(Concrete Factory):实现工厂接口,负责创建具体产品
其核心价值在于解耦对象的创建与使用 :客户端只需知道工厂类型,而不需要关心对象的构造细节,当需要扩展新的产品类型时,只需新增对应的产品类和工厂类,无需修改已有代码,符合开闭原则。
二、百度/高德/腾讯 GPS 坐标转换接口说明
在中国大陆开发地图相关应用时,坐标转换是绕不开的技术环节。不同地图服务商采用了不同的坐标系,直接使用原始 GPS 坐标会导致显著的位置偏移。
| 坐标系 | 说明 | 适用场景 |
|---|---|---|
| WGS-84 | 国际标准 GPS 坐标系 | 原始 GPS 设备输出的坐标 |
| GCJ-02 | 火星坐标系,中国国家测绘局制定的加密坐标系 | 高德地图、腾讯地图、Google 中国地图 |
| BD-09 | 百度坐标系,在 GCJ-02 基础上进行了二次加密 | 百度地图 |
实测数据显示,直接使用未经转换的 WGS84 坐标在高德地图上显示时,偏移量可达 500-1000 米。因此,在集成地图服务时,必须将坐标转换到目标服务商要求的坐标系。
2.1 各服务商坐标转换接口
百度地图坐标转换 API:
- 接口地址:
https://api.map.baidu.com/geoconv/v1/ - 支持将 WGS84 或 GCJ02 坐标转换为 BD09 百度坐标
高德地图坐标转换 API:
- 接口地址:
https://restapi.amap.com/v3/assistant/coordinate/convert - 支持将 GPS 坐标、mapbar 坐标、baidu 坐标转换为高德坐标
腾讯地图坐标转换 API:
- 接口地址:
https://apis.map.qq.com/ws/coord/v1/translate - 支持将 GPS 坐标、百度经纬度、搜狗经纬度等批量转换为腾讯地图坐标系
三、工厂模式实现 GPS 坐标转换
以下是使用 C# 实现工厂方法模式来封装百度、高德、腾讯坐标转换接口的完整示例。
3.1 产品接口:定义坐标转换器规范
csharp
/// <summary>
/// 坐标转换结果
/// </summary>
public class CoordConvertResult
{
public bool Success { get; set; }
public double? Latitude { get; set; }
public double? Longitude { get; set; }
public string? ErrorMessage { get; set; }
}
/// <summary>
/// 产品接口:坐标转换器
/// </summary>
public interface ICoordConverter
{
/// <summary>
/// 将 WGS84 GPS 坐标转换为目标地图坐标系
/// </summary>
Task<CoordConvertResult> ConvertFromWgs84Async(double latitude, double longitude);
/// <summary>
/// 服务商名称
/// </summary>
string ProviderName { get; }
}
3.2 具体产品:百度坐标转换器
csharp
using System.Text.Json;
/// <summary>
/// 百度地图坐标转换器
/// </summary>
public class BaiduCoordConverter : ICoordConverter
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
private const string ApiUrl = "https://api.map.baidu.com/geoconv/v1/";
public BaiduCoordConverter(string apiKey)
{
_apiKey = apiKey;
_httpClient = new HttpClient();
}
public string ProviderName => "百度地图";
public async Task<CoordConvertResult> ConvertFromWgs84Async(double latitude, double longitude)
{
try
{
// 百度坐标转换接口:from=1(WGS84) to=5(BD09)
var url = $"{ApiUrl}?coords={longitude},{latitude}&from=1&to=5&ak={_apiKey}";
var response = await _httpClient.GetStringAsync(url);
var json = JsonDocument.Parse(response);
var root = json.RootElement;
if (root.GetProperty("status").GetInt32() == 0)
{
var result = root.GetProperty("result")[0];
return new CoordConvertResult
{
Success = true,
Longitude = result.GetProperty("x").GetDouble(),
Latitude = result.GetProperty("y").GetDouble()
};
}
else
{
return new CoordConvertResult
{
Success = false,
ErrorMessage = $"百度坐标转换失败: {root.GetProperty("message").GetString()}"
};
}
}
catch (Exception ex)
{
return new CoordConvertResult
{
Success = false,
ErrorMessage = $"百度坐标转换异常: {ex.Message}"
};
}
}
}
3.3 具体产品:高德坐标转换器
csharp
/// <summary>
/// 高德地图坐标转换器
/// </summary>
public class AmapCoordConverter : ICoordConverter
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
private const string ApiUrl = "https://restapi.amap.com/v3/assistant/coordinate/convert";
public AmapCoordConverter(string apiKey)
{
_apiKey = apiKey;
_httpClient = new HttpClient();
}
public string ProviderName => "高德地图";
public async Task<CoordConvertResult> ConvertFromWgs84Async(double latitude, double longitude)
{
try
{
// 高德坐标转换:locations 格式为 "经度,纬度",coordsys=gps
var url = $"{ApiUrl}?locations={longitude},{latitude}&coordsys=gps&output=JSON&key={_apiKey}";
var response = await _httpClient.GetStringAsync(url);
var json = JsonDocument.Parse(response);
var root = json.RootElement;
if (root.GetProperty("status").GetString() == "1")
{
var locations = root.GetProperty("locations").GetString()?.Split(',');
if (locations != null && locations.Length == 2)
{
return new CoordConvertResult
{
Success = true,
Longitude = double.Parse(locations[0]),
Latitude = double.Parse(locations[1])
};
}
}
return new CoordConvertResult
{
Success = false,
ErrorMessage = $"高德坐标转换失败: {root.GetProperty("info").GetString()}"
};
}
catch (Exception ex)
{
return new CoordConvertResult
{
Success = false,
ErrorMessage = $"高德坐标转换异常: {ex.Message}"
};
}
}
}
3.4 具体产品:腾讯坐标转换器
csharp
/// <summary>
/// 腾讯地图坐标转换器
/// </summary>
public class TencentCoordConverter : ICoordConverter
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
private const string ApiUrl = "https://apis.map.qq.com/ws/coord/v1/translate";
public TencentCoordConverter(string apiKey)
{
_apiKey = apiKey;
_httpClient = new HttpClient();
}
public string ProviderName => "腾讯地图";
public async Task<CoordConvertResult> ConvertFromWgs84Async(double latitude, double longitude)
{
try
{
// 腾讯坐标转换:type=1 表示 GPS 坐标
var url = $"{ApiUrl}?locations={latitude},{longitude}&type=1&key={_apiKey}";
var response = await _httpClient.GetStringAsync(url);
var json = JsonDocument.Parse(response);
var root = json.RootElement;
if (root.GetProperty("status").GetInt32() == 0)
{
var locations = root.GetProperty("locations");
if (locations.GetArrayLength() > 0)
{
var first = locations[0];
return new CoordConvertResult
{
Success = true,
Latitude = first.GetProperty("lat").GetDouble(),
Longitude = first.GetProperty("lng").GetDouble()
};
}
}
return new CoordConvertResult
{
Success = false,
ErrorMessage = $"腾讯坐标转换失败: {root.GetProperty("message").GetString()}"
};
}
catch (Exception ex)
{
return new CoordConvertResult
{
Success = false,
ErrorMessage = $"腾讯坐标转换异常: {ex.Message}"
};
}
}
}
3.5 工厂接口与具体工厂
csharp
/// <summary>
/// 工厂接口:坐标转换器工厂
/// </summary>
public interface ICoordConverterFactory
{
ICoordConverter CreateConverter();
}
/// <summary>
/// 百度坐标转换器工厂
/// </summary>
public class BaiduConverterFactory : ICoordConverterFactory
{
private readonly string _apiKey;
public BaiduConverterFactory(string apiKey) => _apiKey = apiKey;
public ICoordConverter CreateConverter() => new BaiduCoordConverter(_apiKey);
}
/// <summary>
/// 高德坐标转换器工厂
/// </summary>
public class AmapConverterFactory : ICoordConverterFactory
{
private readonly string _apiKey;
public AmapConverterFactory(string apiKey) => _apiKey = apiKey;
public ICoordConverter CreateConverter() => new AmapCoordConverter(_apiKey);
}
/// <summary>
/// 腾讯坐标转换器工厂
/// </summary>
public class TencentConverterFactory : ICoordConverterFactory
{
private readonly string _apiKey;
public TencentConverterFactory(string apiKey) => _apiKey = apiKey;
public ICoordConverter CreateConverter() => new TencentCoordConverter(_apiKey);
}
/// <summary>
/// 地图服务商枚举
/// </summary>
public enum MapProvider
{
Baidu,
Amap,
Tencent
}
/// <summary>
/// 配置中心:统一工厂创建入口
/// </summary>
public static class CoordConverterConfig
{
private static readonly Dictionary<MapProvider, string> ApiKeys = new();
public static void RegisterApiKey(MapProvider provider, string apiKey)
{
ApiKeys[provider] = apiKey;
}
public static ICoordConverterFactory GetFactory(MapProvider provider)
{
return provider switch
{
MapProvider.Baidu => new BaiduConverterFactory(ApiKeys[MapProvider.Baidu]),
MapProvider.Amap => new AmapConverterFactory(ApiKeys[MapProvider.Amap]),
MapProvider.Tencent => new TencentConverterFactory(ApiKeys[MapProvider.Tencent]),
_ => throw new NotSupportedException($"不支持的地图服务商: {provider}")
};
}
}
3.6 客户端调用示例
csharp
/// <summary>
/// 客户端使用示例
/// </summary>
public class ClientExample
{
public static async Task Main()
{
// 1. 配置各服务商的 API Key(需要到对应开放平台申请)
CoordConverterConfig.RegisterApiKey(MapProvider.Baidu, "你的百度AK");
CoordConverterConfig.RegisterApiKey(MapProvider.Amap, "你的高德Key");
CoordConverterConfig.RegisterApiKey(MapProvider.Tencent, "你的腾讯Key");
// 2. 原始 GPS 坐标(WGS84)
double gpsLat = 22.502729; // 深圳湾公园示例坐标
double gpsLng = 113.945324;
// 3. 根据需求选择地图服务商,通过工厂创建转换器
var factory = CoordConverterConfig.GetFactory(MapProvider.Amap);
var converter = factory.CreateConverter();
Console.WriteLine($"使用转换器: {converter.ProviderName}");
var result = await converter.ConvertFromWgs84Async(gpsLat, gpsLng);
if (result.Success)
{
Console.WriteLine($"转换成功: ({result.Latitude}, {result.Longitude})");
}
else
{
Console.WriteLine($"转换失败: {result.ErrorMessage}");
}
// 4. 轻松切换服务商,只需改变枚举值
await ConvertForProvider(MapProvider.Baidu, gpsLat, gpsLng);
await ConvertForProvider(MapProvider.Tencent, gpsLat, gpsLng);
}
private static async Task ConvertForProvider(MapProvider provider, double lat, double lng)
{
var factory = CoordConverterConfig.GetFactory(provider);
var converter = factory.CreateConverter();
var result = await converter.ConvertFromWgs84Async(lat, lng);
if (result.Success)
{
Console.WriteLine($"{converter.ProviderName}: ({result.Latitude}, {result.Longitude})");
}
else
{
Console.WriteLine($"{converter.ProviderName} 转换失败: {result.ErrorMessage}");
}
}
}
四、实际应用场景
在实际项目中,上述工厂模式的实现可以广泛应用于:
- 多地图服务商切换:产品需要同时支持多家地图服务,或根据用户偏好选择最优服务商
- 坐标数据入库前标准化:将不同来源的坐标统一转换到项目基准坐标系后再存储
- GPS 轨迹展示:将原始 GPS 轨迹实时转换为当前展示地图所需的坐标系
- 地理围栏检测:将用户上报的 GPS 坐标转换为服务商标准坐标系后进行位置匹配
五、完整代码结构
解决方案:
├── ICoordConverter.cs // 产品接口
├── CoordConvertResult.cs // 转换结果类
├── Converters/
│ ├── BaiduCoordConverter.cs // 百度具体产品
│ ├── AmapCoordConverter.cs // 高德具体产品
│ └── TencentCoordConverter.cs// 腾讯具体产品
├── Factories/
│ ├── ICoordConverterFactory.cs // 工厂接口
│ ├── BaiduConverterFactory.cs // 百度具体工厂
│ ├── AmapConverterFactory.cs // 高德具体工厂
│ ├── TencentConverterFactory.cs // 腾讯具体工厂
│ └── CoordConverterConfig.cs // 配置中心
└── Program.cs // 客户端调用示例
💡 提示:使用上述代码前,需要先到百度地图开放平台、高德开放平台、腾讯位置服务官网申请 API Key,并注意各服务商的请求配额限制。以上示例代码中已包含必要的 using 指令和异常处理,可直接集成到你的 .NET 项目中运行。