user1884155
user1884155

Reputation: 3736

Java domain model: categories with specific subcategories

I have a class Product. Each product can have any amount of Categories:

public class Product
{
    private List<Category> categories

    ...
}

Each Category has its own unique set of subcategories. For example:

For each set of subcategories, I created an enum with possible static values:

public enum RedSubCategory
{
    MAROON, ORANGE, BORDEAUX;
}

public enum BlueSubCategory
{
    LIGHT, DARK, AZURE;
}

I'm now struggling with creating the Category class. Because each category is well defined, I thought of making it an enum. However, each enum value should have a list of subcategories of a different sub-type and this won't work:

public enum Category
{
    RED, BLUE;

    private List<SubCategory> subCategories;

    // This won't work! SubCategory is not a real class and the 
    // SubCategory enums can't extend from a common supertype!
    public setSubCategories(List<SubCategory> subCategories)
    {
        this.subCategories : subCategories; 
    }
}

I run into the same problem if I make Category a class instead of an enum. I can solve this using generics, but I lose the static definition of all possible categories. Additionally, how do I make it so the category RED is the only category that can use RedSubCategories? By using a class with generics, nothing would stop me from making a category object with category "blue" and red subcategories!

Are there any patters or inheritance/generics tricks I can use here?

Upvotes: 3

Views: 1978

Answers (1)

atao
atao

Reputation: 845

May be using java interface:



    package basic;

    import static basic.SubCatMain.RedSubCategory.*;
    import static basic.SubCatMain.BlueSubCategory.*;

    public class SubCatMain {

        public interface Category {
        }

        public enum MainCategory implements Category {
            RED(MAROON, ORANGE, BORDEAUX), 
            BLUE(LIGHT, DARK, AZURE);

            final private Category[] subcategories;

            private MainCategory(Category... subcategories) {
                this.subcategories = subcategories;
            }

            public Category[] getSubcategories() {
                return subcategories;
            }
        }

        public enum RedSubCategory implements Category {
            MAROON, ORANGE, BORDEAUX;
        }

        public enum BlueSubCategory implements Category {
            LIGHT, DARK, AZURE;
        }

        public static void main(String[] args) {
            // do something with your categories
        }

    }

EDIT

@user1884155: about "I want to ensure a product cannot have a red category but a list of blue subcategories".

I don't think you can do that using only casting at compile time. The compiler can't know which main category will be provided to the Product ctor. Coding some business rule will be required, something like:

 

    public static class Product {
        final private MainCategory category;
        private Category[] categories;

        public Product(MainCategory category) {
            this.category = category;
        }

        protected void checkCategoriesAreValid(Category... categories) {
            // throw an exception if at least one of the categories 
            // is not valid in respect of some business rules
            // by default any sub-category of a main one is ok
            List refs = Arrays.asList(category.getSubcategories());
            for(Category c:categories) {
                if (!refs.contains(c)) {
                    throw new IllegalArgumentException("...");
                }
            }
        }

        public Category[] getCategories() {
            return categories;
        }

        public void setCategories(Category... categories) {
            checkCategoriesAreValid(categories);
            this.categories = categories;
        }

    }

Upvotes: 1

Related Questions