1.10 常量特定方法


Java的枚举机制可以通过为每个枚举实例编写不同的方法,来赋予它们不同的行为。

package www.com.cat.chapter01;  
  
import java.util.Arrays;  
  
public enum ConstantSpecificMethod {  
    QU {  
        @Override  
        void action() {  
            System.out.println("生死喧嚣,归于寂静");  
        }  
    },  
    SUOLONG {  
        @Override  
        void action() {  
            System.out.println("三千世界");  
        }  
    };  
  
    abstract void action();  
  
    public static void main(String[] args) {  
        Arrays.stream(values()).forEach(ConstantSpecificMethod::action);  
    }  
}
输出 : 
生死喧嚣,归于寂静
三千世界

你可以通过关联的枚举实例来查找和调用方法,这通常叫做表驱动模式
枚举的各种实例可以拥有各自的行为,这表明每个实例都是不同的类型。

package www.com.cat.chapter01;  
  
import java.util.EnumMap;  
import java.util.EnumSet;  
  
public class People {  
    enum Ninja{  
        YOU {  
            @Override  
            void action() {  
                System.out.println("月读");  
            }  
        },  
        DAITU {  
            @Override  
            void action() {  
                System.out.println("神威");  
            }  
        },  
        KAKAXI {  
            @Override  
            void action() {  
                System.out.println("雷切");  
            }  
        },  
        MINGREN {  
            @Override  
            void action() {  
                System.out.println("螺旋丸");  
            }  
        };  
        abstract void action();  
    }  
  
    EnumSet<Ninja> ninjas = EnumSet.noneOf(Ninja.class);  
    public void add(Ninja ninja) {  
        ninjas.add(ninja);  
    }  
  
    public void ninjasAction() {  
        ninjas.stream().forEach(Ninja::action);  
    }  
  
  
    @Override  
    public String toString() {  
        return ninjas.toString();  
    }  
  
    public static void main(String[] args) {  
        People people = new People();  
        System.out.println(people);  
        people.add(Ninja.DAITU);  
        people.ninjasAction();  
        people.add(Ninja.MINGREN);  
        people.add(Ninja.KAKAXI);  
        people.add(Ninja.YOU);  
        people.add(Ninja.DAITU);  
        System.out.println(people);  
        people.ninjasAction();  
    }  
}
输出 : 
[]
神威
[YOU, DAITU, KAKAXI, MINGREN]
月读
神威
雷切
螺旋丸

添加枚举实例的顺序并不重要–输出顺序由枚举声明的顺序决定。

1.10.1 用枚举实现职责链模式


职责链(Chain of Responsibility)设计模式先创建了一批用于解决目标问题的不同方法,然后将它们连成一条"链"。当一个请求到达时,就会顺着这条链传递下去,直到链上某个可以处理该请求的方法。

这么长的代码写得我心儿都碎了。

package www.com.cat.chapter01;  
  
import java.util.*;  
import java.util.function.Consumer;  
import java.util.stream.Collectors;  
  
@SuppressWarnings("all")  
class Mail {  
    enum Hero {  
        QU, LUXIYA, LUNA, HANYING, QISHI, LUOSAITA;  
    }  
  
    enum JoJo {  
        KIllERQUEEN, STARPLATINUM, THEWORLD, GOLDEXPERIENCE, HEAVENSDOOR;  
    }  
  
    enum Ninja {  
        ZUOZHU, MINGREN, ZILAIYE, WOAILUO, BAN, KAI;  
    }  
  
    enum HaiZei {  
        SUOLONG, LUFEI, SHANZHI, MIHUOKE, BAJI;  
    }  
  
    enum BuLiangRen {  
        LIXINYUN, JIRUXUE, CHIMENG, YUANTIANGANG, LIMANGZHEN;  
    }  
  
  
    Hero hero;  
    JoJo jojo;  
    Ninja ninja;  
    HaiZei haizei;  
    BuLiangRen buliangren;  
    static long counter = 0;  
    long id = counter++;  
  
    @Override  
    public String toString() {  
        return "Mail" + id;  
    }  
  
    public String details() {  
        return "Hero : " + hero + ", jojo : " + jojo + ", ninja : " + ninja + ", haizei : " + haizei + ", buliangren : " + buliangren;  
    }  
  
    // 生成测试邮件  
    public static Mail randomMail() {  
        Mail mail = new Mail();  
        mail.hero = Enums.random(Hero.class);  
        mail.jojo = Enums.random(JoJo.class);  
        mail.ninja = Enums.random(Ninja.class);  
        mail.haizei = Enums.random(HaiZei.class);  
        mail.buliangren = Enums.random(BuLiangRen.class);  
        return mail;  
    }  
  
