继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

枚举特性组合的通用方案

明明如月
关注TA
已关注
手记 59
粉丝 3857
获赞 1465

一、背景

中间件的一些特征组合或者业务的某个字段是多个特征组合,如果直接用数字,组合较多保存非常复杂。

这里提供一个粗略的DEMO, 大家感兴趣可以参考改造。


二、源码

特征

public interface Feature {

    /**
     * 获取特性(掩码)
     */
    int getMask();

    /**
     * 所有特性
     */
    Feature[] listAll();
}

特征工具类

public class FeatureUtil {
    /**
     * 此特性是否开启
     *
     * @param features 特性值
     * @param feature  某个特性
     * @return 是否开启
     */
    public static boolean isEnabled(int features, Feature feature) {
        return (features & feature.getMask()) != 0;
    }

    /**
     * 配置某个特性
     *
     * @param features 特性值
     * @param feature  某个特性
     * @param state    是否开启
     * @return
     */
    public static int config(int features, Feature feature, boolean state) {
        if (state) {
            features |= feature.getMask();
        } else {
            features &= ~feature.getMask();
        }

        return features;
    }

    /**
     * 开启某些特性的值
     *
     * @param features 特性数组
     * @return 特性值
     */
    public static int of(Feature... features) {
        if (features == null) {
            return 0;
        }
        return of(Stream.of(features).collect(Collectors.toSet()));
    }

    /**
     * 开启某些特性的值
     *
     * @param features 特性数组
     * @return 特性值
     */
    public static int of(Set<Feature> features) {
        if (features == null) {
            return 0;
        }

        int value = 0;

        for (Feature feature : features) {
            value |= feature.getMask();
        }

        return value;
    }

    /**
     * 判断特性值包含哪些特性
     *
     * @param features     特性值
     * @param featureArray 特性数组
     * @return 包含的特性
     */
    public static Set<Feature> resolve(Integer features, Feature[] featureArray) {
        if (featureArray == null) {
            throw new IllegalArgumentException("特征数组不为null");
        }
        if (features == null || features == 0) {
            return new HashSet<>(0);
        }

        Set<Feature> featureSet = new HashSet<>();
        for (Feature feature : featureArray) {
            if ((features & feature.getMask()) != 0) {
                featureSet.add(feature);
            }
        }
        return featureSet;
    }


}

枚举举例

public enum DemoEnum implements Feature {

    FIRST("第一个"),
    SECOND("第二个"),
    THIRD("第三个");


    DemoEnum() {
        mask = (1 << ordinal());
    }

    DemoEnum(String desc) {
        mask = (1 << ordinal());
        this.desc = desc;
    }

    private final int mask;


    private String desc;

    @Override
    public final int getMask() {
        return mask;
    }

    @Override
    public Feature[] listAll() {
        return DemoEnum.values();
    }

    public String getDesc() {
        return desc;
    }
}

测试类


public class FeatureUtilTest {
    private int features;

    @Before
    public void init() {
        features = FeatureUtil.of(DemoEnum.values());
    }

    @Test
    public void isEnabled() {
        boolean enabledFirst = FeatureUtil.isEnabled(features, DemoEnum.FIRST);
        boolean enabledSecond = FeatureUtil.isEnabled(features, DemoEnum.SECOND);
        Assert.assertTrue(enabledFirst);
        Assert.assertTrue(enabledSecond);
    }

    @Test
    public void config() {
        int defaultFeature = 0;
        int enableFirst = FeatureUtil.config(defaultFeature, DemoEnum.FIRST, true);
        int enableFirst2 = FeatureUtil.of(DemoEnum.FIRST);
        Assert.assertEquals(enableFirst, enableFirst2);
    }

    @Test
    public void of() {
        // 前两个特性
        int two = FeatureUtil.of(DemoEnum.FIRST, DemoEnum.SECOND);
        Assert.assertEquals(two, 3);

        // 后两个特性
        int lastTwo = FeatureUtil.of(DemoEnum.THIRD, DemoEnum.SECOND);
        Assert.assertEquals(lastTwo, 6);

    }

    @Test
    public void resolve() {
        int features = FeatureUtil.of(DemoEnum.FIRST, DemoEnum.THIRD);
        Set<Feature> resolve = FeatureUtil.resolve(features, DemoEnum.values());

        Assert.assertEquals(resolve.size(), 2);
        Assert.assertTrue(resolve.contains(DemoEnum.FIRST));
        Assert.assertTrue(resolve.contains(DemoEnum.THIRD));

    }
}

三、解析

java.lang.Enum#ordinal 函数继承自枚举对象,获取该枚举值在枚举类中的排序,第一个为0,往后依次是1,2...。

https://img4.mukewang.com/5d308b2700012b1c09600388.jpg

这样不同的枚举通过左移进行区分,不同的特征组合通过 不同特征之间的按位与运算即可自由组合。

如特征1 + 特征4   则,两个特征的掩码 按位或即可。

https://img1.mukewang.com/5d308b1d0001630308060496.jpg

如果取消某个特性,总特性值&=~某个性,让某个特性位置为0即可。


四、总结

由于枚举隐式继承了Enum类,因此如果想实现统一的方法,只能通过实现接口方式。

位运算的恰当使用可以将一些复杂逻辑简单化,可以非常容易得应对变化。

我们学习的时候,写代码的时候多去源码里看看,会有一些意外收获。


创作不易,如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。


打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP

热门评论

高级

查看全部评论