Java-Enum常量特定方法
admin
2024-02-03 11:45:47
0

OnJava8-Enum-常量特定方法

用枚举实现责任链模式

责任链(Chain Of Responsibility)设计模式先创建了一批用于解决目标问题的不同方法,然后将它们连成一条“链”。

当一个请求先到达时,会顺着这条链传递下去,直到遇到链上某个可以处理该请求的方法。

可以很容易地用常量特定方法实现一条简单的责任链。考虑一个邮局模型,它对每一封邮件都会尝试用最常见的方式来处理,(如果行不通)并不断尝试别的方式,直到该邮件最终被视为“死信”(无法投递)。每种尝试可以看作一个策略(另一种设计模式),而整个策略列表放在一起就是一条责任链。

我们从一封邮件开始说起。它所有的重要特征都可以用来枚举表达。由于Mail对象是随机创建的,想要减小一封邮件的GenralDelivery被赋予YES的可能性,最简单的方法是创建更多的非YES的实例,因此枚举的定义一开始可能看起来有点好笑。

在Mail中,你会看到randomMail()方法,用来随机创建测试邮件。generator方法生成了一个Iterable对象,它使用randomMail方法来生成一定数量的Mail对象,每通过迭代器调用一次next()就会生成一个。这种结构允许通过调用Mail.genrator()方法实现for-in循环的简单创建能力。

首先对邮件进行建模:

package org.example.onjava.senior.example01enum.desgin;import org.example.onjava.onjavaUtils.Enums;import java.util.Iterator;/*** @Author Coder_Pans* @Date 2022/11/20 09:32* @PackageName:org.example.onjava.senior.example01enum.desgin* @ClassName: Mail* @Description: TODO 邮件建模* @Version 1.0*/
public class Mail {enum GeneralDelivery {YES,NO1,NO2,NO3,NO4,NO5}enum Scannability {UNSCANNABLE,YES1,YES2,YES3,YES4}enum Readability {ILLEGIBLE,YES1,YES2,YES3,YES4}enum Address {INCORRECT,OK1,OK2,OK3,OK4,OK5,OK6}enum ReturnAddress {MISSING,OK1,OK2,OK3,OK4,OK5}GeneralDelivery generalDelivery;Scannability scannability;Readability readability;Address address;ReturnAddress returnAddress;static long counter = 0;long id = counter++;@Override public String toString() {return "Mail " + id;}public String details() {return toString() +", General Delivery: " + generalDelivery +", Address Scannability: " + scannability +", Address Readability: " + readability +", Address Address: " + address +", Return address: " + returnAddress;}// Generate test Mail:public static Mail randomMail() {Mail m = new Mail();m.generalDelivery =Enums.random(GeneralDelivery.class);m.scannability =Enums.random(Scannability.class);m.readability =Enums.random(Readability.class);m.address = Enums.random(Address.class);m.returnAddress =Enums.random(ReturnAddress.class);return m;}public staticIterable generator(final int count) {return new Iterable() {int n = count;@Override public Iterator iterator() {return new Iterator() {@Override public boolean hasNext() {return n-- > 0;}@Override public Mail next() {return randomMail();}@Overridepublic void remove() { // Not implementedthrow new UnsupportedOperationException();}};}};}
}

创建常量特定方法:

package org.example.onjava.senior.example01enum.desgin;public class PostOffice {enum MailHandler {GENERAL_DELIVERY {@Override boolean handle(Mail m) {switch(m.generalDelivery) {case YES:System.out.println("Using general delivery for " + m);return true;default: return false;}}},MACHINE_SCAN {@Override boolean handle(Mail m) {switch(m.scannability) {case UNSCANNABLE: return false;default:switch(m.address) {case INCORRECT: return false;default:System.out.println("Delivering "+ m + " automatically");return true;}}}},VISUAL_INSPECTION {@Override boolean handle(Mail m) {switch(m.readability) {case ILLEGIBLE: return false;default:switch(m.address) {case INCORRECT: return false;default:System.out.println("Delivering " + m + " normally");return true;}}}},RETURN_TO_SENDER {@Override boolean handle(Mail m) {switch(m.returnAddress) {case MISSING: return false;default:System.out.println("Returning " + m + " to sender");return true;}}};abstract boolean handle(Mail m);}static void handle(Mail m) {for(MailHandler handler : MailHandler.values())if(handler.handle(m))return;System.out.println(m + " is a dead letter");}public static void main(String[] args) {for(Mail mail : Mail.generator(10)) {System.out.println(mail.details());handle(mail);System.out.println("*****");}}
}
/* Output:
Mail 0, General Delivery: NO2, Address Scannability:
UNSCANNABLE, Address Readability: YES3, Address
Address: OK1, Return address: OK1
Delivering Mail 0 normally
*****
Mail 1, General Delivery: NO5, Address Scannability:
YES3, Address Readability: ILLEGIBLE, Address Address:
OK5, Return address: OK1
Delivering Mail 1 automatically
*****
Mail 2, General Delivery: YES, Address Scannability:
YES3, Address Readability: YES1, Address Address: OK1,
Return address: OK5
Using general delivery for Mail 2
*****
Mail 3, General Delivery: NO4, Address Scannability:
YES3, Address Readability: YES1, Address Address:
INCORRECT, Return address: OK4
Returning Mail 3 to sender
*****
Mail 4, General Delivery: NO4, Address Scannability:
UNSCANNABLE, Address Readability: YES1, Address
Address: INCORRECT, Return address: OK2
Returning Mail 4 to sender
*****
Mail 5, General Delivery: NO3, Address Scannability:
YES1, Address Readability: ILLEGIBLE, Address Address:
OK4, Return address: OK2
Delivering Mail 5 automatically
*****
Mail 6, General Delivery: YES, Address Scannability:
YES4, Address Readability: ILLEGIBLE, Address Address:
OK4, Return address: OK4
Using general delivery for Mail 6
*****
Mail 7, General Delivery: YES, Address Scannability:
YES3, Address Readability: YES4, Address Address: OK2,
Return address: MISSING
Using general delivery for Mail 7
*****
Mail 8, General Delivery: NO3, Address Scannability:
YES1, Address Readability: YES3, Address Address:
INCORRECT, Return address: MISSING
Mail 8 is a dead letter
*****
Mail 9, General Delivery: NO1, Address Scannability:
UNSCANNABLE, Address Readability: YES2, Address
Address: OK1, Return address: OK4
Delivering Mail 9 normally
*****
*/

