将许多“if else”语句转换为更清晰的方法

112

我的代码在这里检测mimeType是否等于某些MIME类型,如果是,它将进行某些转换。

public void convertToMp3(File src, File target,String mimeType){
    if(mimeType.equals("audio/mpeg")){
        ...
    }else if(mimeType.equals("audio/wav")){
        mp3ToWav();
    }else if(mimeType.equals("audio/ogg")){
        ...
    }else if(...){
    ... //More if and else here
}

由于代码中有很多else if语句,我已经将其缩短了。 有什么适合消除许多ifelseelse if语句的设计模式吗?


11
枚举和 switch 怎么样? - Pradeep Simha
1
工厂设计模式不适合您的需求吗? - Roman C
1
@RomanC,我为什么要使用工厂设计模式? - user962206
@RomanC,你能提供一个例子吗? - user962206
13
if/else语句有什么问题吗?当然,它不是一个美丽的、过度设计的面向对象编程模式,但它是直接明了且易于维护的。任何阅读它的人都可以理解它,所以为什么要试图将其转换为其他形式呢? - laurent
显示剩余6条评论
7个回答

193
你可以创建一个名为Converter的接口。然后你可以为每种Mimetype创建一个类,例如:
public interface Converter {

    public void convertToMp3();
    public void convertToOgg();

}

public class MpegConverter implements Converter {

    public void convertToMp3() {
        //Code here
    }

    public void convertToOgg() {
        //Code here
    }

}

你需要为每个转换器创建这样一个类。然后,你可以设置一个像这样的映射表:
Map<String, Converter> mimeTypeMap = new HashMap<String, Converter>();

mimeTypeMap.put("audio/mpeg", new MpegConverter());

然后你的 convertToMp3 方法将变成这样:

Converter converter = mimeTypeMap.get(mimeType);
converter.convertToMp3();

使用这种方法,您可以轻松地在将来添加不同的转换器。

所有未经测试,可能无法编译,但您可以了解思路。


2
在这个例子中,convertToMp3方法被用于MPEGConverter上,用来将MPG格式转换为MP3格式;而convertToOgg方法则用于将MPG格式转换为OGG格式。所以,它们并不是做同样的工作。命名可能会引起混淆。 - cowls
3
好主意,对我来说看起来是一个非常简洁的解决方案。只需不要忘记检查是否实际上存在处理程序,并在找不到处理程序时给出错误消息。 - Alexander Weinert
@cowls 我认为你不能在 MpegConverter 中拥有不同的方法,比如 convertToMp3()convertToOgg()。因为当你从 HashMap 中检索 MpegConverter 类时,你怎么知道要调用哪个方法呢?所以据我所知,你每个类只能有一个处理程序方法,就像 Raedwald 所说的那样,例如 convert(File src, File target)。否则,这些又会变成 if 语句:if (mimeType.equals("audio/ogg")) converter.convertToOgg(); else if (mimeType.equals("audio/mp3")) converter.convertToMp3(); 等等... 如果我错了,请纠正我。 - randomUser56789
10
对于那些可能不知道的人,这被称为“策略模式”。 - Andrew Marshall
如果不同的转换器具有不同的构造函数(1-3个参数),这些参数在创建HashMap之前是未知的,只有在转换之前才能确定? - Ewoks
显示剩余7条评论

23

如果您使用JDK7之前的版本,您可以添加一个枚举来表示所有的MIME类型:

如果您使用JDK7之前的版本,您可以为所有MIME类型添加一个枚举:
  public static enum MimeTypes {
      MP3, WAV, OGG
  }

  public class Stuff {
      ...
      switch (MimeTypes.valueOf(mimeType)) {
          case MP3: handleMP3(); break;
          case WAV: handleWAV(); break;
          case OGG: handleOGG(); break;
      }
  }

请看Stack Overflow问题Java - Convert String to enum,了解如何将字符串转换为枚举类型。


15
为什么不在枚举本身上放置一个处理方法?这样它就会变成 MimeTypes.valueOf(mimeType).handle() - SpaceTrucker
@SpaceTrucker:好建议,这比我的想法更简洁。实际上,这将是cowls想法的一个很好的实现,因为不需要处理具有不同实现的Map。 - pgras
4
我无法相信一条回复建议使用switch重构多个if else if,居然得到了23的赞。咕......这让我害怕想到外面有什么样的开发者。 - Daniel
@Daniel,我猜太多的开发人员认为“代码越少=越好”,即使新代码与旧代码具有相同的缺点。 - Tom
1
@Daniel,你能更明确地表达你对这种方法的担忧吗?问题中提供的用例归结为“根据输入变量执行一个且仅执行一个代码路径”,这(在我看来)是switch应该做的事情的非常好的定义。 - scubbo

15

考虑使用策略模式和 Map 来分配到适当的策略。如果您需要额外的功能,除了针对特定 mimeType 的转换之外,尤其有用;或者转换器是庞大而复杂的代码,您希望将每个转换器放置在自己的 .java 文件中。

 interface Convertor {
    void convert(File src, File target);
 }

 private static void convertWav(File src, File target) {
    ...
 }

 ...

 private static final Map< String, Convertor > convertors = new ...;
 static {
    convertors.put("audio/wav", new Convertor {
       void convert(File src, File target) {
          convertWav(src, target);
       }
    });
    convertors.put("audio/ogg", new Convertor {
       void convert(File src, File target) {
          convertOgg(src, target);
       }
    });
    ...
 }

 public void convertToMp3(File src, File target, String mimeType){
     final Convertor convertor = convertors.get(mimeType);
     if (convertor == null ) {
        ...
     } else {
        convertor.convert(src, target);
     }
 }

3
如果你对每个案例都运行相同的方法,你应该检查状态模式

我运行不同的方法。 - user962206
6
状态模式在这里不适用,因为它并没有建模有限状态机,而是根据输入类型选择不同的转换策略,因此策略模式更加合适。 - Paolo

2
这绝对是策略设计模式。但是您的总体设计存在一个大问题。使用字符串来标识类型不是一个好的编程习惯。因为它很容易被编辑,您可能会犯语法错误并花费整个下午查找编程错误。您可以避免使用map<>。
我建议采用以下方法:
1. 扩展类File。新类添加一个新属性FileType和一个新方法convertTo(FileType)到类File中。该属性保存其类型:“音频”,“wav”...再次不要使用String,使用枚举。在这种情况下,我称之为FileType。尽可能扩展File:WavFile、AudioFile等。
2. 使用策略模式创建转换器。
3. 使用工厂模式初始化转换器。
4. 由于每个文件都知道自己的类型和目标类型(使用convertTo()方法指定目标类型),它将调用工厂自动获取正确的转换器!!!
这种设计具有可扩展性,您可以添加任意数量的FileType和转换器。您投票的答案是误导性的!!!编码和黑客之间存在很大的区别。

2

0

如果您没有使用Java 7,您可以创建一个枚举并将该值与switch case一起使用。然后,您只需要传递枚举值(而不是文件,我不知道您为什么要这样做)。这看起来更整洁。

这些应该有助于您想要做的事情:

 [Java Enum Examples][1] - 
 [Java Switch Case examples][2]

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