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 static
Iterable<Mail> generator(final int count) {
return new Iterable<Mail>() {
int n = count;
@Override public Iterator<Mail> iterator() {
return new Iterator<Mail>() {
@Override public boolean hasNext() {
return n-- > 0;
}
@Override public Mail next() {
return randomMail();
}
@Override
public void remove() { // Not implemented
throw 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() { // Disallow
throw new RuntimeException("ABORT.amount()");
}
},
STOP { // This must be the last instance.
@Override public int amount() { // Disallow
throw new
RuntimeException("SHUT_DOWN.amount()");
}
};
int value; // In cents
Input(int value) { this.value = value; }
Input() {}
int amount() { return value; }; // In cents
static Random rand = new Random(47);
public static Input randomSelection() {
// Don't include STOP:
return
values()[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<Input,Category> 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 enum
enum 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 {@Override
void 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<Input> gen) {
while(state != State.TERMINAL) {
state.next(gen.get());
while(state.isTransient)
state.next();
state.output();
}
}
public static void main(String[] args) {
Supplier<Input> gen = new RandomInputSupplier();
if(args.length == 1)
gen = new FileInputSupplier(args[0]);
run(gen);
}
}
// For a basic sanity check:
class RandomInputSupplier implements Supplier<Input> {
@Override public Input get() {
return Input.randomSelection();
}
}
// Create Inputs from a file of ';'-separated strings:
class FileInputSupplier implements Supplier<Input> {
private Iterator<String> 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
*/
标签:Java,常量,01enum,Address,return,amount,Input,Mail,public
From: https://www.cnblogs.com/atwood-pan/p/16907928.html