今天学习了接口,策略模式(Strategy Pattern)

为了以后不加新算法时,不改SmartHomeQuotation类,SmartHomeQuotation类只负责“把总价交给策略对象计算”,至于怎么算是策略对象的事。PercentageDiscountStrategy类做的事和SmartHomeQuotation类里现有的方法一样。但接口的意义不在于做新的事,而在于把“怎么做”从“做什么”里解耦出来,让系统可以无限扩展新算法而不修改旧代码。这就是策略模式(Strategy Pattern)

main(),新增菜单 7 (通过接口来实现)

▼
text
复制代码
package cn.com.d99714.smart.service; import cn.com.d99714.smart.common.Config; import cn.com.d99714.smart.entity.*; import cn.com.d99714.smart.strategy.DiscountStrategy; import cn.com.d99714.smart.strategy.PercentageDiscountStrategy; import java.util.Scanner; public class SmartHomeTest { // Test 类私有的交互常量 private static final String MENU_TITLE = "===== 智能家居报价系统 V0.x ====="; private static final String MENU_EXIT = "0. 退出"; private static final String MSG_INVALID = "无效选项"; private static final String MSG_SUCCESS = "创建成功"; // 模拟数据存储 private static SmartHomeQuotation[] quotations = new SmartHomeQuotation[10]; private static int quotationCount = 0; private static Person[] people = new Person[10]; private static int peopleCount = 0; private static Scanner scanner = new Scanner(System.in); public static void main(String[] args) { // 预置测试数据 initTestData(); // 初始化数据 // 菜单循环 while (true) { printMenu(); // 输出菜单 String selected = scanner.nextLine().trim(); switch (selected) { case "1" -> createCustomer(); // 创建客户 case "2" -> createQuotation(); // 创建报价单 case "3" -> showQuotationList(); // 查看报价单列表(摘要) case "4" -> showQuotationDetail(); // 查看报价单详情 case "5" -> showRoleDemo(); // 多态角色展示 case "6" -> discountDemo(); // 折扣计算演示 case "7" -> showStategyDemo(); // 折扣方案演示 case "0" -> { // 退出 System.out.println("Bye. See you next time."); return; } default -> System.out.println("无效选项,请重新输入"); } System.out.println(); } } /** * 1. 打印菜单(方法提取) */ private static void printMenu() { System.out.println("========== 智能家居报价系统 v0.1 =========="); System.out.println("1. 创建客户"); System.out.println("2. 创建报价单"); System.out.println("3. 查看报价单列表"); System.out.println("4. 查看报价单详情"); System.out.println("5. 多态角色展示"); System.out.println("6. 折扣计算演示"); System.out.println("7. 折扣方案演示"); System.out.println("0. 退出"); System.out.print("请选择(1-6)【0-退出】:"); } /** * 2. 预置测试数据(方法提取) */ private static void initTestData() { Company vendor = new Company("张经理", "13829871635", "视声智能", "广州市黄埔区", "KNX全屋智能/灯光/安防"); Person client1 = new Customer("赵今麦", "15138473848", "上海市静安区", 533); Person client2 = new Company("李经理", "13150432864", "杜亚窗帘", "杭州市拱墅区", "智能窗帘/智能门锁"); Person worker = new Worker("王师傅", "13022531893", "电工", 2, 4.2); people[peopleCount++] = vendor; people[peopleCount++] = client1; people[peopleCount++] = client2; people[peopleCount++] = worker; SmartHomeQuotation q1 = new SmartHomeQuotation( Config.QUOTATION_PREFIX + "20260001-000001", "2026-10-02 16:35:11", "赵女士静安府全屋智能", client1, vendor, 35800.0, 5800.0, 6500.0, 4200.0, 2000.0); q1.setTotalDiscount(0.85); SmartHomeQuotation q2 = new SmartHomeQuotation( Config.QUOTATION_PREFIX + "20260001-000002", "2026-10-02 16:37:20", "杜亚展厅智能改造", client2, vendor, 28000.0, 3500.0, 4000.0, 2500.0, 1500.0); q2.setTotalDiscount(0.80); quotations[quotationCount++] = q1; quotations[quotationCount++] = q2; } /** * 3. 创建客户(方法提取) */ private static void createCustomer() { System.out.println("创建客户"); System.out.print("类型(1-业主 2-公司 3-师傅):"); String type = scanner.nextLine().trim(); System.out.print("姓名:"); String name = scanner.nextLine().trim(); System.out.print("电话:"); String phone = scanner.nextLine().trim(); switch (type) { case "1" -> { System.out.print("房屋地址:"); String addr = scanner.nextLine().trim(); System.out.print("房屋面积:"); double area = Double.parseDouble(scanner.nextLine().trim()); people[peopleCount++] = new Customer(name, phone, addr, area); } case "2" -> { System.out.print("公司名称:"); String cName = scanner.nextLine().trim(); System.out.print("公司地址:"); String cAddr = scanner.nextLine().trim(); people[peopleCount++] = new Company(name, phone, cName, cAddr, ""); } case "3" -> { System.out.print("工种:"); String job = scanner.nextLine().trim(); System.out.print("工龄:"); int year = Integer.parseInt(scanner.nextLine().trim()); System.out.print("评分:"); double score = Double.parseDouble(scanner.nextLine().trim()); people[peopleCount++] = new Worker(name, phone, job, year, score); } default -> System.out.println("无效类型"); } System.out.println("创建成功!"); } /** * 4. 创建报价单(方法提取) */ private static void createQuotation() { if (peopleCount < 2) { System.out.println("请先创建客户和厂家"); return; } System.out.println("创建报价单"); // 直接选预置数据,不交互选客户 System.out.println("功能开发中,请使用预置数据(选项3/4查看)"); } /** * 5. 查看报价单列表(方法提取) */ private static void showQuotationList() { System.out.println("报价单列表"); for (int i = 0; i < quotationCount; i++) { System.out.println((i + 1) + ". " + quotations[i].getSummaryLine()); } if (quotationCount == 0) { System.out.println("暂无报价单"); } } /** * 6. 查看报价单详情(方法提取) */ private static void showQuotationDetail() { System.out.print("请输入报价单序号(1-" + quotationCount + "):"); int idx = Integer.parseInt(scanner.nextLine().trim()) - 1; if (idx < 0 || idx >= quotationCount) { System.out.println("序号无效"); return; } System.out.println(quotations[idx]); } /** * 7. 多态角色展示(方法提取) */ private static void showRoleDemo() { System.out.println("多态角色展示"); for (int i = 0; i < peopleCount; i++) { Person p = people[i]; System.out.println(p.showRole() + " " + p.getName()); System.out.println(" " + p.introduce()); } } /** * 8. 折扣计算演示(方法提取) */ private static void discountDemo() { System.out.println("折扣计算演示"); for (int i = 0; i < quotationCount; i++) { SmartHomeQuotation q = quotations[i]; System.out.printf("%s 原价:%,.2f,%s后:%,.2f,优惠:%,.2f%n", q.getQuotationNo(), q.calcOriginalTotalAmount(), q.getDiscountDesc(), q.calcDiscountedTotalAmount(), q.calcOriginalTotalAmount() - q.calcDiscountedTotalAmount()); } } /** * 9. 策略接口演示(方法提取) */ private static void showStategyDemo(){ System.out.println("策略接口演示"); DiscountStrategy s1 = new PercentageDiscountStrategy(0.85); DiscountStrategy s2 = new PercentageDiscountStrategy(0.80); System.out.printf(" %s 接口策略【%s】:%,.2f 元 %n", quotations[0].getQuotationNo(), s1.getStrategyName(), quotations[0].getDiscountedTotal(s1)); System.out.printf(" %s 接口策略【%s】:%,.2f 元 %n", quotations[1].getQuotationNo(), s2.getStrategyName(), quotations[1].getDiscountedTotal(s2)); } }

新增DiscountStrategy接口,两个方法,一个是获取整单折扣后的金额,一个是获取折扣的中文描述

▼
text
复制代码
package cn.com.d99714.smart.strategy; public interface DiscountStrategy { // 获取整单折扣后的金额,相当于SmartHomeQuotation类中的getDiscountedTotal() double applyDiscount(double amount); // 获取折扣的中文描述,相当于SmartHomeQuotation类中的getDiscountDesc() String getStrategyName(); }

新增PercentageDiscountStrategy类,实现DiscountStrategy接口的两个方法

▼
text
复制代码
package cn.com.d99714.smart.strategy; public class PercentageDiscountStrategy implements DiscountStrategy{ private double rate; public PercentageDiscountStrategy(double rate){ this.rate = rate; } @Override public double applyDiscount(double amount){ return amount * rate; } @Override public String getStrategyName(){ return (int)(rate * 100) + "%折扣"; } }

SmartHomeQuotation类,新增:通过接口实现类的方法来获取整单折扣后的金额

▼
text
复制代码
package cn.com.d99714.smart.entity; import cn.com.d99714.smart.common.Config; import cn.com.d99714.smart.strategy.DiscountStrategy; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; public class SmartHomeQuotation { private String quotationNo = ""; private String quotationDate = ""; private String projectName = ""; private Person client; private Company vendor; private double deviceCost = 0.0; private double materialCost = 0.0; private double installCost = 0.0; private double debugCost = 0.0; private double otherCost = 0.0; // 分类折扣 private double deviceDiscount = 1.0; private double materialDiscount = 1.0; private double installDiscount = 1.0; private double debugDiscount = 1.0; private double otherDiscount = 1.0; // 整单折扣 private double totalDiscount = 1.0; public SmartHomeQuotation() { } public SmartHomeQuotation(String quotationNo, String quotationDate, String projectName, Person client, Company vendor, double deviceCost, double materialCost, double installCost, double debugCost, double otherCost) { this.setQuotationNo(quotationNo); this.setQuotationDate(quotationDate); this.setProjectName(projectName); this.setClient(client); this.setVendor(vendor); this.setDeviceCost(deviceCost); this.setMaterialCost(materialCost); this.setInstallCost(installCost); this.setDebugCost(debugCost); this.setOtherCost(otherCost); } public void setQuotationNo(String quotationNo) { this.quotationNo = quotationNo; } public void setQuotationDate(String quotationDate) { this.quotationDate = quotationDate; } public void setProjectName(String projectName) { this.projectName = projectName; } public void setClient(Person client) { this.client = client; } public void setVendor(Company vendor) { this.vendor = vendor; } public void setDeviceCost(double deviceCost) { this.deviceCost = deviceCost; } public void setMaterialCost(double materialCost) { this.materialCost = materialCost; } public void setInstallCost(double installCost) { this.installCost = installCost; } public void setDebugCost(double debugCost) { this.debugCost = debugCost; } public void setOtherCost(double otherCost) { this.otherCost = otherCost; } public void setDeviceDiscount(double deviceDiscount) { if (deviceDiscount < Config.MIN_DISCOUNT_RATE || deviceDiscount > Config.MAX_DISCOUNT_RATE) { System.out.println(Config.MSG_WARN_PREFIX + Config.MSG_DISCOUNT_RATE_INVALID); return; } this.deviceDiscount = deviceDiscount; } public void setMaterialDiscount(double materialDiscount) { if (materialDiscount < Config.MIN_DISCOUNT_RATE || materialDiscount > Config.MAX_DISCOUNT_RATE) { System.out.println(Config.MSG_WARN_PREFIX + Config.MSG_DISCOUNT_RATE_INVALID); return; } this.materialDiscount = materialDiscount; } public void setInstallDiscount(double installDiscount) { if (installDiscount < Config.MIN_DISCOUNT_RATE || installDiscount > Config.MAX_DISCOUNT_RATE) { System.out.println(Config.MSG_WARN_PREFIX + Config.MSG_DISCOUNT_RATE_INVALID); return; } this.installDiscount = installDiscount; } public void setDebugDiscount(double debugDiscount) { if (debugDiscount < Config.MIN_DISCOUNT_RATE || debugDiscount > Config.MAX_DISCOUNT_RATE) { System.out.println(Config.MSG_WARN_PREFIX + Config.MSG_DISCOUNT_RATE_INVALID); return; } this.debugDiscount = debugDiscount; } public void setOtherDiscount(double otherDiscount) { if (otherDiscount < Config.MIN_DISCOUNT_RATE || otherDiscount > Config.MAX_DISCOUNT_RATE) { System.out.println(Config.MSG_WARN_PREFIX + Config.MSG_DISCOUNT_RATE_INVALID); return; } this.otherDiscount = otherDiscount; } public void setTotalDiscount(double totalDiscount) { if (totalDiscount < Config.MIN_DISCOUNT_RATE || totalDiscount > Config.MAX_DISCOUNT_RATE) { System.out.println(Config.MSG_WARN_PREFIX + Config.MSG_DISCOUNT_RATE_INVALID); return; } this.totalDiscount = totalDiscount; } public String getQuotationNo() { return this.quotationNo; } public String getQuotationDate() { LocalDateTime dateTime = LocalDateTime.parse(this.quotationDate, Config.DATE_TIME_INPUT_FORMATTER); return dateTime.format(Config.DATE_TIME_DISPLAY_FORMATTER); } public String getProjectName() { return this.projectName; } public Person getClient() { return this.client; } public Company getVendor() { return this.vendor; } public double getDeviceCost() { return this.deviceCost; } public double getMaterialCost() { return this.materialCost; } public double getInstallCost() { return this.installCost; } public double getDebugCost() { return this.debugCost; } public double getOtherCost() { return this.otherCost; } public double getDeviceDiscount() { return this.deviceDiscount; } public double getMaterialDiscount() { return this.materialDiscount; } public double getInstallDiscount() { return this.installDiscount; } public double getDebugDiscount() { return this.debugDiscount; } public double getOtherDiscount() { return this.otherDiscount; } public double getTotalDiscount() { return this.totalDiscount; } /** * 计算原价的总金额 * @return 原价的总金额 */ public double calcOriginalTotalAmount() { return (deviceCost + materialCost + installCost + debugCost + otherCost); } /** * 计算优惠后的总金额 (总金额 = 设备费用 * 折扣 + 材料费用 * 折扣 + 安装费用 * 折扣 + 调试费用 * 折扣 + 其他费用 * 折扣) * 折扣 * * @return 折扣后的总金额 */ public double calcDiscountedTotalAmount() { double dc = deviceCost * deviceDiscount; double mc = materialCost * materialDiscount; double ic = installCost * installDiscount; double bc = debugCost * debugDiscount; double oc = otherCost * otherDiscount; return (dc + mc + ic + bc + oc) * totalDiscount; } /** * 计算优惠的金额(优惠的金额 = 原始的总金额 - 优惠后的总金额) * * @return 优惠的金额 */ public double caclDiscountAmount() { return calcOriginalTotalAmount() - calcDiscountedTotalAmount(); } // TODO:测试,要删除 /** * 试算:不修改字段,只返回给定折扣率的计算结果 * @param discountRate 试算折扣率 * @return 试算金额 */ public double getDiscountedTotal(double discountRate) { if (discountRate < 0.0 || discountRate > 1.0) { System.out.println(Config.MSG_WARN_PREFIX + Config.MSG_DISCOUNT_RATE_INVALID); return calcOriginalTotalAmount(); } return calcOriginalTotalAmount() * discountRate; } /** * 获取客户信息,通过 instanceof来判断客户是 Customer还是 Company * @return 客户类型:Customer或 Company(Config类有配置) */ public String getClientDisplay() { if (client instanceof Customer) { Customer cu = (Customer) client; return cu.getName() + Config.TAG_CUSTOMER; } else if (client instanceof Company) { Company co = (Company) client; return co.getCompanyName() + Config.TAG_COMPANY; } return ""; } /** * 获取客户的完整信息,通过 instanceof来判断客户是 Customer还是 Company * @return 客户信息 */ public String getClientFullInfo() { if (client instanceof Customer) { Customer cu = (Customer) client; return Config.TAG_CUSTOMER + "电话:" + cu.getPhone() + ",地址:" + cu.getHouseAddress() + ",房屋面积:" + cu.getHouseArea() + "m²"; } else if (client instanceof Company) { Company co = (Company) client; return Config.TAG_COMPANY + "联系人:" + co.getName() + ",电话:" + co.getPhone() + ",地址:" + co.getAddress(); } return ""; } /** * 折扣率转中文描述 * 1.0 → 原价, 0.85 → 85折, 0.8 → 8折, 0.0 → 免费 * @return 中文描述 */ public String getDiscountDesc() { if (totalDiscount == Config.MAX_DISCOUNT_RATE) return "原价"; if (totalDiscount == Config.MIN_DISCOUNT_RATE) return "免费"; long percent = Math.round(totalDiscount * 100); // 0.85→85, 0.8→80 if (percent % 10 == 0) { return (percent / 10) + "折"; // 80→8折, 50→5折 } else { return percent + "折"; // 85→85折, 88→88折 } } /** * 报价单摘要展示 * @return 报价单的摘要 */ public String getSummaryLine() { return String.format("%s | %s | %s | 原始金额:%s 元 | 折扣:%.0f%% | 折后金额:%s 元", quotationNo, projectName, getClientDisplay(), String.format(Config.AMOUNT_FORMAT, calcOriginalTotalAmount()), totalDiscount * 100, String.format(Config.AMOUNT_FORMAT, calcDiscountedTotalAmount()) ); } /** * 重写 toString() * @return */ @Override public String toString() { double totalAmount = calcDiscountedTotalAmount(); // 调用calcTotalAmount(),计算报价单总金额 StringBuilder sb = new StringBuilder(); // 静态拼接报价单的头 sb.append(Config.LINE_CHAR.repeat(Config.LINE_TITLE_REPEAT_COUNT)).append(Config.QUOTATION_TITLE).append(Config.LINE_CHAR.repeat(Config.LINE_TITLE_REPEAT_COUNT)).append("\n"); sb.append(String.format(Config.LABEL_QUOTATION_NO + "%s%n", getQuotationNo())); sb.append(String.format(Config.LABEL_QUOTATION_DATE + "%s%n", getQuotationDate())); sb.append(String.format(Config.LABEL_VENDOR + "%s%n", vendor.getCompanyName())); sb.append(String.format(Config.LABEL_PROJECT_NAME + "%s%n", getProjectName())); sb.append(String.format(Config.LABEL_CLIENT_INFO + "%s%n", getClientDisplay())); sb.append(Config.LINE_CHAR.repeat(Config.LINE_REPEAT_COUNT * 2)).append("\n"); // 动态拼接报价单金额(按照总金额格式化后的长度右对齐) String totalStr = String.format(Config.AMOUNT_FORMAT, totalAmount); int width = totalStr.length(); // 折后金额 sb.append(String.format(Config.LABEL_DISCOUNTED_AMOUNT + "%," + width + ".2f" + " 元%n", calcDiscountedTotalAmount())); // 优惠金额 sb.append(String.format(Config.LABEL_DISCOUNT_AMOUNT + "%," + width + ".2f" + " 元%n", caclDiscountAmount())); // 原始金额 sb.append(String.format(Config.LABEL_TOTAL_AMOUNT + "%," + width + ".2f" + " 元(" + Config.LABEL_DISCOUNT_RATE + "%.0f %% )%n", calcOriginalTotalAmount() , totalDiscount * 100)); sb.append(Config.LINE_CHAR.repeat(Config.LINE_REPEAT_COUNT * 2)).append("\n"); sb.append(Config.LABEL_DETAIL_PREFIX + "\n"); sb.append(String.format(Config.LABEL_DEVICE_COST + "%," + width + ".2f" + " 元(" + Config.LABEL_DISCOUNT_RATE + "%.0f %% )%n", deviceCost * deviceDiscount, deviceDiscount * 100)); sb.append(String.format(Config.LABEL_MATERIAL_COST + "%," + width + ".2f" + " 元(" + Config.LABEL_DISCOUNT_RATE + "%.0f %%) %n", materialCost * materialDiscount, materialDiscount * 100)); sb.append(String.format(Config.LABEL_INSTALL_COST + "%," + width + ".2f" + " 元(" + Config.LABEL_DISCOUNT_RATE + "%.0f %% )%n", installCost * installDiscount, installDiscount * 100)); sb.append(String.format(Config.LABEL_DEBUG_COST + "%," + width + ".2f" + " 元(" + Config.LABEL_DISCOUNT_RATE + "%.0f %% )%n", debugCost * debugDiscount, debugDiscount * 100)); sb.append(String.format(Config.LABEL_OTHER_COST + "%," + width + ".2f" + " 元(" + Config.LABEL_DISCOUNT_RATE + "%.0f %% )%n", otherCost * otherDiscount, otherDiscount * 100)); sb.append(Config.LINE_CHAR.repeat(Config.LINE_REPEAT_COUNT * 2)).append("\n"); return sb.toString(); } /** * 获取整单折扣的总金额(调用接口实现类 discountStrategy中的 applyDiscount()方法) * @param discountStrategy 折扣策略(也就是折扣) * @return 整单折扣的总金额(原始金额打折后的金额) */ public double getDiscountedTotal(DiscountStrategy discountStrategy){ return discountStrategy.applyDiscount(calcOriginalTotalAmount()); } }
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
鱼友0258
下载 APP