一、介绍
传统开我们只是将常量类用于作为字典功能保存一些不容易改变的状态等信息,而实际上来自于日常开发的设计,常量类的设计方式主要有四种
1、超级枚举
2、注册单例
3、字典常量
4、策略工厂
二、步骤
<超级枚举>
大部分人使用常量类的时候均用于不超过两个常量属性的保存,而实际上常量类可以理解为一个被static final修改的类,既然作为类,那么其本身是允许具有多个成员变量和行为方法的。我可以这样使用:
// 身份证映射码
@Getter
public enum MedicalInsuranceCertType {
MEDICAL_INSURANCE_CERT_TYPE_IDCARD(new String[] { "1", "6" }, "身份证","10"),
MEDICAL_INSURANCE_CERT_TYPE_PASSPORT(new String[] { "2" }, "护照", "20"),
MEDICAL_INSURANCE_CERT_TYPE_OFFICIAL(new String[] { "3", "8" }, "军官证", "30"),
MEDICAL_INSURANCE_CERT_TYPE_MAINLAND(new String[] { "4", "9", "B", "C" }, "港澳台", "40"),
MEDICAL_INSURANCE_CERT_TYPE_WORKCARD(new String[] { "25" }, "工作签证", "50"),
MEDICAL_INSURANCE_CERT_TYPE_PERMITS(new String[] { "A" }, "居留证", "60"),
MEDICAL_INSURANCE_CERT_TYPE_LIVEPERMITS(new String[] { "7" }, "长期居住证", "70"),
MEDICAL_INSURANCE_CERT_TYPE_OTHERS(new String[] {"21", "23", "24", "26", "5", "0" }, "其他", "100");
private String[] certType;
private String certTypeMsg;
private String certTypeBody;
MedicalInsuranceCertType(String[] certType, String certTypeMsg, String certTypeBody) {
this.certType = certType;
this.certTypeMsg = certTypeMsg;
this.certTypeBody = certTypeBody;
}
}
说明:在枚举类中使用多属性将不同类型的数据进行映射归并,比如图中每一个枚举对象都对应着三个属性
<注册单例>
在日常中,个人使用单例的地方并不是特别多,而实际上我们的单例对象是交给了IOC容器进行管理,IOC使用了容器进行管理。在对象被创建后保存起来,通过固定的key值进行获取,从而避免被反射暴力破坏。IOC容器中如果使用相同的class信息创建对象会获取到相同的key值,从而避免了出现多个相同实例。在我们日常的开发中,可以使用枚举来实现该模式:
Class<?> clazz = xxxxx.class;
//通过反射回去私有构造方法
Constructor constructor = clazz.getDeclaredConstructor(null);
//强制访问
constructor.setAccessible(true);
//暴力初始化,创建两个实例
Object o1 = constructor.newInstance();
Object o2 = constructor.newInstance();
注册单例 (代码来自网络)
这是一个枚举
public enum EnumSingleton{
INSTANCE;
private Object instance;
EnumSingleton(){
instance = new EnumResource();//这是一个对象
}
public Object getInstance(){
return instance;
}
}
这个测试代码
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i=0;i<10;i++){
executorService.submit(new Runnable() {
@Override public void run() {
EnumSingleton instance = EnumSingleton.INSTANCE;//通过标记来获取实例,该实例无法被反射破坏
System.out.println(Thread.currentThread().getName() + " : " + instance.getInstance());//获取到某个实例对象
}
});
}
executorService.shutdown();
}
<字典常量>
用于存储字典和匹配信息
// 身份证映射码
@Getter
public enum MedicalInsuranceCertType {
MEDICAL_INSURANCE_CERT_TYPE_IDCARD(new String[] { "1", "6" }, "身份证","10"),
MEDICAL_INSURANCE_CERT_TYPE_PASSPORT(new String[] { "2" }, "护照", "20"),
MEDICAL_INSURANCE_CERT_TYPE_OFFICIAL(new String[] { "3", "8" }, "军官证", "30"),
MEDICAL_INSURANCE_CERT_TYPE_MAINLAND(new String[] { "4", "9", "B", "C" }, "港澳台", "40"),
MEDICAL_INSURANCE_CERT_TYPE_WORKCARD(new String[] { "25" }, "工作签证", "50"),
MEDICAL_INSURANCE_CERT_TYPE_PERMITS(new String[] { "A" }, "居留证", "60"),
MEDICAL_INSURANCE_CERT_TYPE_LIVEPERMITS(new String[] { "7" }, "长期居住证", "70"),
MEDICAL_INSURANCE_CERT_TYPE_OTHERS(new String[] {"21", "23", "24", "26", "5", "0" }, "其他", "100");
private String[] certType;
private String certTypeMsg;
private String certTypeBody;
MedicalInsuranceCertType(String[] certType, String certTypeMsg, String certTypeBody) {
this.certType = certType;
this.certTypeMsg = certTypeMsg;
this.certTypeBody = certTypeBody;
}
public static String getCertTypeBody(String certTypeBody) {
for (MedicalInsuranceCertType mict : MedicalInsuranceCertType.values()) {
if (ArrayUtils.contains(mict.certType, certTypeBody)) {
return mict.getCertTypeBody();
}
}
return "100";
}
}
说明:通过静态化放进行匹配从而获取到对应映射码,这是在做第三方接口经常用到的方式
<策略工厂>
对于一个业务的某个功能,具有多种可选择的方法和类型(用户可自选),这种情况下通过枚举工厂的形式将多个类型给统一管理起来,通过将工厂开放出去从而统一对外的管理。(代码来自网络)
public class PayStrategyFactory {
// 支付方式常量
public static final String ALI_PAY = "aliPay";
public static final String JD_PAY = "jdPay";
public static final String WECHAT_PAY = "wechatPay";
public static final String UNION_PAY = "unionPay";
// 支付方式管理集合
private static Map<String, Payment> PAYMENT_STRATEGY_MAP = new HashMap<>();
static {
PAYMENT_STRATEGY_MAP.put(ALI_PAY, new AliPay());
PAYMENT_STRATEGY_MAP.put(JD_PAY, new JdPay());
PAYMENT_STRATEGY_MAP.put(WECHAT_PAY, new WeChatPay());
PAYMENT_STRATEGY_MAP.put(UNION_PAY, new UnionPay());
}
/**
* 获取支付方式类
*
* @param payType 前端传入支付方式
* @return
*/
public static Payment getPayment(String payType) {
Payment payment = PAYMENT_STRATEGY_MAP.get(payType);
if (payment == null) {
throw new NullPointerException("支付方式选择错误!");
}
return payment;
}
}
订单这一工具天然具备被支付的行为,可以将支付的类型裹挟在订单里面
public class NewOrder {
// 订单id
private String orderId;
// 金额
private long amount;
public NewOrder(String orderId, long amount) {
this.orderId = orderId;
this.amount = amount;
}
/**
* 订单支付方法
*
* @return
*/
public boolean pay(String payType) {
boolean paySuccess;
Payment payment = PayStrategyFactory.getPayment(payType);
// 调用支付接口
paySuccess = payment.pay(orderId, amount);
if (!paySuccess) {
// 支付失败逻辑
System.out.println("支付失败!");
}
return paySuccess;
}
}
测试案例
public static void main(String[] args) {
// 前端传入的参数
String orderId = "01000005";
String payType = PayStrategyFactory.ALI_PAY;
long amount = 190;
// 创建策略上下文(订单),并将具体的策略实现注入
NewOrder order = new NewOrder(orderId, amount);
// 实际情况是 在支付的时候选择支付方式,因为有可能先提交了订单,后面再付款
order.pay(payType);//付款是动态选择的
}
三、总结
在开发中,为了方便经常是会将字典常量和超级枚举和策略工厂的方式结合在一起使用。但具体应用还是好区分不同的场景,不能一次性全部用上,也没意义。
有问题欢迎上报和分享!