工厂方法模式完整实现:GPS转换

一、设计模式中的工厂方法

在软件设计中,工厂方法模式 (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 项目中运行。

相关推荐
likerhood2 小时前
工厂方法模式(Factory Method Pattern)
工厂方法模式
胡志辉的博客5 小时前
多智能体协作,不是多开几个 Agent:从中介者模式看 OpenClaw 和 Hermes Agent
人工智能·设计模式·ai·agent·中介者模式·openclaw·herman
shark22222225 小时前
能懂!基于Springboot的用户增删查改(三层设计模式)
spring boot·后端·设计模式
014-code6 小时前
日志规范:怎么写才不算写废话
java·开发语言·设计模式·日志
楼田莉子7 小时前
同步/异步日志系统:日志落地模块\日志器模块\异步日志模块
linux·服务器·c++·学习·设计模式
kyriewen1114 小时前
代码写成一锅粥?这5种设计模式让你的项目“起死回生”
前端·javascript·设计模式·typescript·ecmascript·html5
kyriewen14 小时前
代码写成一锅粥?这5种设计模式让你的项目“起死回生”
前端·javascript·设计模式
两年半的个人练习生^_^14 小时前
每日一学:设计模式之原型模式
java·开发语言·设计模式·原型模式
断眉的派大星1 天前
工厂模式(Factory Pattern)完整详解
python·设计模式