菜单服务设计选型:单父类实现还是拆分三类服务?
菜单管理服务设计方案咨询
我有一个菜单管理服务的练习需求,具体如下:
- 食品菜单包含早餐、午餐、晚餐三类菜单,每类都包含
MenuItem列表(属性:name、price、description、image) - 需要为三类菜单实现CRUD操作
初始实现方案
初始模型
FoodMenu模型
public class FoodMenu{ private BreakfastMenu breakfastMenu; private DinnerMenu dinnerMenu; private LunchMenu lunchMenu; public FoodMenu(BreakfastMenu breakfastMenu, DinnerMenu dinnerMenu, LunchMenu lunchMenu){ this.breakfastMenu = breakfastMenu; this.dinnerMenu = dinnerMenu; this.lunchMenu = lunchMenu; } }
BreakfastMenu模型
public class BreakfastMenu { private List<MenuItem> menuItemList; public BreakfastMenu(List<MenuItem> menuItemList){ this.menuItemList = menuItemList; } }
DinnerMenu模型
public class DinnerMenu { public DinnerMenu(){} private List<MenuItem> menuItemList; public DinnerMenu(List<MenuItem> menuItemList){ this.menuItemList = menuItemList; } }
LunchMenu模型
public class LunchMenu { private List<MenuItem> menuItemList; public LunchMenu(List<MenuItem> menuItemList){ this.menuItemList = menuItemList; } public LunchMenu(){} }
初始Service实现
早餐菜单服务实现(午餐、晚餐服务逻辑完全一致,仅参数类型不同):
public class BreakfastMenuServicesImpl implements BreakfastMenuServices { @Override public BreakfastMenu addMenuItemsToBreakfastMenu(BreakfastMenu breakfastMenu) { Scanner scanner = new Scanner(System.in); MenuItem menuItem = new MenuItem(); List<MenuItem> menuItemList= breakfastMenu.getMenuItemList(); if(menuItemList == null){ menuItemList = new ArrayList<MenuItem>(); } System.out.print("\nInsert food name: "); menuItem.setNames(scanner.nextLine()); System.out.print("Insert food description: "); menuItem.setDescription(scanner.nextLine()); System.out.print("Insert food image: "); menuItem.setImage(scanner.nextLine()); System.out.print("Insert food price:"); menuItem.setPrice(scanner.nextFloat()); menuItemList.add(menuItem); breakfastMenu.setMenuItemList(menuItemList); return breakfastMenu; } @Override public void showBreakFastMenu(BreakfastMenu breakfastMenu) { System.out.println("\nBreakfast menu:"); breakfastMenu.getMenuItemList().forEach(System.out::println); } @Override public void updateBreakfastMenu(BreakfastMenu breakfastMenu) { Scanner scanner = new Scanner(System.in); System.out.print("Insert food want to update:"); String foodItem = scanner.nextLine(); breakfastMenu.getMenuItemList().forEach((MenuItem menuItem)->{ if(menuItem.getNames().equals(foodItem)){ System.out.print("\nInsert food name: "); menuItem.setNames(scanner.nextLine()); System.out.print("Insert food description: "); menuItem.setDescription(scanner.nextLine()); System.out.print("Insert food image: "); menuItem.setImage(scanner.nextLine()); System.out.print("Insert food price:"); menuItem.setPrice(scanner.nextFloat()); } }); } @Override public void deleteBreakfastMenu(BreakfastMenu breakfastMenu) { Scanner scanner = new Scanner(System.in); System.out.print("Insert food want to delete:"); String foodItem = scanner.nextLine(); breakfastMenu.getMenuItemList().removeIf(menuItem -> menuItem.getNames().equals(foodItem)); } }
优化后实现方案
优化后模型
FoodMenu模型
public class FoodMenu{ private BreakfastMenu breakfastMenu; private DinnerMenu dinnerMenu; private LunchMenu lunchMenu; public FoodMenu(BreakfastMenu breakfastMenu, DinnerMenu dinnerMenu, LunchMenu lunchMenu){ this.breakfastMenu = breakfastMenu; this.dinnerMenu = dinnerMenu; this.lunchMenu = lunchMenu; } }
AbstractMenu模型
public class AbstractMenu { private List<MenuItem> menuItemList; public AbstractMenu(List<MenuItem> menuItemList){ this.menuItemList = menuItemList; } public AbstractMenu(){ } }
BreakfastMenu模型
public class BreakfastMenu extends AbstractMenu{ }
LunchMenu模型
public class LunchMenu extends AbstractMenu{ }
DinnerMenu模型
public class DinnerMenu extends AbstractMenu{ }
优化后Service
AbstractMenuService接口
public interface AbstractMenuService { AbstractMenu addMenuItemsToMenu(AbstractMenu abstractMenu); void showMenu(AbstractMenu abstractMenu); void updateMenu(AbstractMenu abstractMenu); void deleteMenu(AbstractMenu abstractMenu); }
子类服务接口
// BreakfastMenuServices接口 public interface BreakfastMenuServices extends AbstractMenuService{ } // DinnerMenuServices接口 public interface DinnerMenuServices extends AbstractMenuService{ } // LunchServices接口 public interface LunchServices extends AbstractMenuService{ }
优化后实现类(以BreakfastMenuServicesImpl为例)
public class BreakfastMenuServicesImpl implements BreakfastMenuServices { @Override public AbstractMenu addMenuItemsToMenu(AbstractMenu abstractMenu) { return null; } @Override public void showMenu(AbstractMenu abstractMenu) { } @Override public void updateMenu(AbstractMenu abstractMenu) { } @Override public void deleteMenu(AbstractMenu abstractMenu) { } }
咨询问题
- 两种方案中哪种更易读且便于维护?
- 因继承父接口需重写所有方法,但我无需差异化逻辑而留空实现,这种做法是否存在负面影响?
回答
一、方案对比:哪种更易读易维护
优化后的方案更符合DRY(Don't Repeat Yourself)原则,长期来看更易维护:
- 初始方案存在大量重复代码:三类菜单模型结构几乎一致,服务CRUD逻辑完全相同,仅参数类型不同。后续修改逻辑(比如新增字段、调整交互)需在三个服务类重复修改,极易出现遗漏或不一致。
- 优化后的方案通过
AbstractMenu统一菜单核心属性,AbstractMenuService定义通用CRUD方法,彻底避免重复代码。后续修改只需调整一处,维护成本大幅降低。 - 可读性上,优化方案结构清晰:抽象层明确了"所有菜单都具备CRUD能力"的核心逻辑,子类仅需继承,代码意图更直观。
不过当前优化方案的实现不完善(实现类留空),需要补充通用服务实现,比如编写AbstractMenuServiceImpl基类完成核心CRUD逻辑,让子类直接继承即可。
二、空实现的负面影响
留空实现(甚至返回null)会带来诸多问题:
- 运行时错误风险:
addMenuItemsToMenu返回null会触发空指针异常;空的showMenu、updateMenu会让功能直接失效,用户操作无反馈,排查难度大。 - 违背接口契约:接口定义了方法的行为约定,空实现相当于打破契约,会误导其他开发者对方法功能的预期。
- 扩展隐患:后续新增依赖这些方法的逻辑时,空实现会导致功能异常,且难以快速定位问题。
改进建议
- 编写通用的
AbstractMenuServiceImpl实现类,完成CRUD核心逻辑:
public class AbstractMenuServiceImpl implements AbstractMenuService { @Override public AbstractMenu addMenuItemsToMenu(AbstractMenu abstractMenu) { Scanner scanner = new Scanner(System.in); MenuItem menuItem = new MenuItem(); List<MenuItem> menuItemList = abstractMenu.getMenuItemList(); if (menuItemList == null) { menuItemList = new ArrayList<>(); } System.out.print("\nInsert food name: "); menuItem.setNames(scanner.nextLine()); System.out.print("Insert food description: "); menuItem.setDescription(scanner.nextLine()); System.out.print("Insert food image: "); menuItem.setImage(scanner.nextLine()); System.out.print("Insert food price:"); menuItem.setPrice(scanner.nextFloat()); menuItemList.add(menuItem); abstractMenu.setMenuItemList(menuItemList); return abstractMenu; } @Override public void showMenu(AbstractMenu abstractMenu) { String menuName = ""; if (abstractMenu instanceof BreakfastMenu) { menuName = "Breakfast"; } else if (abstractMenu instanceof LunchMenu) { menuName = "Lunch"; } else if (abstractMenu instanceof DinnerMenu) { menuName = "Dinner"; } System.out.printf("\n%s menu:\n", menuName); abstractMenu.getMenuItemList().forEach(System.out::println); } @Override public void updateMenu(AbstractMenu abstractMenu) { Scanner scanner = new Scanner(System.in); System.out.print("Insert food want to update:"); String foodItem = scanner.nextLine(); abstractMenu.getMenuItemList().forEach(menuItem -> { if (menuItem.getNames().equals(foodItem)) { System.out.print("\nInsert food name: "); menuItem.setNames(scanner.nextLine()); System.out.print("Insert food description: "); menuItem.setDescription(scanner.nextLine()); System.out.print("Insert food image: "); menuItem.setImage(scanner.nextLine()); System.out.print("Insert food price:"); menuItem.setPrice(scanner.nextFloat()); } }); } @Override public void deleteMenu(AbstractMenu abstractMenu) { Scanner scanner = new Scanner(System.in); System.out.print("Insert food want to delete:"); String foodItem = scanner.nextLine(); abstractMenu.getMenuItemList().removeIf(menuItem -> menuItem.getNames().equals(foodItem)); } }
- 子类服务实现直接继承通用实现类:
public class BreakfastMenuServicesImpl extends AbstractMenuServiceImpl implements BreakfastMenuServices { // 无需重复实现方法,后续需差异化逻辑时再重写对应方法 }
这样既避免重复代码,又保证方法有实际实现,同时保留了扩展差异化逻辑的灵活性。
内容的提问来源于stack exchange,提问作者David Moore
相关产品推荐
相关产品推荐

