用状态/策略模式替换if/else逻辑

8
我已经阅读了以前有关Java中替换条件逻辑(如IF / ELSE)的Stack Exchange,例如状态/策略模式,但我不确定我的情况是否适合这种替换。以下是我查看的两个相关问题 - Java中的if语句列表将许多“if else”语句转换为更简洁的方法 我基本上正在编写一个文件下载管理器,以下是我的IF/ELSE结构:
  1. 如果文件及其zip文件存在,则将zip文件移动到zip文件目录并读取文件
  2. 如果zip文件存在,则解压文件并将zip文件移动到zip文件目录并读取文件
  3. 如果zip文件不存在,则从指定URL下载并解压缩它,并读取文件并将zip文件移动到指定目录。
  4. 如果URL中不存在zip文件则创建空文件并将其写入磁盘。
本质上,我理解为将这四个条件作为哈希映射中的键,然后值是需要发出的“命令”。但是,我认为您仍然需要一个If/Else来决定在给定作为输入的键上调用哪个操作。所以我看不到它的好处。有人能解释一下吗?
2个回答

5
我认为你应该使用GoF模式责任链。您应该引入两个接口:1)Condition,您将在其中检查正确的条件,例如“如果zip文件不存在”,并返回布尔结果-如果满足条件,则为“true”,否则为“else”,2)Execution strategy,它将运行与条件分配的操作,例如“从指定URL下载它,然后解压缩它并读取文件并将zip文件移动到指定目录”。因此,第一个接口将回答“何时”,第二个接口将回答“然后”。 “Condition”实现和“execution strategy”实现应该组合成“元组”(或对,条目等)。这个“元组”应该按顺序移动到集合中,您已经描述了它。然后,当您需要处理zip文件时,您将遍历集合,调用条件并检查结果,如果结果为“true”,则调用适当的“execution strategy”。此外,条件可以与执行策略结合并移至单个接口/实现,具有两种方法。可以在条件/执行策略之间传递描述zip文件当前状态的上下文。 希望这有所帮助。
更新。 Java代码示例。
/**
 * All implementations should check proper condition
 */
interface Condition { 

  /**
   * Check if condition is satisfied
   *
   * @param pathToFile path to target file
   * @return 'true' if condition is satisfied, otherwise 'false'
   */
  boolean isSatisfied(String pathToFile); //i've made an assumption that you'll manipulate file path for checking file
}
...
/**
 * Childs will wrap some portion of code (if you'll use language, that supports lambdas/functors, this interface/implementation can be replaced with lambda/functor)
 */
interface Action {

  /**
   * Execute some portion of code
   *
   * @param pathToFile path to target file
   */ 
  void execute(String pathToFile);
}
...
class ZipFileExistsCondition implements Condition {

  @Override
  public boolean isSatisfied(String pathToFile) {
   ... //check if zip file exists
  }
}
...
class ZipFileDoesNotExists implements Condition {
  @Override
  public boolean isSatisfied(String pathToFile) {
   ... //download zip file and move it to some temp directory
   //if file downloaded ok, than return 'true' otherwise 'false'
  }
}
...
class AlwaysSatisfiedCondition implements Condition {
  @Override
  public boolean isSatisfied(String pathToFile) {
   ... //always returns 'true', to run action assigned with this condition
  }
}
...
Collection<Map.Entry<Condition, Action>> steps = Arrays.asList(
 new AbstractMap.ImmutableEntry<Condition, Action>(new ZipFileExistsCondition(), 
 new Action() { /*move zip file to zip file directory and read in file*/ }),
 new ZipFileDoesNotExists(), new Action() { /*download it from specified URL and then unzip it and read in file and move zip file to specified directory*/ },
 new AlwaysSatisfiedCondition(), new Action() { /*create blank file and write it out to disk*/  }
);
...
String pathToFile = ...
...
for(Map.Entry<Condition, Action> step: steps) {
 if(!step.getKey().isSatisfied(pathToFile))
   continue;

 step.getValue().execute(pathToFile); 
}  

备注: 1)您可以将“条件”实现为匿名类, 2)“AlwaysSatisfiedCondition”可以是单例模式, 3)如果您使用Java / Groovy / Scala,则可以使用Guava / Apache Commons的“Predicate”代替“Condition”,使用“Function”或“Closure”代替“Action”。

如果您需要在第一个“满足”的条件和适当的操作执行后退出,则只需在操作执行后放置“break”/“return”。


谢谢您的回复。根据我的理解,您希望我迭代一个集合,然后创建一个if语句来确定需要执行哪种策略,是这样吗? - gansub
不应该使用“if/then/else”语句,而是应该迭代集合并询问每个“condition.isSatisfied”,这样每个“condition”都将包装“if”语句。 - nndru
太好了。那么这里的关键点就是迭代的“顺序”。一旦顺序确定,那么就很简单了。为了更清晰明了,我有一个实现Condition接口的FileReadCondition和一个实现Execution接口的FileReadExecution。FileReadCondition是否委托给FileReadExecution?也许一些代码会有所帮助。 - gansub
是的,你说得对,顺序应该反映检查逻辑。 - nndru
如果状态是复合状态,即压缩文件和未压缩文件都存在怎么办?那我也需要一个复合条件,对吧? - gansub

2
以下是编译此代码的正确方法。关键点在于AbstractMap.SimpleImmutableEntry是单个条目。如果您希望添加更多条目,则需要为每个条目实例化该类。
Collection<Map.Entry<Condition,Action>> steps = Arrays.asList
    (
     (new AbstractMap.SimpleImmutableEntry<Condition,Action>
      (new FileExistsCondition(),
       new Action()
       {
       public void execute(String pathToFile){System.out.println("the path to file is srtm " + pathToFile);}
      }
       )
      ),
     (new AbstractMap.SimpleImmutableEntry<Condition,Action>
      (new ZipFileExistsCondition(),
       new Action()
       {
       public void execute(String pathToFile){System.out.println("the path to file is  " + pathToFile);}
      }
       )
      ),
     (new AbstractMap.SimpleImmutableEntry<Condition,Action>
      (new ZipFileDoesNotExistCondition(),
       new Action()
       {
       public void execute(String pathToFile){System.out.println("the path to file is " + pathToFile);}
      }
      )
      )
     );

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