Improving Design for structured Keys On several occasions I had to use a fixed set of keys (constants in the sense of static final String) with some additional logic (grouping in different keySets). After some more or less lefthanded approaches with static final String constants, I learned the advantages of enum. Since everything is known from the start I tend to use enum. But I don't see how to make this approach reusable for different sets of keys (avoid copy paste). Here is the pattern I found unseful so far. Hope you have some ideas how to improve.

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;

public class TestKeys
{
    public enum Groups
    {
        BASIC, 
        BROAD(BASIC), // Keys defined in group BASIC are also part of group BROAD
        SEPARATE;

        private Collection&lt;Groups&gt; m_subGroups = new HashSet&lt;Groups&gt;();
        private Collection&lt;String&gt; m_keySet = new HashSet&lt;String&gt;();

        private Groups() 
        {
            m_subGroups.add(this);
        }

        private Groups(Groups... groups) 
        {
            this();
            for (Groups group : groups)
            {
                m_subGroups.add(group);
            }
        }

        public boolean contains(String keyString)
        {
            return m_keySet.contains(keyString);
        }

        public boolean contains(Keys key)
        {
            return m_keySet.contains(key.toString());
        }

        public boolean containsIgnoreCase(String keyString)
        {
            for (String key : m_keySet)
            {
                if (key.equalsIgnoreCase(keyString)) return true;
            }
            return false;
        }
    }

    public enum Keys {
        KEY_BASE1("Base 1", Groups.BASIC),
        KEY_BASE2("Base 2", Groups.BASIC),
        KEY_BROAD1("Broad 1", Groups.BROAD),
        KEY_BROAD2("Broad 2", Groups.BROAD),
        KEY_DIFF1("Different 1", Groups.SEPARATE)
        ;

        private String m_str;
        private Groups m_group;

        Keys(String str, Groups group) 
        { 
            m_str = str;
            m_group = group;
            if (group == null)
            {
                throw new AssertionError("group mustn't be null");
            }
        }

        public Groups getGroup()
        {
            return m_group;
        }

        @Override
        public String toString()
        {
            return m_str;
        }

        public boolean equals(String string)
        {
            if (toString().equals(string)) return true;
            return false;
        }

        public Keys getKeyIgnoreCase(String keyString)
        {
            for (Keys key : Keys.values())
            {
                if (key.toString().equalsIgnoreCase(keyString)) return key;
            }
            return null;
        }
    }

    static
    {
        fillKeySets();
        integrityCheck();
    }


    public static void integrityCheck() throws AssertionError
    {
        for (Groups bereich : Groups.values())
        {
            checkMultipleKeys(bereich);
        }
    }

    public static void checkMultipleKeys(Groups bereich)
            throws AssertionError
    {
        HashSet&lt;String&gt; set = new HashSet&lt;String&gt;();
        for (Keys key : Keys.values())
        {
            if (set.contains(key.toString()))
            {
                throw new AssertionError("Multiple Key \"" + key + "\".");
            }
            set.add(key.toString());
        }
    }

    public static void fillKeySets()
    {
        for (Groups bereich : Groups.values())
        {
            for (Keys key : Keys.values())
            {
                if (bereich.m_subGroups.contains(key.m_group))
                {
                    bereich.m_keySet.add(key.toString());
                }
            }
            bereich.m_keySet = Collections.unmodifiableCollection(bereich.m_keySet);
        }
    }

    public static Collection&lt;String&gt; keySet(Groups group)
    {       
        return group.m_keySet;
    }
}
My initial thought is that you don't :)

The whole idea behind the enum approach starts from your premise:


  Since everything is known from the start I tend to use enum


Which means you're dealing with a specific set with it's own rules and checks that you put into that enum.

If you were to start from the idea of reusing code to represent different kinds of sets you wouldn't go for enums (or static finals for that matter).
That requires a completetly different design.

In this case I'll point out Martin Fowler's rule of three.

More specifically, you should only consider refactoring this for reusability if you actually want to reuse it in the first place.

Also: copy-pasting some code isn't a deadly sin. It's only a bad smell, that you have to decide to remove or not.