Workaround implementation for "enum inheritance" in Java? - java

Preface: I've already researched why "enum inheritance" is illegal in Java.
My problem is the following: given a class Recipe, I want its property category to be a list of constant values like APPETIZER, BREAKFAST, DESSERT, MAIN_COURSE, SOUP - I logically use enums for this.
My question then is: if I wanted each of this enums to have "children of their own" (for example: SWEET and SAVORY for BREAKFAST, or CAKE, MUFFIN and BISCUITS for DESSERT), so that:
Specifying the subcategory ("child enum") is mandatory (e.g. myRecipe.setCategory(DESSERT) should raise an exception);
Using a "child enum" from a different "family" is forbidden (e.g. SOUP.BISCUITS should raise an exception);
I should be able to access the "child enum" through dot notation (e.g. myRecipe.setCategory(DESSERT.CAKE)) - or other similar "lightweight" syntax.
I haven't been able to come up with any clean solution in Java to fit all three requisites.
Is there any "esoteric" design pattern for this?
How would you implement this?

You can do this:
class Recipe {
private final Meal meal;
private final MealCategory category;
public <T extends Meal> Recipe(T meal, MealCategory<T> category) {
this.meal = meal;
this.category = category;
}
}
abstract class Meal {}
class Breakfast extends Meal {}
class Dinner extends Meal {}
class MealCategory<T extends Meal> {}
class Cereal extends MealCategory<Breakfast> {}
class Meat extends MealCategory<Dinner> {}
public class Test {
public static void main(String[] args) {
Recipe r = new Recipe(new Breakfast(), new Cereal());
Recipe r2 = new Recipe(new Breakfast(), new Meat()); // compile time error
}
}

Simple Design
Create a class Category. Inside Category, declare all the enum classes.
public class Category
{
public enum APPETIZER
{
}
public enum BREAKFAST
{
SWEET,
SAVORY
}
public enum DESSERT
{
CAKE,
MUFFIN,
BISCUITS
}
public enum MAIN_COURSE
{
}
}
Inside the Recipe class, category should be of type DESSERT. I have static imported Category class.
public class Recipe
{
DESSERT category;
public void setCategory(DESSERT category)
{
this.category = category;
}
public static void main(String[] args)
{
Recipe myRecipe = new Recipe();
myRecipe.setCategory(DESSERT.BISCUITS);
// statements below give compile time errors
// myRecipe.setCategory(DESSERT);
// myRecipe.setCategory(BREAKFAST.SWEET);
}
}
Improvement
Convert Category into a marker interface. All the categories such as DESSERT, BREAKFAST, etc. should implement Category.
interface Category {}
enum APPETIZER implements Category
{
}
enum BREAKFAST implements Category
{
SWEET,
SAVORY
}
enum DESSERT implements Category
{
CAKE,
MUFFIN,
BISCUITS
}
enum MAIN_COURSE implements Category
{
}
Make Recipe generic.
public class Recipe <T extends Category>
{
T category;
public void setCategory(T category)
{
this.category = category;
}
public static void main(String[] args)
{
Recipe<DESSERT> myRecipe = new Recipe<>();
myRecipe.setCategory(DESSERT.BISCUITS);
// statements below give compile time errors
// myRecipe.setCategory(DESSERT);
// myRecipe.setCategory(BREAKFAST.SWEET);
}
}
These are not design patterns. They are self implementation.

Related

How to pass multiple Types that implement the same interface?

