天天看點

Java特性組合的通用方案

一、背景

一些架構的特性組合,以及開發中業務的某個字段是多個特征組合,如果直接用數字,組合較多儲存非常複雜。

這裡提供一個參考工具類, 大家感興趣可以參考改造。

二、源碼

特征

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...。

這樣不同的枚舉通過左移進行區分,不同的特征組合通過 不同特征之間的&運算即可區分。

如特征1 + 特征4   則,兩個特征的掩碼 按位或即可。

如果取消某個特性,總特性值&=~某個性,讓某個特性位置為0即可。

怎麼證明隐式繼承了那個類呢?

方法1:單元測試

@Test
public void testExtends(){
    Assert.assertTrue(DemoEnum.FIRST instanceof Enum);
}           

方法2:源碼注釋法

Enum類的注釋第一句話就是:

This is the common base class of all Java language enumeration types.

這是Java語言中枚舉類型的基類。

方法3:反解析

随手寫一個枚舉類

public enum SomeEnum {

}

javac SomeEnum.java

javap -verbose  SomeEnum.class

Compiled from "SomeEnum.java"

public final class com.chujianyun.common.enums.SomeEnum extends java.lang.Enum<com.chujianyun.common.enums.SomeEnum>

...

很明顯可以看出繼承自java.lang.Enum類。

另外我們還有意外發現,該類自動加上了final修飾符,是以無法再繼承。

四、總結

由于枚舉隐式繼承了Enum類,由于Java不支援多ji'cheng是以如果想實作統一的方法,隻能通過實作接口方式。

位運算的恰當使用可以将一些複雜邏輯簡單化,可以非常容易得應對變化。

我們學習的時候,寫代碼的時候多去源碼裡看看,會有一些意外收獲。

有時間研究下Java反解析class檔案去讀讀位元組碼可以收獲更多。

創作不易,如果覺得本文對你有幫助,歡迎點贊,歡迎關注我,如果有補充歡迎評論交流,我将努力創作更多更好的文章。