    public static Iterable<Mail> generator(final int count) {  
        return new Iterable<Mail>() {  
            int total = count;  
  
            @Override  
            public Iterator<Mail> iterator() {  
                return new Iterator<Mail>() {  
                    @Override  
                    public boolean hasNext() {  
                        return total-- > 0;  
                    }  
  
                    // 生成一个随机的护送列表  
                    @Override  
                    public Mail next() {  
                        return randomMail();  
                    }  
                };  
            }  
        };  
    }  
}  
  
@SuppressWarnings("all")  
public class Delivery {  
    enum MailHandler {  
        HeroHan {  
            @Override  
            boolean check(Mail mail) {  
                switch (mail.hero) {  
                    case QU:  
                    case LUNA:  
                    case HANYING:  
                    case QISHI:  
                        return true;  
                    default:  
                        System.out.println("Error : " + mail.hero);  
                        return false;  
                }  
            }  
        },  
        JoJoHan {  
            @Override  
            boolean check(Mail mail) {  
                switch (mail.jojo) {  
                    case STARPLATINUM:  
                    case KIllERQUEEN:  
                    case GOLDEXPERIENCE:  
                    case THEWORLD:  
                        return true;  
                    default:  
                        System.out.println("Error : " + mail.jojo);  
                        return false;  
                }  
            }  
        },  
        NinjaHan {  
            @Override  
            boolean check(Mail mail) {  
                switch (mail.ninja) {  
                    case ZUOZHU:  
                    case MINGREN:  
                    case BAN:  
                    case KAI:  
                        return true;  
                    default:  
                        System.out.println("Error : " + mail.ninja);  
                        return false;  
                }  
            }  
        },  
        HaiZeiHan {  
            @Override  
            boolean check(Mail mail) {  
                switch (mail.haizei) {  
                    case SUOLONG:  
                    case LUFEI:  
                    case SHANZHI:  
                    case MIHUOKE:  
                        return true;  
                    default:  
                        System.out.println("Error : " + mail.haizei);  
                        return false;  
                }  
            }  
        },  
        BuLiangRenHan {  
            @Override  
            boolean check(Mail mail) {  
                switch (mail.buliangren) {  
                    case LIXINYUN:  
                    case JIRUXUE:  
                    case YUANTIANGANG:  
                    case LIMANGZHEN:  
                        return true;  
                    default:  
                        System.out.println("Error : " + mail.buliangren);  
                        return false;  
                }  
            }  
        };  
  
        abstract boolean check(Mail mail);  
    }  
  
    public static void handle(Mail mail) {  
        for (MailHandler handler : MailHandler.values()) {  
            if (!handler.check(mail)) {  
                return;  
            }  
        }  
        System.out.println("Successful!!!");  
    }  
  
    public static void main(String[] args) {  
        for (Mail mail : Mail.generator(10)) {  
            System.out.println(mail.details());  
            handle(mail);  
            System.out.println("*******************************");  
        }  
    }  
}

输出 : 
Hero : QU, jojo : KIllERQUEEN, ninja : ZUOZHU, haizei : SHANZHI, buliangren : JIRUXUE
Successful!!!
*******************************
Hero : LUOSAITA, jojo : KIllERQUEEN, ninja : KAI, haizei : SUOLONG, buliangren : LIXINYUN
Error : LUOSAITA
*******************************
Hero : LUOSAITA, jojo : HEAVENSDOOR, ninja : WOAILUO, haizei : SUOLONG, buliangren : YUANTIANGANG
Error : LUOSAITA
*******************************
Hero : QU, jojo : GOLDEXPERIENCE, ninja : BAN, haizei : BAJI, buliangren : LIMANGZHEN
Error : BAJI
*******************************
Hero : HANYING, jojo : GOLDEXPERIENCE, ninja : KAI, haizei : BAJI, buliangren : YUANTIANGANG
Error : BAJI
*******************************
Hero : LUNA, jojo : HEAVENSDOOR, ninja : ZUOZHU, haizei : LUFEI, buliangren : CHIMENG
Error : HEAVENSDOOR
*******************************
Hero : HANYING, jojo : GOLDEXPERIENCE, ninja : BAN, haizei : SHANZHI, buliangren : JIRUXUE
Successful!!!
*******************************
Hero : LUOSAITA, jojo : STARPLATINUM, ninja : WOAILUO, haizei : SUOLONG, buliangren : CHIMENG
Error : LUOSAITA
*******************************
Hero : QU, jojo : GOLDEXPERIENCE, ninja : ZILAIYE, haizei : SHANZHI, buliangren : JIRUXUE
Error : ZILAIYE
*******************************
Hero : LUNA, jojo : HEAVENSDOOR, ninja : ZILAIYE, haizei : SHANZHI, buliangren : LIMANGZHEN
Error : HEAVENSDOOR
*******************************