Firstly apologies about the not so great title, I am new to Java and wasn't sure how to title this.
I have a interface class "TestInterface":
ublic interface TestInterface {
String getForename();
void setForename(String forename);
String getSurname();
void setSurname(String surname);
}
"TestImpl" implements "TestInterface":
public class TestImpl implements TestInterface{
private String forename;
private String surname;
#Override
public String getForename() {
return forename;
}
public void setForename(String forename) {
this.forename = forename;
}
#Override
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
}
Then I have a call called "ExtendTest" which extends "TestImpl":
public class ExtendTest extends TestImpl{
private String firstLineAddress;
public String getFirstLineAddress() {
return firstLineAddress;
}
public void setFirstLineAddress(String firstLineAddress) {
this.firstLineAddress = firstLineAddress;
}
}
I then have this "Entity" class:
import java.util.List;
public class Entity {
private List<TestInterface> testInterfaces;
private List<ExtendTest> extendTests;
public List<TestInterface> getTestInterfaces() {
return testInterfaces;
}
public void setTestInterfaces(List<TestInterface> testInterfaces) {
this.testInterfaces = testInterfaces;
}
public List<ExtendTest> getExtendTests() {
return extendTests;
}
public void setExtendTests(List<ExtendTest> extendTests) {
this.extendTests = extendTests;
}
}
and finally this "DoStuff" class where the dostuff method accepts a parameter of type List
import java.util.List;
public class DoStuff {
public void doStuff(List<TestInterface> testData) {
}
}
I try to test this like so:
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
DoStuff doStuff = new DoStuff();
Entity entity = new Entity();
// Works
doStuff.doStuff(entity.getTestInterfaces());
// Does not work
doStuff.doStuff(entity.getExtendTests());
}
}
However where the comment is "Does not work" their is an error
Required type:
List<TestInterface>
Provided:
List<ExtendTest>
My question is how do I make it so that I can pass it in. My understanding was that becase they all implement TestInterface that it would work but I think I am wrong with this.
Thanks for any help and learnings here :)
You've run afoul of PECS. I recommend reading the linked answer for a more detailed explanation, but here's the bits specific to your use case.
When you have a generic type (List, in your case), if you only read from it, you should write List<? extends MyInterface>. If you only write to it, you should write List<? super MyInterface>. If you do both, then you want List<MyInterface>. Why do we do this? Well, look at your code.
public void doStuff(List<TestInterface> testData) { ... }
This function takes a List<TestInterface>. The List interface has a ton of capability. You can add and remove things to it in addition to just reading from it. And doStuff expects a list of TestInterface. So it's entirely fair game for the implementation of doStuff to do
testData.add(new ClassIJustMadeUp());
assuming ClassIJustMadeUp implements TestInterface. So we definitely can't pass this function a List<ExtendTest>, since that list type can't contain ClassIJustMadeUp.
However, if your function does only read from the list and isn't planning to add anything to it, you can write the signature as
public void doStuff(List<? extends TestInterface> testData) { ... }
and now you can pass a List of any type which extends TestInterface. It's fine to read from this list, since any type which extends TestInterface clearly can be upcast safely to TestInterface. But if we try to add a list element, that's a compiler error since the list doesn't necessarily support that particular type.

Java Generics - Choosing the correct generic service

Lets say we have a product of different types, the different types have different internal variables and does not share anything beside the fact they are all products.
public interface Product {}
public class ProductA implements Product {
private String productAVariable;
// getters
}
public class ProductB implements Product {
private String productBVariable;
// getters
}
To get the quote/price of a product we must call an external service:
public interface QuoteService<T extends Product> {
ProductPrice getQuote(T product);
}
public class ProductAQuoteService implements QuoteService<ProductA> {
#Override
public ProductPrice getQuote(ProductA product) {
return new ProductPrice(someExternalService.getQuote(product.getProductAVariable()));
}
}
public class ProductBQuoteService implements QuoteService<ProductB> {
#Override
public ProductPrice getQuote(ProductB product) {
return new ProductPrice(someOtherExternalService.getQuote(product.getProductBVariable()));
}
}
So far so good. The last step is to iterate through a List of Products in our shoppingcart. Fetching the price/quote for each one. But how can we do that in a generic way? Currently i have some if-else instance of statements, but I feel there must be something more generic?
List<Product> productsInCart = Lists.asList(new ProductA(), new ProductB());
for (Product product : productsInCart) {
if (product instanceof ProductA) {
productAQuoteService.getQuote((ProductA) product);
} else if (product instanceof ProductB) {
productBQuoteService.getQuote((ProductB) product);
}
}

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:
Category 'Red' has subcategories 'Maroon', 'Orange', 'Bordeaux'
Category 'Blue' has subcategories 'Light', 'Dark', 'Azure'
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?
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;
}
}

How to implement usage dependency, Enumeration data type in Java? Confusion to implement Aggregation, Composition

