Java进阶知识点1:白捡的扩展性 - 枚举值也是对象
一、背景
枚举经常被大家用来储存一组有限个数的候选常量。比如下面定义了一组常见数据库类型:
public enum DatabaseType {
MYSQL,
ORACLE,
SQLSERVER
}
当围绕这一组常量出现功能上的扩展点时,很多人的做法是为新的功能编写一个新类,新类中依赖该枚举类型。
比如要在界面上显示常见数据库类型的官方名称,可以用如下类实现这一功能:
public class DatabaseNameParser {
public String getDatabaseName(DatabaseType databaseType) {
if (DatabaseType.ORACLE.equals(databaseType)) {
return "Oracle数据库";
} else if (DatabaseType.MYSQL.equals(databaseType)) {
return "MySQL数据库";
} else if (DatabaseType.SQLSERVER.equals(databaseType)) {
return "SQL Server数据库";
} else {
throw new RuntimeException("不支持的数据库类型。");
}
}
public static void main(String[] args) {
System.out.println(new DatabaseNameParser().getDatabaseName(DatabaseType.MYSQL));
}
}
大量的if - else语句以及对其他类的过渡依赖(几乎每两行代码就会引用一次DatabaseType对象),让上面这段代码散发出浓浓的坏味道,你可能会想如果这些逻辑可以整合在枚举类中实现就好了。答案是当然可以。
我们只需意识到枚举值不只是常量值,枚举值也是一种对象,他几乎拥有面向对象编程的绝大部分功能 -- 封装、多态、继承(不支持,但一定程度上可以模拟类似效果)。
二、用枚举进行面向对象编程
2.1 封装
上面的例子中,数据库类型的官方名称应该封装到每个数据库类型枚举对象中,作为一个属性字段,每个枚举值提供get方法即可直接通过枚举对象获取其对应的官方名称。如下:
public enum DatabaseType {
MYSQL("MySQL数据库"),
ORACLE("Oracle数据库"),
SQLSERVER("SQL Server数据库");
private final String name;
DatabaseType(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static void main(String[] args) {
System.out.println(MYSQL.getName());
}
}
2.2 多态
上面的例子中,比如新增一个需求,要在界面上提供数据库的连接检测功能。而不同数据库类型的连接检测逻辑是不同的。此时可以为枚举对象声明一个抽象方法,每个枚举值负责实现此方法。如下:
public enum DatabaseType {
MYSQL {
@Override
public boolean detect(String ip, int port) {
//为了简化举例环境,此处不真正实现功能
return false;
}
},
ORACLE {
@Override
public boolean detect(String ip, int port) {
return false;
}
},
SQLSERVER {
@Override
public boolean detect(String ip, int port) {
return false;
}
};
public abstract boolean detect(String ip, int port);
public static void main(String[] args) {
System.out.println(MYSQL.detect("127.0.0.1", 3306));
}
}
这一特性也常被称之为:定义常量相关方法。
2.3 继承
枚举对象均是final对象,故不支持枚举对象的继承。但我们可以通过实现接口来模拟枚举类型的子类化,这也是子类化枚举类型的唯一方法。如下:
interface DatabaseType {
public boolean detect(String ip, int port);
enum RDBMS implements DatabaseType {
MYSQL {
@Override
public boolean detect(String ip, int port) {
return false;
}
},
ORACLE {
@Override
public boolean detect(String ip, int port) {
return false;
}
},
SQLSERVER {
@Override
public boolean detect(String ip, int port) {
return false;
}
}
}
enum NOSQL implements DatabaseType {
REDIS {
@Override
public boolean detect(String ip, int port) {
return false;
}
},
HBASE {
@Override
public boolean detect(String ip, int port) {
return false;
}
},
MONGODB {
@Override
public boolean detect(String ip, int port) {
return false;
}
}
}
}
这就模拟了DatabaseType派生出RDBMS和NOSQL两个抽象子类,每个子类下又派生出各自的具体实现类,这种继承结构。
三、用枚举更好地实现设计模式
当枚举值被赋予对象的强大能力后,再结合枚举类型本身的易用性,被聪明的Java开发者们发现了如下两类好用的模式:
3.1 使用枚举定义单例
—— 这是JDK5之后定义单例最好的方法,没有之一。
说起单例模式,常常让我们想起很多技巧或问题:
1、延迟初始化单例要Double Check,Double Check会失效需要使用volatile修饰符;
2、可以使用静态变量初始化单例;
3、单例对象可能可以被多次反序列化,就违背了单例模式的要求了;
... ...
现在不用纠结这些过时的套路了,就按如下方式定义单例,任何已知的问题都没有,一行代码搞定,而且引用起来更紧凑简洁。
public enum Singleton {
INSTANCE;
public void anyFunction() {
}
public static void main(String[] args) {
Singleton.INSTANCE.anyFunction();
}
}
3.2 使用枚举实现责任链或状态机
public enum StateMachine {
BEGIN {
@Override
public StateMachine handleAndReturnNextState() {
System.out.println("begin");
return PROCESS1;
}
},
PROCESS1 {
@Override
public StateMachine handleAndReturnNextState() {
System.out.println("process1");
if (true/*some condition*/) {
return PROCESS2;
}
return END;
}
},
PROCESS2 {
@Override
public StateMachine handleAndReturnNextState() {
System.out.println("process2");
return END;
}
},
END {
@Override
public StateMachine handleAndReturnNextState() {
System.out.println("end");
return null;
}
};
public abstract StateMachine handleAndReturnNextState();
public static void main(String[] args) {
StateMachine state = BEGIN;
while (state != null) {
state = state.handleAndReturnNextState();
}
}
}
四、总结
1、枚举不只是常量,也是对象,当与枚举相关的功能遇到扩展需求时,可以考虑在枚举对象上扩展功能点,以获得更加简洁紧凑的代码。
2、枚举是实现单例的最好方式。
3、枚举可以用来实现状态机。
Java进阶知识点1:白捡的扩展性 - 枚举值也是对象的更多相关文章
- Java进阶知识点: 枚举值
Java进阶知识点1:白捡的扩展性 - 枚举值也是对象 一.背景 枚举经常被大家用来储存一组有限个数的候选常量.比如下面定义了一组常见数据库类型: public enum DatabaseType ...
- 5.7w字?GitHub标星120K的Java面试知识点总结,真就物超所值了
如果你觉得在一些程序员平台获取到的资料太乱学习起来毫无头绪,但是单看<Java编程思想>相似的一类的Java圣经"枯燥无味",那我推荐你看一下这份GitHub获得过12 ...
- Java进阶知识点:协变与逆变
一.背景 要搞懂Java中的协办与逆变,不得不从继承说起,如果没有继承,协变与逆变也天然不存在了. 我们知道,在Java的世界中,存在继承机制.比如MochaCoffee类是Coffee类的派生类,那 ...
- Java进阶知识点:更优雅地关闭资源 - try-with-resource
一.背景 我们知道,在Java编程过程中,如果打开了外部资源(文件.数据库连接.网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们.因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制, ...
- Java进阶知识点3:更优雅地关闭资源 - try-with-resource及其异常抑制
一.背景 我们知道,在Java编程过程中,如果打开了外部资源(文件.数据库连接.网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们.因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制, ...
- Java进阶知识点2:看不懂的代码 - 协变与逆变
一.背景 要搞懂Java中的协办与逆变,不得不从继承说起,如果没有继承,协变与逆变也天然不存在了. 我们知道,在Java的世界中,存在继承机制.比如MochaCoffee类是Coffee类的派生类,那 ...
- Java进阶知识点:不要只会写synchronized - JDK十大并发编程组件总结
一.背景 提到Java中的并发编程,首先想到的便是使用synchronized代码块,保证代码块在并发环境下有序执行,从而避免冲突.如果涉及多线程间通信,可以再在synchronized代码块中使用w ...
- Java进阶知识点:并发容器背后的设计理念
一.背景 容器是Java编程中使用频率很高的组件,但Java默认提供的基本容器(ArrayList,HashMap等)均不是线程安全的.当容器和多线程并发编程相遇时,程序员又该何去何从呢? 通常有两种 ...
- Java进阶知识点:服务端高并发的基石 - NIO与Reactor AIO与Proactor
一.背景 要提升服务器的并发处理能力,通常有两大方向的思路. 1.系统架构层面.比如负载均衡.多级缓存.单元化部署等等. 2.单节点优化层面.比如修复代码级别的性能Bug.JVM参数调优.IO优化等等 ...
随机推荐
- Intellij IDEA 报错java.lang.NoClassDefFoundError
Intellij IDEA 报错java.lang.NoClassDefFoundError 11-Aug-2018 23:48:24.686 严重 [http-nio-8080-exec-5] or ...
- 消息中间件JMS(一)
1.JMS入门 1.1消息中间件 模块之间的依赖也称之为耦合.而耦合越多,之后的维护工作就越困难.那么如果改善系统模块调用关系.减少模块之间的耦合呢?我们接下来就介绍一种解决方案----消息中间件. ...
- SQL模糊查询,sum,AVG,MAX,min函数
cmd mysql -hlocalhost -uroot -p select * from emp where ename like '___' -- 三个横线, - 代表字符,可以查询 三个enam ...
- <head> 中的 JavaScript 函数
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- data-ng-show 指令
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- 简析--HashCode
内容转载自:http://www.cnblogs.com/szlbm/p/5806226.html 哈希表 在了解HashCode之前,我们先来认识一下哈希表; 散列表(Hash table,也叫哈希 ...
- 你不知道的javaScript笔记(6)
语法 语句表达式 句子是完整表达某个意思的一组词,由一个或多个短语组成,他们之间由标点符号或者连接词连接起来. 语句相当于句子,表达式相当于短语,运算符则相当于标点符号和连接词. JavaScript ...
- 【前行&赛时总结】◇第4站&赛时9◇ CF Round 513 Div1+Div2
◇第4站&赛时9◇ CF Round 513 Div1+Div2 第一次在CF里涨Rating QWQ 深感不易……作blog以记之 ( ̄▽ ̄)" +Codeforces 的门为你打 ...
- 爬虫——使用BeautifulSoup4的爬虫
我们以腾讯社招页面来做示例:http://hr.tencent.com/position.php?&start=0#a 如上图,使用BeautifulSoup4解析器,将图1中229页,每页1 ...
- 更新Composer依赖报错处理Fatal error: Declaration of Fxp\Composer\AssetPlugin\Repository\AbstractAssetsRe
更新Composer依赖报错处理 Fatal error: Declaration of Fxp\Composer\AssetPlugin\Repository\AbstractAssetsRe po ...