65.9K
CodeProject 正在变化。 阅读更多。
Home

设计模式:策略

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.57/5 (12投票s)

2015 年 10 月 12 日

CPOL

2分钟阅读

viewsIcon

16291

设计模式:策略

很多时候,我们需要在不改变使用它的客户端代码的情况下,更换某个算法。在这篇文章中,我想展示一个我遇到的并使用了策略模式的用例。

它是什么?

这是 GoF 书中对该模式的官方定义

定义一系列算法,将每个算法封装起来,并使它们可以互换。策略模式允许算法独立于使用它的客户端而变化。

用例:获取外部 IP 地址

在一个我正在开发的应用程序中,我需要获取应用程序运行的计算机的外部 IP 地址。实现这一点有多种方法。这看起来是一个很好的机会来使用策略模式,因为我希望能够轻松地在不同的方法之间切换。

实现

接口非常简单

public interface IIpCheckStrategy
{
    string GetExternalIp();
}

有些服务以 JSON 格式返回数据,有些服务包含额外的文本。但是,通过将算法封装在各自的类中,客户端代码不必担心解析这些不同的返回值。这在每个类中处理。如果某个服务更改了其输出并破坏了实现,我可以通过更改实例化类的代码来恢复。

接口的具体实现如下。它们实现了 IIpCheckStrategy 接口,并负责获取数据并将解析后的 IP 地址作为 string 返回。

AWS IP 检查器

public class AwsIPCheckStrategy : IIpCheckStrategy
{
    public string GetExternalIp()
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://checkip.amazonaws.com/");
            string result = client.GetStringAsync("").Result;
            return result.TrimEnd('\n');
        }
    }
}

DynDns IP 检查器

public class DynDnsIPCheckStrategy : IIpCheckStrategy
{
    public string GetExternalIp()
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://checkip.dyndns.org/");
            HttpResponseMessage response = client.GetAsync("").Result;
            return HelperMethods.ExtractIPAddress(response.Content.ReadAsStringAsync().Result);
        }
    }
}

自定义 IP 检查器

public class CustomIpCheckStrategy : IIpCheckStrategy
{
    public string GetExternalIp()
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://check-ip.herokuapp.com/");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add
            (new MediaTypeWithQualityHeaderValue("application/json"));

            HttpResponseMessage response = client.GetAsync("").Result;
            string json = response.Content.ReadAsStringAsync().Result;
            dynamic ip = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
            string result = ip.ipAddress;
            return result;
        }
    }
}

选择算法

算法的使用者可以选择任何实现 IIpCheckStrategy 接口的类,并在它们之间切换。例如

class StrategyClient1
{
    public void Execute()
    {
        IIpCheckStrategy ipChecker;

        ipChecker = new DynDnsIPCheckStrategy();
        Console.WriteLine(ipChecker.GetExternalIp());

        ipChecker = new AwsIPCheckStrategy();
        Console.WriteLine(ipChecker.GetExternalIp());

        ipChecker = new CustomIpCheckStrategy();
        Console.WriteLine(ipChecker.GetExternalIp());

        Console.ReadKey();
    }
}

此外,在某些情况下,要使用的类名可以存储在配置文件中,以便在不重新编译应用程序的情况下在运行时更改它。例如

class StrategyClient2
{
    public void Execute()
    {
        string ipcheckerTypeName = ConfigurationManager.AppSettings["IPChecker"];

        IIpCheckStrategy ipchecker = 
        Assembly.GetExecutingAssembly().CreateInstance(ipcheckerTypeName) as IIpCheckStrategy;

        Console.WriteLine(ipchecker.GetExternalIp());
    }
}

配置中的 appSettings 将如下所示

<appsettings>
        <add key="IPChecker" value="Strategy.AwsIPCheckStrategy">
</add></appsettings>

资源

© . All rights reserved.