策略模式是正确的选择吗?

3

我希望你能帮助我解决问题:

我有一个进行SOAP调用的类。但是,如果SOAP定义发生更改,我将不得不编写一个新的类或继承它等等。 因此,我想到了编写如下内容的解决方案:

switch(version)
{
  case "1.0":
     saopV1.getData()
  case "2.0":
     soapV2.getData()
}

我知道这是很糟糕的代码。然后我了解到策略模式,我想,哇,这正是我需要摆脱这个糟糕的switch-case的东西:

abstract SoapVersion
{
    public SoapVersion GetSoapVersion(string version)
    {
         //Damn switch-case thing
         //with return new SoapV1() and return new SoapV2()
    }
    public string[] virtual getData()
    {
          //Basic Implementation
    }
}

class SoapV1:SoapVersion
{
       public override string[] getData()
       {
           //Detail Implementation
       }
}

class SoapV2:SoapVersion
{//the same like soapv1}

但我无法避免在我的代码中使用"if"或switch语句。 是否可以使用面向对象技术来解决这个问题?

编辑: GetSoapVersion函数应该是静态的。

6个回答

4

这更或多或少是以美观的方式完成此操作的正确方法。 在代码的某个点上,您必须做出决策,是使用v1还是v2,因此您必须有一个条件语句(if或switch)。但是,使用策略和工厂(工厂方法或工厂类),您已经将该决策集中化。

我会使我的抽象类中的工厂方法成为静态方法。 另外,我会使用模板方法模式:即一个公共的、不可重写的GetData方法调用一个应在具体实现中重写的保护虚拟(抽象)方法。

public abstract class SoapProcessor
{

    protected SoapProcessor() { /* protected constructor since public is of no use */  }

    public static SoapProcessor Create( SoapVersion version )
    {
          switch( version )
          {
               case SoapVersion.Version1 : return new SoapV1Processor();
               case SoapVersion.Version2 : return new SoapV2Processor();
               default: throw new NOtSupportedException();
          }
    }


    public string[] GetData()
    {
         return GetDataCore();
    }

    protected abstract GetDataCore();
 }

}


感谢您的有用回复。实际上,我不知道模板方法模式,它以后非常有用。但在这种情况下,我需要定义一个默认实现。那么这个模式就不是必需的了,对吗? - Nathan Nash
当您需要定义默认实现时,我认为您不需要抽象类。 - Frederik Gheysels

1

如果你只在工厂中使用switch-case,或者在代码的各个地方都有,这是有区别的。你可以在一个单一的点上做出决定(选择哪种实现方式)。


1
在类似情况下,我会根据以下标准在反射和if/case之间做出选择:如果应该动态添加新版本支持(例如插件),则选择反射,否则选择if/case。如其他答案中所提到的,它应该位于工厂方法中,以提供一个单一的地方来创建东西。值得一提的是,Strategy是一种行为模式,而你所询问的似乎是创造性的。

关于插件的有用信息,谢谢。 所以我现在实现了工厂方法模式而不是策略模式??我有点困惑。或者工厂方法是策略模式的一种形式吗? - Nathan Nash
  1. 我会将其设计为工厂方法。
  2. 不,工厂方法不是策略模式的一种。这个讨论可能会有所帮助:https://dev59.com/I0nSa4cB1Zd3GeqPPJXS
- khachik

1

你不需要使用switch或if语句。
只需使用委托
即抽象类的具体实现将按需执行(例如SoapV1、SoapV2等),客户端设置对象引用中的适当实例
你只有一个对基类的引用,适当的子类由客户端设置。你的代码只调用基类的方法(在运行时是派生实现之一)。例如,一个示例(免责声明:未编译代码,仅为示例)

public abstract class SoapHandler
{

    protected abstract string[] getData();
 }

public class SoapHandlerV1 extends SoapHandler
{

    public string[] getData(){
        //V1 implementation
    }

}
public class SoapHandlerV2 extends SoapHandler
{

    public string[] getData(){
        //V2 implementation
    }

}


public class SoapProcessor{

    public SoapHandler soapHandler;

    public setSoapHandler(SoapHandler h)
    {
                soapHandler = h;
    }

    public String[] getData(){
        //delegate to specific version
        soapHandler->getData();
    }
}


//in your code
SoapProcessor soap = new SoapProcessor();
soap.setSoapHandler(new SoapHandlerV1());
String[] soapData = soap.getData();//Will get the appropriate version
//use soap data
//do stuff

如果不清楚我的意思,请查看GoF的策略模式示例


0
因为version仅在运行时才知道,因此它一定会归结为某些条件语句(if或switch或使用字符串和原型之间的映射等)。
因此,值得追求的目标是减少条件语句的数量/隔离变更点。

0

你应该针对接口编程,而不是具体实现

在客户端中使用一个单独的服务接口。

public interface IService
{
    string[] GetData();
}

将您的客户端编写为 -

IService srvice = ServiceFactory.GetProxy();
string[] value = service.GetData();

这样一来,当服务代理更改时,您的客户端代码就不会改变。

接下来,您可以从创建适当的代理的条件逻辑开始,将其移动到ServiceFactory类中。随后,您可以使用以下技术之一来消除有条件的逻辑:

  1. 从配置文件中读取实现类和程序集名称,并使用反射创建它。
  2. 创建具有SOAP版本作为键的代理实例字典。

告诉我为什么在这种情况下接口比抽象类更好。抽象类包含(静态)工厂方法(接口无法实现),并且抽象类可以包含基本的(或通用的)实现(如果适用,在这种情况下不清楚)。使用抽象类时,客户端代码也不会改变。 - Frederik Gheysels
嗯...我想要强调的是,调用服务方法的代码不应包含任何条件逻辑。如果抽象类比接口更合适,则可以使用抽象类。 - Unmesh Kondolikar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接