其实我写的程序功能比书上稍微强大一点,我的程序会打印出在职责链上是谁没有成功完成任务

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

1.10.2 用枚举实现状态机


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

个人认为这本书最大的缺点就是示例代码给的太长,也太难了。没有基础的话看这么长这么难的代码,看得心都碎了,就算有基础的话,看起来也很累。

package www.com.cat.chapter01;  
  
import java.util.Random;  
  
public enum Input {  
    NICKEL(5), DIME(10), QUARTER(25), DOLLAR(100), TOOTHPASTE(200), CHIPS(75), SODA(100), SOAP(50),  
    ABORT_TRANSACTION {  
        @Override  
        int amount() {  
            throw new RuntimeException("ABORT_TRANSACTION.amount()");  
        }  
    },  
    STOP {  
        @Override  
        int amount() {  
            throw new RuntimeException("STOP.amount()");  
        }  
    };  
    int value;  
  
    Input(int value) {  
        this.value = value;  
    }  
  
    Input() {  
    }  
    int amount() {  
        return value;  
    }  
  
    static Random random = new Random(13);  
  
    public static Input randomSelection() {  
        // 不存在stop  
        return values()[random.nextInt(values().length - 1)];  
    }  
}

下面是一个自动售卖机的例子

我的代码很长,你忍一下

package www.com.cat.chapter01;  
  
import java.io.IOException;  
import java.nio.file.Files;  
import java.nio.file.Path;  
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;  
import java.util.stream.IntStream;  
import java.util.stream.Stream;  
  
@SuppressWarnings("all")  
// 为原来能对自动售卖机的操作分类  
// 使用了Category来达到分类的目的  
enum Category {  
    // 收钱  
    MONEY(Input.DIME, Input.DOLLAR, Input.NICKEL, Input.QUARTER),  
    // 能购买的物品  
    ITEM(Input.TOOTHPASTE, Input.SODA, Input.SOAP, Input.CHIPS),  
    // 退出  
    QUIT(Input.ABORT_TRANSACTION),  
  
    SHUTDOWN(Input.STOP);  
  
    Input[] inputs;  
  
    Category(Input... inputs) {  
        this.inputs = inputs;  
    }  
  
    private static EnumMap<Input, Category> categories = new EnumMap<>(Input.class);  
  
    static {  
//        for (Category category : Category.class.getEnumConstants()) {  
//            for (Input input : category.inputs) {  
//                categories.put(input, category);  
//            }  
//        }  
        // 下面的流操作与上面的for循环功能一致  
        Arrays.stream(Category.class.getEnumConstants()).  
                forEach(category -> Arrays.stream(category.inputs).  
                        forEach(input -> categories.put(input, category)));  
        System.out.println(categories);  
    }  
    // 得到这个input的操作类型  
    public static Category categorize(Input input) {  
        return categories.get(input);  
    }  
}  
  
@SuppressWarnings("all")  
// 下面这两个类都是用来输入指令的,这个使用文件来输入指令  
class FileInputSupplier implements Supplier<Input> {  
    private Iterator<String> input;  
    public FileInputSupplier(String filename) {  
        try {  
            // 文件会以特定的格式存在  
            input = Files.lines(Paths.get(filename)).skip(1).   // 跳过注释行  
                    flatMap(s -> Arrays.stream(s.split(";"))).  // 得到一行的内容,这行有多个指令以;分隔,首先将行以;切割,这会得到多个流,再将流合并  
                    map(String::trim).collect(Collectors.toList()).iterator();  // 去除空格和换行,将每一行的内容都收集到一个List中,再将这个List的迭代器返回。  
        } catch (IOException e) {  
            throw new RuntimeException(e);  
        }  
    }  
    // 获得一个指令  
    @Override  
    public Input get() {  
        // 没有指令就返回null  
        if (!input.hasNext()) {  
            return null;  
        }  
        String next = input.next().trim().replace('-', '_');  
        // 返回一个指令,因为指令是一字符串的形式存在的,所以将其转换成Input类型  
        return Enum.valueOf(Input.class, next);  
    }  
}  
  