I have to implement the following class diagram to the java code. This diagram is very complicated and some parts creates confusion. This question definitely going to help me a lot as well as any reader because it contains several important aspects of UML diagram.
class Book{
String isbn;
String publisher;
String publishDate;
int pages;
}
class BookItem extends Book{
String barcode;
boolean isReferenceOnly;
}
class Author{
String name;
String biography;
Collection<Book> book;
}
class Account{
String number;
List<History> history;
String openDate;
AccountState state;
public Account(AccountState state){
this.state = state;
}
}
enum AccountState{
Active,
Frozen,
Closed
}
class Catalog implements Search, Manage{
List<BookItem> bookItem;
/* Implement the methods of Manage interface */
void add(BookItem item){ }
void remove(BookItem item){ }
/* Implement the methods of Search interface */
int search(BookItem item){ }
}
class Account{
String number;
List<History> history;
Student student = new Student();
void setStudent(Student student){
this.student = student;
}
}
interface Search{
int search(BookItem item);
}
interface Manage{
void add(BookItem item);
void remove(BookItem item);
}
class Student{
String name;
String address;
Search searchBook = new Catalog();
}
class Librarian{
String name;
String address;
String position;
Search searchBook = new Catalog();
Manage manage = new Catalog();
Account account = new Account();
void setAccount(Account account){
this.account = account;
}
class Library{
String name;
String Address;
List<BookItem> bookItem = new ArrayList<BookItem>();
Catalog catalog = new catalog();
List<Account> accounts = new ArrayList<Account>();
Library(Catalog catalog){
this.catalog = catalog;
}
void setBookItem(List<BookItem> bookItem){
this.bookItem = bookItem;
}
void setAccounts(List<Account> accounts){
this.accounts = accounts;
}
}
I implemented in the following way but confusion arise in various cases:
How to implement Class Student use the interface Search.
How to implement Class Librarian use the interfaces Search and Manage.
Why we are not use association instead of usage dependency.
How to implement that Enumeration data type in this case with usage dependency [I have just considered AccountState as a class, i the it is a wrong implementation].
How to use AccountState in the Account [I have just created a object of AccountState].
After read many blogs still unable to implement Aggregation and Composition confidently. Note: In this diagram 3 Aggregations and 1 Composition Exist. Those are:
(a) Library consists of many Account. {Aggregation}
(b) Many Book Item is the part of Library. {Aggregation}
(c) An Account is the part of a Student. {Aggregation}
(d) Library must have a Catalog. {Composition}
Please give your valuable advice so i can learn it well. Thanking you.
Since this question is homework for learning purposes, I will post only examples of how to implement the things you need to review and won't give a direct answer about how to apply them to your current design.
Enumeration in Java is implemented by using enum.
enum WeekDays {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
}
Aggregation/Composition means to have a field of the other class. If it's a weak association (aggregation), it should be initialized by the setter or another method. If it's a strong association, it should be initialized in the class constructor since it is needed for the class to live/work.
class WeakAssociation { }
class StrongAssociation { }
class NeedWeekAndStrongAssociation {
private WeakAssociation weakAssociation;
private StrongAssociation strongAssociation;
public NeedWeekAndStrongAssociation(StrongAssociation strongAssociation) {
this.strongAssociation = strongAssociation;
}
public void setWeakAssociation(WeakAssociation weakAssociation) {
this.weakAssociation = weakAssociation;
}
}
Usage dependency means that the class/interface will use the other class/interface within one or more of its methods:
class WantToBeUsed {
public void methodToBeUsed(String data) {
//fancy implementation
}
}
class CannotDoThisAlone {
public void cannotDoItAlone(String data) {
WantToBeUsed wantToBeUsed = new WantToBeUsed();
wantToBeUsed.methodToBeUsed(data);
}
}

Java - project, adding an object to class

I am currently struggling to figure out this problem for my project. I currently have a Food class that stores name, price and description with getters and setters and a toString. And a course class with subclasses (starter, main dessert). I am trying to figure out how to attach a Food to a Course.
public abstract class Course{
//fields
//protected only accessible to subclasses
protected MenuList starter;
protected MenuList main;
protected MenuList dessert;
protected MenuList drinks;
//Constructor
public Course(){
starter = new MenuList();
main = new MenuList();
dessert = new MenuList();
drinks = new MenuList();
}
//getters and setters
//methods
public abstract MenuList getList();
//add item
public void addItem(String course, String foodName, double price, String description, int calories){
this.addItem(course, foodName, price, description, calories);
}
}
starter subclass its the same with main and dessert subclasses
public class StarterFood extends Course{
//fields
//constructor
public StarterFood(){
//course,
starter.addItem("starter", "chicken wings", 2.30, "very nice", 150, false);
}
#Override
public MenuList getList() {
return starter;
}
//Constructors
//getters and setters
//methods
}
so far ive:
adding food (with a name, price, description, calories)
listing all food items
adding courses
searching for a course (by course number or name)
listing all courses
I only need to do this but I'm struggling any help is appreciated
attaching food to courses
If your trying to add a Food to a Course, you should use a "has a" relationship for example:
public class Course {
private Food food;
public Course(Food food) {
this.food = food;
}
public Course() {
}
public Food getFood() {
return this.food;
}
public void setFood(Food food) {
this.food = food;
}
}
I also wouldn't use StarterFood to extend a Course, because extends is for and "is a" relationship, I would call it StarterCourse and then add a default food for that course in the constructor.
public class StarterCourse extends Course {
public StarterCourse(Food food) {
// here call the super classes constructor
// add items via the Course constructor
super(food);
}
}
Then in your main class to test it out try this:
public class Main() {
public static void main() {
// First create new Food object
Food food = new Food();
// Create a new StarterCourse and add the Food object to it
StarterCourse starterCourse = new StarterCourse(food);
}
}

Categories

Resources