责任链模式的作用体现在了MailHandler枚举中,枚举的定义顺序则决定了各个策略在每封邮件上被应用的顺序。该模式会按顺序尝试应用每个策略,直到某个策略执行成功,或者全部策略都执行失败(即邮件无法投递)

用枚举实现状态机

枚举类型很适合用来实现状态机。状态机可以处于有限数量的特定状态。它们通常根据输入,从一个状态转移到下一个状态,但同时也会存在瞬态。当任务执行完毕后,状态机会立即跳出所有状态。

每个状态一般也会有某种对应的输出。

通过自动售货机案例来了解如何实现状态机,首先,在一个枚举中定义一系列输入:

/*** 用枚举实现状态机  01*/
public enum Input {NICKEL(5), DIME(10), QUARTER(25), DOLLAR(100),TOOTHPASTE(200), CHIPS(75), SODA(100), SOAP(50),ABORT_TRANSACTION {@Override public int amount() { // Disallowthrow new RuntimeException("ABORT.amount()");}},STOP { // This must be the last instance.@Override public int amount() { // Disallowthrow newRuntimeException("SHUT_DOWN.amount()");}};int value; // In centsInput(int value) { this.value = value; }Input() {}int amount() { return value; }; // In centsstatic Random rand = new Random(47);public static Input randomSelection() {// Don't include STOP:returnvalues()[rand.nextInt(values().length - 1)];}
}

VendingMachine(自动售货机)接收到输入后,首先通过Category(类别)枚举来对这些输入进行分类,这样就可以在各个类别间切换了。

package org.example.onjava.senior.example01enum.desgin;// enums/VendingMachine.java
// (c)2021 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
// {java VendingMachine VendingMachineInput.txt}import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.function.Supplier;
import java.util.stream.Collectors;enum Category {MONEY(Input.NICKEL, Input.DIME,Input.QUARTER, Input.DOLLAR),ITEM_SELECTION(Input.TOOTHPASTE, Input.CHIPS,Input.SODA, Input.SOAP),QUIT_TRANSACTION(Input.ABORT_TRANSACTION),SHUT_DOWN(Input.STOP);private Input[] values;Category(Input... types) { values = types; }private static EnumMap categories =new EnumMap<>(Input.class);static {for(Category c : Category.class.getEnumConstants())for(Input type : c.values)categories.put(type, c);}public static Category categorize(Input input) {return categories.get(input);}
}public class VendingMachine {private static State state = State.RESTING;private static int amount = 0;private static Input selection = null;enum StateDuration { TRANSIENT } // Tagging enumenum State {RESTING {@Override void next(Input input) {switch(Category.categorize(input)) {case MONEY:amount += input.amount();state = ADDING_MONEY;break;case SHUT_DOWN:state = TERMINAL;default:}}},ADDING_MONEY {@Override void next(Input input) {switch(Category.categorize(input)) {case MONEY:amount += input.amount();break;case ITEM_SELECTION:selection = input;if(amount < selection.amount())System.out.println("Insufficient money for " + selection);else state = DISPENSING;break;case QUIT_TRANSACTION:state = GIVING_CHANGE;break;case SHUT_DOWN:state = TERMINAL;default:}}},DISPENSING(StateDuration.TRANSIENT) {@Override void next() {System.out.println("here is your " + selection);amount -= selection.amount();state = GIVING_CHANGE;}},GIVING_CHANGE(StateDuration.TRANSIENT) {@Override void next() {if(amount > 0) {System.out.println("Your change: " + amount);amount = 0;}state = RESTING;}},TERMINAL {@Overridevoid output() { System.out.println("Halted"); } };private boolean isTransient = false;State() {}State(StateDuration trans) { isTransient = true; }void next(Input input) {throw new RuntimeException("Only call " +"next(Input input) for non-transient states");}void next() {throw new RuntimeException("Only call next() for " +"StateDuration.TRANSIENT states");}void output() { System.out.println(amount); }}static void run(Supplier gen) {while(state != State.TERMINAL) {state.next(gen.get());while(state.isTransient)state.next();state.output();}}public static void main(String[] args) {Supplier gen = new RandomInputSupplier();if(args.length == 1)gen = new FileInputSupplier(args[0]);run(gen);}
}// For a basic sanity check:
class RandomInputSupplier implements Supplier {@Override public Input get() {return Input.randomSelection();}
}// Create Inputs from a file of ';'-separated strings:
class FileInputSupplier implements Supplier {private Iterator input;FileInputSupplier(String fileName) {try {input = Files.lines(Paths.get(fileName)).skip(1) // Skip the comment line.flatMap(s -> Arrays.stream(s.split(";"))).map(String::trim).collect(Collectors.toList()).iterator();} catch(IOException e) {throw new RuntimeException(e);}}@Override public Input get() {if(!input.hasNext())return null;return Enum.valueOf(Input.class, input.next().trim());}
}
/* Output:
25
50
75
here is your CHIPS
0
100
200
here is your TOOTHPASTE
0
25
35
Your change: 35
0
25
35
Insufficient money for SODA
35
60
70
75
Insufficient money for SODA
75
Your change: 75
0
Halted
*/

相关内容

热门资讯

视频号广告服务企业梳理:工业制... 导语:根据《2025-2026中国短视频与直播营销白皮书》及多家第三方机构数据,视频号广告服务市场近...
原创 黄... 黄金这一跌,把两类人同时照了出来:一类是嘴上喊着“回调就买”的围观者,另一类是把客户定金当筹码的黄金...
半导体大牛股,紧急发布澄清声明 【导读】富信科技发布澄清公告 中国基金报记者 莫琳 6月28日,针对部分网络媒体、自媒体及社交媒体平...
脑佳科技CEO蒲云海:在真实场... 封面新闻记者 欧阳宏宇 无需开刀,靠意念就能操控机械手……近年来,脑机接口正逐步走进医疗领域,尤其是...
原创 6... 中国并没有选择让伊朗拖垮美国,这背后的算盘比表面复杂得多。 很多人以为中国是在“帮美国解围”,但其实...
三条全球最大碳纤维产线同日投产... 来源:第一财经 我国碳纤维产业创新发展再添里程碑。 6月28日,中国建材三条世界级高性能碳纤维生产线...
天津名酒回收行业观察:打破单一... 提起名酒回收,绝大多数人的固有认知,还停留在单一回收茅台、五粮液等国产高端白酒的传统模式。 过去很长...
东莞松山湖科技金融集聚区正式开... 6月25日上午,东莞松山湖科技金融集聚区开园仪式在集聚区金融广场举行。活动现场八大合作平台集中揭牌、...
携手华为“鸿图”打造智慧医院“... 在数字化浪潮奔涌的今天,一部手机控制全屋智能已不稀奇,但在关乎生命健康的医院里,让成千上万的医疗设备...
蜜雪冰城的玩法,口子窖能复制吗... 文 | 创业最前线 “麻雀也能喝二两”这句在安徽淮北濉溪县街头巷尾流传的俗语,最近被口子窖写成了一...
必迈体育冲击IPO,实控人、董... 曾任职李宁CEO的张志勇,如今带领旗下跑步运动垂类品牌“必迈”的运营主体北京必迈体育股份有限公司(以...
OpenAI推迟上市,那“Ki... 来源:虎嗅APP AI的估值逻辑变了,“Kimi们”准备好了吗? 出品|虎嗅科技组 作者|宋思杭 ...
又有城商行获批入股农商行 6月26日,宁夏金融监管局发布的行政许可信息显示,同意宁夏银行股份有限公司受让江苏汇金控股集团有限公...
中国建材三条世界级高性能碳纤维... 6月28日,据中国建材集团消息,中国建材三条世界级高性能碳纤维生产线在中复神鹰连云港基地集中投产,覆...
原创 6... 2026年的上半年,眼瞅着就要翻篇了。这半年,咱们普通人的日子过得有甜也有苦。菜价稳得住,工资单上的...
股价三连板!000823,紧急... 股价连续三个交易日涨停后,6月28日晚间超声电子(000823)发布了股票交易异常波动公告。 超声...
原创 人... 文 | 壹派 编辑 | 壹派 全国人口格局正在迎来彻底洗牌,新一轮定向迁徙浪潮早已悄悄拉开帷幕。 2...
原创 黄... 6月24日那天,盘面上的数字让不少人盯着屏幕发愣。国际现货黄金跌穿了4000美元。 高点还在5626...
原创 帮... 老铁们,下周开盘前先把这俩日子钉日历上:美国6月非农下周四出(因下周五独立日休市,比平时早一天),7...
日媒紧盯:中国关键金属进口量飙... 【文/观察者网 王恺雯】 为维护国家安全和利益、履行防扩散等国际义务,中方于今年1月宣布加强两用物项...