class RandomInputSupplier implements Supplier<Input> {  
    @Override  
    public Input get() {  
        // 随机获得一个指令  
        return Input.randomSelection();  
    }  
}  
  
@SuppressWarnings("all")  
// 自动售卖机类  
public class VendingMachine {  
    private static State state = State.RESTING;  
    // amount : 记录客户投进自动售卖机的钱  
    private static int amount = 0;  
    // 将要执行的指令  
    private static Input selection = null;  
    // 是否为瞬态  
    enum StateDuration {  
        TRANSIENT;  
    }  
  
    enum State {  
        // 初始态,此时直接接受投币和退出操作  
        RESTING {  
            @Override  
            void next(Input input) {  
                switch (Category.categorize(input)) {  
                    case MONEY: // 投币后状态转为ADDING_MONEY  
                        amount += input.amount();  
                        state = ADDING_MONEY;  
                        break;  
                    case SHUTDOWN:  
                        state = TERMINAL;  
                    default:    // 其他类型指令不响应  
                }  
            }  
        },  
        // 已投币态  
        ADDING_MONEY {  
            @Override  
            void next(Input input) {  
                // 取出是哪一种状态,究竟是买东西还是给钱还是终止操作  
                switch (Category.categorize(input)) {  
                    case MONEY: // 一次可以投多枚硬币,所以状态不用变仍然保持ADDING_MONEY就好  
                        amount += input.amount();  
                        break;  
                    case ITEM:  // 选择物品  
                        selection = input;   // 买东西,selection指向被选中的物品  
                        if (amount < selection.amount()) {  // 投进去的钱不够,此时还需要投更多的钱,故状态仍然保持不变  
                            System.out.println("Insufficient money for " + selection);  
                        } else {  
                            state = DISPENSING; // 把东西给顾客  
                        }  
                        break;  
                    case QUIT:  // 不买了  
                        state = GIVING_CHANGE;  // 把剩余的钱还给顾客  
                        break;  
                    case SHUTDOWN:  // 关闭  
                        state = TERMINAL;  
                    default:    // 其余的指令不响应  
                }  
            }  
        },  
        // 交货态  
        DISPENSING(StateDuration.TRANSIENT) {  
            @Override  
            void next() {  
                System.out.println("Here is your " + selection);    // 把货物给顾客  
                amount -= selection.amount();   // 还剩下多少钱,在进入这个状态之前就已经确认过钱够不够了(见ADDING_MONEY)  
//                state = GIVING_CHANGE;  // 其实这里不用着急吐钱出来,这样一次就只能买一个物品,显得机器很蠢  
                state = ADDING_MONEY;   // 应该再次回到已投币态,在那里选择退出,或接着投币买更多东西  
            }  
        },  
        // 找零态  
        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) {  
            System.out.println(state);  
            throw new RuntimeException("Only call" + "next(Input input) for non-transient state");  
        }  
  
        void next() {  
            throw new RuntimeException("Only call " + "next() for StateDuration.TRANSIENT states");  
        }  
  
        void output() {  
            System.out.println(amount);  
        }  
    }  
  
    static void run(Supplier<Input> supplier) {  
        while (state != State.TERMINAL) {   // 不是终止态就一直运行  
            state.next(supplier.get()); // state一开始是初始态,此时它从supplier获取一个指令  
            while (state.isTransient) {     // 进入这个状态之后,它不会保持在这个状态  
                state.next();  
            }  
            state.output(); // 打印出当前状态下有多少钱  
        }  
    }  
  
    public static void main(String[] args) {  
        Supplier<Input> supplier = new RandomInputSupplier();  
        if (args.length == 1) {  
            supplier = new FileInputSupplier(args[0]);  
        }  
        run(supplier);  
//        IntStream.rangeClosed(1, 20).forEach(x -> System.out.println(supplier.get()));  
    }  
}

输出 : 
{NICKEL=MONEY, DIME=MONEY, QUARTER=MONEY, DOLLAR=MONEY, TOOTHPASTE=ITEM, CHIPS=ITEM, SODA=ITEM, SOAP=ITEM, ABORT_TRANSACTION=QUIT, STOP=SHUTDOWN}
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

输入文件 : 
// 为了与书上保持一致写的
QUARTER;QUARTER;QUARTER;CHIPS;
DOLLAR;DOLLAR;TOOTHPASTE;
QUARTER;DIME;ABORT-TRANSACTION;
QUARTER;DIME;SODA;
QUARTER;DIME;NICKEL;SODA;
ABORT-TRANSACTION;
STOP;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值