Java 中 Switch Case 的替代方案
Posted
技术标签:
【中文标题】Java 中 Switch Case 的替代方案【英文标题】:Alternative to Switch Case in Java 【发布时间】:2010-11-28 09:07:49 【问题描述】:除了看起来不太好的 if else 之外,还有其他方法可以在 Java 中实现 switch case。一组值会组合在一起,根据选择需要执行相应的方法。
【问题讨论】:
当您说“组合中将包含一组值”时,您是什么意思? 已经问过类似问题***.com/questions/126409/… @cdb:那你为什么又问了?以前的答案没有告诉你什么? 【参考方案1】:如果您的代码周围有大量 switch/case 语句,它们会让您发疯。
您可以选择重构:Replace conditional with polymorphism.
假设您有一个用于将信息保存到不同设备的软件:定义了 4 个持久性操作:获取、保存、删除、更新,其中可以通过N种持久化机制(平面文件、网络、RDBMS、XML等)来实现。
你的代码必须支持它们,所以在 4 个不同的地方你有这个:
之前
class YourProblematicClass
....
public void fetchData( Object criteria )
switch ( this.persitanceType )
case FilePersistance:
// open file
// read it
// find the criteria
// build the data
// close it.
break;
case NetWorkPersistance:
// Connect to the server
// Authenticate
// retrieve the data
// build the data
// close connection
break;
case DataBasePersistace:
// Get a jdbc connection
// create the query
// execute the query
// fetch and build data
// close connection
break;
return data;
保存/删除/更新也一样
public void saveData( Object data)
switch ( this.persitanceType )
case FilePersistance:
// open file, go to EOF, write etc.
break;
case NetWorkPersistance:
// Connect to the server
// Authenticate
// etc
break;
case DataBasePersistace:
// Get a jdbc connection, query, execute...
break;
等等……
public void deleteData( Object data)
switch ( this.persitanceType )
case FilePersistance:
break;
case NetWorkPersistance:
break;
case DataBasePersistace:
break;
public void updateData( Object data)
switch ( this.persitanceType )
case FilePersistance:
break;
case NetWorkPersistance:
break;
case DataBasePersistace:
break;
使用 switch/case 语句会出现问题:
每次您想添加新类型时,您都必须在每个部分中插入新的开关/案例。
很多时候,有些类型是相似的,它们不需要不同的 switch/case(你可以级联它们)
它们是其他一些,有时它们略有不同
你甚至可能需要在运行时加载不同的类型(比如插件)
因此这里的重构将是添加一个接口或抽象类型,并让不同的类型实现该接口并将责任委托给该对象。
所以你会有这样的东西:
之后
public interface PersistenceManager
public void fetchData( Object criteria );
public void saveData( Object toSave );
public void deleteData( Object toDelete );
public void updateData( Object toUpdate );
以及不同的实现
public class FilePersistence implements PersistanceManager
public void fetchData( Object criteria )
// open file
// read it
// find the criteria
// build the data
// close it.
public void saveData( Object toSave )
// open file, go to EOF etc.
public void deleteData( Object toDelete )
....
public void updateData( Object toUpdate )
....
其他类型会根据它们的逻辑来实现。网络将处理套接字和流,DB 将处理 JDBC、ResultSets 等。XML 与节点等。
public class NetworkPersistence implements PersistanceManager
public void fetchData( Object criteria )
// Socket stuff
public void saveData( Object toSave )
// Socket stuff
public void deleteData( Object toDelete )
// Socket stuff
public void updateData( Object toUpdate )
// Socket stuff
public class DataBasePersistence implements PersistanceManager
public void fetchData( Object criteria )
// JDBC stuff
public void saveData( Object toSave )
// JDBC stuff
public void deleteData( Object toDelete )
// JDBC stuff
public void updateData( Object toUpdate )
// JDBC stuff
最后你只需要委托调用。
稍后:
public YouProblematicClass // not longer that problematic
PersistamceManager persistance = // initialize with the right one.
public void fetchData( Object criteria )
// remove the switch and replace it with:
this.persistance.fetchData( criteria );
public void saveData( Object toSave )
// switch removed
this.persistance.saveData( toSave );
public void deleteData( Object toDelete )
this.persistance.deleteData( toDelete );
public void updateData( Object toUpdate )
this.persistance.updateData( toUpdate );
因此,您只需根据类型为持久性管理器创建一次正确的实例即可。然后所有的调用都由多态性解决。这是面向对象技术的关键特性之一。
如果您决定需要另一个持久性管理器,您只需创建新的实现并分配给该类。
public WavePersistance implements PersistanceManager
public void fetchData( Object criteria )
// ....
public void saveData( Object toSave )
// ....
public void deleteData( Object toDelete )
// ....
public void updateData( Object toUpdate )
// ....
【讨论】:
【参考方案2】:大概您正在努力满足案例保持不变的要求。通常这是一种代码气味,但您可以做一些事情。您可能想提出并链接到另一个详细说明您尝试转换的问题的问题。
Map<String,Object> map = new HasMap<String,Object>();
// ... insert stuff into map
// eg: map.add("something", new MyObject());
String key = "something";
if (map.contains(key))
Object o = map.get(key);
在上面的示例中,您可能希望映射到“处理程序”,例如
interface Handler
public void doSomething();
然后这一切都变成了查找。
if (map.contains(key)) map.get(key).doSomething();
再次,它有点味道,所以请发布一个说明推理的问题。
【讨论】:
如果恒定性很痛苦,您也可以使用枚举。 正在寻找类似的东西。 为什么包含然后获取,您从地图中读取两次。命令注释 = map.get(key); if( null != command ) ...command.execute(); 根据实施情况,包含可能不会太贵。知道是否有条目还可以让您以合理可读的方式处理“默认”操作。过早的优化等等。这实际上可能是我在 Perl 中经常这样做以避免自动恢复的结果...... 为了让枚举更进一步,您可以使用处理方法设置每个枚举常量,使其成为 value.doSomthing()。【参考方案3】:重构代码以使用多态性可以消除对 switch 语句的需要。但是,switch 有一些合法用途,因此取决于您的情况。
【讨论】:
是的-这是一个众所周知的重构-refactoring.com/catalog/replaceConditionalWithPolymorphism.html 不错的链接。看起来目录里有很多不错的。【参考方案4】:一系列丑陋的if,else if,else
?
【讨论】:
【参考方案5】:或者可以想象一种动态开关盒:
public interface Task<T>
public void doSomething(T context);
public Class SwitchCase<T>
Map<Integer,Task<T>> tasks;
Task<T> defaultTask;
public void choose(int choice, T context)
Task<T> t= this.tasks.get(choice);
if(t!=null) t.doSomething(context); return;
if(defaultTask!=null) defaultTask.doSomething(context);
【讨论】:
【参考方案6】:我猜“清洁代码”根据 switch/case 与 if/else 有一个不错的章节。
另外:我认为决定是否可以通过使用 switch case、多态甚至是一个好的 if/else 来减少“噪音”并使代码更清晰是有意义的。我猜,案件的数量在这里起主要作用。
【讨论】:
【参考方案7】:我发布了一个典型案例,我如何用枚举替换 switch case。
在重构之前我有枚举 PatternTypes
:
public enum PatternTypes
ALPHA_CHAR, ALPHANUMERIC_CHAR, ADDITIONAL_CHAR, UNICODE_BMP_CHARS
和功能:
private static final String ALPHA_CHAR = "[a-zA-Z]+";
private static final String ALPHANUMERIC_CHAR = "[a-zA-Z0-9\\_]+";
private static final String ADDITIONAL_CHAR = "[a-zA-Z0-9\\_\\-\\,\\.\\s\\!\\#\\$\\&\\(\\)\\*\\+\\;\\:\\=\\?\\@\\|\\[\\]\\\\\\~]+";
private static final String UNICODE_BMP_CHARS = "[a-zA-Z0-9\\_\\-\\,\\.\\s\\!\\#\\$\\&\\(\\)\\*\\+\\;\\:\\=\\?\\@\\|\\[\\]\\\\\\~\u00A0-\uD7FF\uF900-\uFFFD]+";
/*
* Match given classAbbr with given RegEx pattern
*/
private void checkInvalidClassAbbr(String classAbbr,
PatternTypes classAbbrPattern)
switch (classAbbrPattern)
case ALPHA_CHAR:
checkUnmatched(classAbbr, ALPHA_CHAR, CLASS_ABBR_VAR_NAME);
break;
case ALPHANUMERIC_CHAR:
checkUnmatched(classAbbr, ALPHANUMERIC_CHAR, CLASS_ABBR_VAR_NAME);
break;
case ADDITIONAL_CHAR:
throw new MalFormedDNException("Not support Pattern Type:"
+ classAbbrPattern);
case UNICODE_BMP_CHARS:
throw new MalFormedDNException("Not support Pattern Type:"
+ classAbbrPattern);
重构后PatternTypes
修改为:
public enum PatternTypes
/**
* RegEx patterns divided by restriction level
*/
ALPHA_CHAR("[a-zA-Z]+"),
ALPHANUMERIC_CHAR("[a-zA-Z0-9\\_]+"),
ADDITIONAL_CHAR("[a-zA-Z0-9\\_\\-\\,\\.\\s\\!\\#\\$\\&\\(\\)\\*\\+\\;\\:\\=\\?\\@\\|\\[\\]\\\\\\~]+"),
UNICODE_BMP_CHARS("[a-zA-Z0-9\\_\\-\\,\\.\\s\\!\\#\\$\\&\\(\\)\\*\\+\\;\\:\\=\\?\\@\\|\\[\\]\\\\\\~\u00A0-\uD7FF\uF900-\uFFFD]+");
public String getPatternContent()
return patternContent;
private String patternContent;
PatternTypes(String patternContent)
this.patternContent = patternContent;
函数简化为:
/*
* Match given classAbbr with given RegEx pattern
*/
private void checkInvalidClassAbbr(String classAbbr, PatternTypes classAbbrPattern)
if (PatternTypes.ADDITIONAL_CHAR.equals(classAbbrPattern) || PatternTypes.UNICODE_BMP_CHARS.equals(classAbbrPattern))
throw new MalFormedDNException("RegEx pattern:" + classAbbrPattern.name() + "is not allowed for Class Abbr");
checkUnmatched(classAbbr, classAbbrPattern.getPatternContent(), CLASS_ABBR_VAR_NAME);
【讨论】:
我喜欢它,但这不是用大量的 IF 替换 select 吗?新代码的味道不是 IF 吗?【参考方案8】:Hashmap 被认为对内存不友好,因此您可以为此目的使用 Enum。
例子:
class EnumExample4
enum Season
WINTER(5), SPRING(10), SUMMER(15), FALL(20);
private int value;
private Season(int value)
this.value=value;
public static void main(String args[])
System.out.println(Season.WINTER.value); //This gives you 5
这将使您免于编写 Switch Case 或 if 语句。
【讨论】:
您能否提供一些参考来支持 Hashmap 被认为对内存不友好的说法?如果可以,请编辑您的答案以将其链接到此处。【参考方案9】:对于 switch 语句的替代,我认为最好的解决方案是使用 枚举。例如:考虑以下情况:-
public enum EnumExample
OPTION1
public double execute()
Log.info(CLASS_NAME, "execute", "The is the first option.");
return void;
,
OPTION2
public double execute()
Log.info(CLASS_NAME, "execute", "The is the second option.");
return void;
,
OPTION3
public double execute()
Log.info(CLASS_NAME, "execute", "The is the third option.");
return void;
;
public static final String CLASS_NAME = Indicator.class.getName();
public abstract void execute();
上面的枚举可以按以下方式使用:
EnumExample.OPTION1.execute();
希望这对你们有帮助。
【讨论】:
【参考方案10】:你想做什么?为什么 Switch-Case 不够好?
快速的答案是:使用 if-else
【讨论】:
【参考方案11】: if ()
else if ()
...
else if ()
?
但我不会说它更好......
【讨论】:
【参考方案12】:if
(连同else if
和else
)声明怎么样? switch
只允许您使用相等来切换整数或枚举类型,if
允许您使用任何布尔逻辑。
【讨论】:
char、byte、short也可以用在switch中。 是的,但它们也可以安全(自动)转换为 int【参考方案13】:您总是可以用if-else if-else if-else if...
替换开关,但我不明白您为什么要这样做。根据上下文,switch
s 有时也可以替换为数组或哈希图。
【讨论】:
【参考方案14】:如果字符串是静态的,您可以创建一个 ENUM。 并打开它。
【讨论】:
以上是关于Java 中 Switch Case 的替代方案的主要内容,如果未能解决你的问题,请参考以下文章
有啥完美的方法替代java中的 if-else,switch-case
有啥完美的方法替代java中的 if-else,switch-case
寻找 elif 和 if 的良好替代品,例如 switch case [重复]