【问题标题】:Strategy Pattern the right thing?策略模式正确吗?
【发布时间】:2011-05-17 19:24:19
【问题描述】:

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

我有一个班级正在做肥皂电话。但是如果肥皂定义发生变化,我将不得不编写一个新类或从它继承等。 所以我找到了解决方案来写这样的东西:

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}

但我无法避免在我的代码中使用“ifs”或 switch case。这可能使用 OO 技术吗??

编辑: GetSoapVersion-Function 应该是静态的

【问题讨论】:

    标签: oop design-patterns strategy-pattern


    【解决方案1】:

    您不需要 switch 或 if 语句。
    只需使用委托
    IE。抽象类的具体实现将根据需要执行(即 SoapV1、SoapV2 等)并且客户端在对象引用中设置适当的实例
    您只有对基类的引用,并且适当的子类由客户端设置。您的代码只调用基类的方法(在运行时是派生实现之一)。例如。一个例子(DISCLAIMER:没有编译代码。只是一个示例)

    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 的示例以了解我的意思

    【讨论】:

      【解决方案2】:

      你应该program to an interface not an implementation

      拥有一个可以从客户端使用的单独服务接口。

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

      并将您的客户编码为 -

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

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

      然后您首先可以将用于创建适当代理的条件逻辑移动到ServiceFactory 类。稍后您可以使用以下技术更改它以删除条件逻辑 -

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

      【讨论】:

      • 告诉我为什么在这种情况下接口比抽象类更好。抽象类包含(静态)工厂方法(不可能使用接口),并且抽象类可以包含基本(或通用)实现(如果适用,在这种情况下不清楚)。使用 asbract 类时,客户端代码也不会更改。
      • 嗯..我要强调的一点是调用服务方法的代码不应该包含任何条件逻辑。如果抽象类比接口更适合,则可以使用抽象类。
      【解决方案3】:

      在类似的情况下,我使用以下标准在反射和if/cases 之间进行选择:如果应该动态添加新版本支持(如插件),我选择反射,否则 - if/case。正如其他答案中提到的那样,它应该在工厂方法中提供一个创建事物的地方。值得一提的是,Strategy 是一种行为模式,而您所要求的似乎是创造性的。

      【讨论】:

      • 关于插件的有用信息,谢谢。所以我实现了工厂方法模式而不是策略模式??我现在有点困惑。还是工厂方法是一种策略模式?
      • @fczler 1. 我会使用工厂方法。 2.不,工厂方法不是一种策略。这个讨论可能有用:stackoverflow.com/questions/1498823/factory-strategy-patterns
      【解决方案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();
       }
      

      }

      【讨论】:

      • 感谢您的有用回复。我实际上不知道模板方法模式,它在以后非常有用。但在这种情况下,我需要定义一个默认实现。所以这种模式不是必需的,是吗?
      • 当你需要定义一个默认实现时,我认为你不应该也需要一个抽象类。
      【解决方案5】:

      如果您的 switch-case 仅在工厂中使用,或者在整个代码中使用,情况会有所不同。你有你的决定(选择什么实现)在一个点上。

      【讨论】:

        【解决方案6】:

        因为version 只在运行时才知道,它肯定会归结为一些条件(如果或切换或使用字符串和原型之间的映射等)。

        因此,有价值的目标是减少条件句的数量/隔离变化点。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多