11个行为模式之6(备忘录模式,观察者模式,状态模式,策略模式,模版模式,访问者模式)
备忘录模式
Sunny软件公司欲开发一款可以运行在Android平台的触摸式中国象棋软件,由于考虑到有些用户是“菜鸟”,经常不小心走错棋;还有些用户因为不习惯使用手指在手机屏幕上拖动棋子,常常出现操作失误,因此该中国象棋软件要提供“悔棋”功能,用户走错棋或操作失误后可恢复到前一个步骤, 如何实现“悔棋”功能是Sunny软件公司开发人员需要面对的一个重要问题,“悔棋”就是让系统恢复到某个历史状态,在很多软件中通常称之为“撤销
- //象棋棋子类:原发器
- class Chessman {
- private String label;
- private int x;
- private int y;
-
- public Chessman(String label,int x,int y) {
- this.label = label;
- this.x = x;
- this.y = y;
- }
-
- public void setLabel(String label) {
- this.label = label;
- }
-
- public void setX(int x) {
- this.x = x;
- }
-
- public void setY(int y) {
- this.y = y;
- }
-
- public String getLabel() {
- return (this.label);
- }
-
- public int getX() {
- return (this.x);
- }
-
- public int getY() {
- return (this.y);
- }
-
- //保存状态
- public ChessmanMemento save() {
- return new ChessmanMemento(this.label,this.x,this.y);
- }
-
- //恢复状态
- public void restore(ChessmanMemento memento) {
- this.label = memento.getLabel();
- this.x = memento.getX();
- this.y = memento.getY();
- }
- }
-
- //象棋棋子备忘录类:备忘录
- class ChessmanMemento {
- private String label;
- private int x;
- private int y;
-
- public ChessmanMemento(String label,int x,int y) {
- this.label = label;
- this.x = x;
- this.y = y;
- }
-
- public void setLabel(String label) {
- this.label = label;
- }
-
- public void setX(int x) {
- this.x = x;
- }
-
- public void setY(int y) {
- this.y = y;
- }
-
- public String getLabel() {
- return (this.label);
- }
-
- public int getX() {
- return (this.x);
- }
-
- public int getY() {
- return (this.y);
- }
- }
-
- //象棋棋子备忘录管理类:负责人
- class MementoCaretaker {
- private ChessmanMemento memento;
-
- public ChessmanMemento getMemento() {
- return memento;
- }
-
- public void setMemento(ChessmanMemento memento) {
- this.memento = memento;
- }
- }
编写如下客户端测试代码:
- class Client {
- public static void main(String args[]) {
- MementoCaretaker mc = new MementoCaretaker();
- Chessman chess = new Chessman("车",1,1);
- display(chess);
- mc.setMemento(chess.save()); //保存状态
- chess.setY(4);
- display(chess);
- mc.setMemento(chess.save()); //保存状态
- display(chess);
- chess.setX(5);
- display(chess);
- System.out.println("******悔棋******");
- chess.restore(mc.getMemento()); //恢复状态
- display(chess);
- }
-
- public static void display(Chessman chess) {
- System.out.println("棋子" + chess.getLabel() + "当前位置为:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");
- }
- }
多次撤销?
- import java.util.*;
-
- class MementoCaretaker {
- //定义一个集合来存储多个备忘录
- private ArrayList mementolist = new ArrayList();
-
- public ChessmanMemento getMemento(int i) {
- return (ChessmanMemento)mementolist.get(i);
- }
-
- public void setMemento(ChessmanMemento memento) {
- mementolist.add(memento);
- }
- }
编写如下客户端测试代码:
- class Client {
- private static int index = -1; //定义一个索引来记录当前状态所在位置
- private static MementoCaretaker mc = new MementoCaretaker();
-
- public static void main(String args[]) {
- Chessman chess = new Chessman("车",1,1);
- play(chess);
- chess.setY(4);
- play(chess);
- chess.setX(5);
- play(chess);
- undo(chess,index);
- undo(chess,index);
- redo(chess,index);
- redo(chess,index);
- }
-
- //下棋
- public static void play(Chessman chess) {
- mc.setMemento(chess.save()); //保存备忘录
- index ++;
- System.out.println("棋子" + chess.getLabel() + "当前位置为:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");
- }
-
- //悔棋
- public static void undo(Chessman chess,int i) {
- System.out.println("******悔棋******");
- index --;
- chess.restore(mc.getMemento(i-1)); //撤销到上一个备忘录
- System.out.println("棋子" + chess.getLabel() + "当前位置为:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");
- }
-
- //撤销悔棋
- public static void redo(Chessman chess,int i) {
- System.out.println("******撤销悔棋******");
- index ++;
- chess.restore(mc.getMemento(i+1)); //恢复到下一个备忘录
- System.out.println("棋子" + chess.getLabel() + "当前位置为:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");
- }
- }
观察者模式
Sunny软件公司欲开发一款多人联机对战游戏(类似魔兽世界、星际争霸等游戏),在该游戏中,多个玩家可以加入同一战队组成联盟,当战队中某一成员受到敌人攻击时将给所有其他盟友发送通知,盟友收到通知后将作出响应。
Sunny软件公司开发人员需要提供一个设计方案来实现战队成员之间的联动。
- import java.util.*;
-
- //抽象观察类
- interface Observer {
- public String getName();
- public void setName(String name);
- public void help(); //声明支援盟友方法
- public void beAttacked(AllyControlCenter acc); //声明遭受攻击方法
- }
-
- //战队成员类:具体观察者类
- class Player implements Observer {
- private String name;
-
- public Player(String name) {
- this.name = name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getName() {
- return this.name;
- }
-
- //支援盟友方法的实现
- public void help() {
- System.out.println("坚持住," + this.name + "来救你!");
- }
-
- //遭受攻击方法的实现,当遭受攻击时将调用战队控制中心类的通知方法notifyObserver()来通知盟友
- public void beAttacked(AllyControlCenter acc) {
- System.out.println(this.name + "被攻击!");
- acc.notifyObserver(name);
- }
- }
-
- //战队控制中心类:目标类
- abstract class AllyControlCenter {
- protected String allyName; //战队名称
- protected ArrayList<Observer> players = new ArrayList<Observer>(); //定义一个集合用于存储战队成员
-
- public void setAllyName(String allyName) {
- this.allyName = allyName;
- }
-
- public String getAllyName() {
- return this.allyName;
- }
-
- //注册方法
- public void join(Observer obs) {
- System.out.println(obs.getName() + "加入" + this.allyName + "战队!");
- players.add(obs);
- }
-
- //注销方法
- public void quit(Observer obs) {
- System.out.println(obs.getName() + "退出" + this.allyName + "战队!");
- players.remove(obs);
- }
-
- //声明抽象通知方法
- public abstract void notifyObserver(String name);
- }
-
- //具体战队控制中心类:具体目标类
- class ConcreteAllyControlCenter extends AllyControlCenter {
- public ConcreteAllyControlCenter(String allyName) {
- System.out.println(allyName + "战队组建成功!");
- System.out.println("----------------------------");
- this.allyName = allyName;
- }
-
- //实现通知方法
- public void notifyObserver(String name) {
- System.out.println(this.allyName + "战队紧急通知,盟友" + name + "遭受敌人攻击!");
- //遍历观察者集合,调用每一个盟友(自己除外)的支援方法
- for(Object obs : players) {
- if (!((Observer)obs).getName().equalsIgnoreCase(name)) {
- ((Observer)obs).help();
- }
- }
- }
- }
编写如下客户端测试代码:
[java] view plain copy
- class Client {
- public static void main(String args[]) {
- //定义观察目标对象
- AllyControlCenter acc;
- acc = new ConcreteAllyControlCenter("金庸群侠");
-
- //定义四个观察者对象
- Observer player1,player2,player3,player4;
-
- player1 = new Player("杨过");
- acc.join(player1);
-
- player2 = new Player("令狐冲");
- acc.join(player2);
-
- player3 = new Player("张无忌");
- acc.join(player3);
-
- player4 = new Player("段誉");
- acc.join(player4);
-
- //某成员遭受攻击
- Player1.beAttacked(acc);
- }
- }
状态模式
Sunny软件公司欲为某银行开发一套信用卡业务系统,银行账户(Account)是该系统的核心类之一,通过分析,Sunny软件公司开发人员发现在该系统中,账户存在三种状态,且在不同状态下账户存在不同的行为,具体说明如下:
(1) 如果账户中余额大于等于0,则账户的状态为正常状态(Normal State),此时用户既可以向该账户存款也可以从该账户取款;
(2) 如果账户中余额小于0,并且大于-2000,则账户的状态为透支状态(Overdraft State),此时用户既可以向该账户存款也可以从该账户取款,但需要按天计算利息;
(3) 如果账户中余额等于-2000,那么账户的状态为受限状态(Restricted State),此时用户只能向该账户存款,不能再从中取款,同时也将按天计算利息;
(4) 根据余额的不同,以上三种状态可发生相互转换。
实现代码:
[java] view plain copy
- class Account {
- private String state; //状态
- private int balance; //余额
- ......
-
- //存款操作
- public void deposit() {
- //存款
- stateCheck();
- }
-
- //取款操作
- public void withdraw() {
- if (state.equalsIgnoreCase("NormalState") || state.equalsIgnoreCase("OverdraftState ")) {
- //取款
- stateCheck();
- }
- else {
- //取款受限
- }
- }
-
- //计算利息操作
- public void computeInterest() {
- if(state.equalsIgnoreCase("OverdraftState") || state.equalsIgnoreCase("RestrictedState ")) {
- //计算利息
- }
- }
-
- //状态检查和转换操作
- public void stateCheck() {
- if (balance >= 0) {
- state = "NormalState";
- }
- else if (balance > -2000 && balance < 0) {
- state = "OverdraftState";
- }
- else if (balance == -2000) {
- state = "RestrictedState";
- }
- else if (balance < -2000) {
- //操作受限
- }
- }
- ......
- }
分析上述代码,我们不难发现存在如下几个问题:
(1) 几乎每个方法中都包含状态判断语句,以判断在该状态下是否具有该方法以及在特定状态下该方法如何实现,导致代码非常冗长,可维护性较差;
(2) 拥有一个较为复杂的stateCheck()方法,包含大量的if…else if…else…语句用于进行状态转换,代码测试难度较大,且不易于维护;
(3) 系统扩展性较差,如果需要增加一种新的状态,如冻结状态(Frozen State,在该状态下既不允许存款也不允许取款),需要对原有代码进行大量修改,扩展起来非常麻烦。
为了解决这些问题,我们可以使用状态模式,在状态模式中,我们将对象在每一个状态下的行为和状态转移语句封装在一个个状态类中,通过这些状态类来分散冗长的条件转移语句,让系统具有更好的灵活性和可扩展性,状态模式可以在一定程度上解决上述问题。
- //银行账户:环境类
- class Account {
- private AccountState state; //维持一个对抽象状态对象的引用
- private String owner; //开户名
- private double balance = 0; //账户余额
-
- public Account(String owner,double init) {
- this.owner = owner;
- this.balance = balance;
- this.state = new NormalState(this); //设置初始状态
- System.out.println(this.owner + "开户,初始金额为" + init);
- System.out.println("---------------------------------------------");
- }
-
- public double getBalance() {
- return this.balance;
- }
-
- public void setBalance(double balance) {
- this.balance = balance;
- }
-
- public void setState(AccountState state) {
- this.state = state;
- }
-
- public void deposit(double amount) {
- System.out.println(this.owner + "存款" + amount);
- state.deposit(amount); //调用状态对象的deposit()方法
- System.out.println("现在余额为"+ this.balance);
- System.out.println("现在帐户状态为"+ this.state.getClass().getName());
- System.out.println("---------------------------------------------");
- }
-
- public void withdraw(double amount) {
- System.out.println(this.owner + "取款" + amount);
- state.withdraw(amount); //调用状态对象的withdraw()方法
- System.out.println("现在余额为"+ this.balance);
- System.out.println("现在帐户状态为"+ this. state.getClass().getName());
- System.out.println("---------------------------------------------");
- }
-
- public void computeInterest()
- {
- state.computeInterest(); //调用状态对象的computeInterest()方法
- }
- }
-
- //抽象状态类
- abstract class AccountState {
- protected Account acc;
- public abstract void deposit(double amount);
- public abstract void withdraw(double amount);
- public abstract void computeInterest();
- public abstract void stateCheck();
- }
-
- //正常状态:具体状态类
- class NormalState extends AccountState {
- public NormalState(Account acc) {
- this.acc = acc;
- }
-
- public NormalState(AccountState state) {
- this.acc = state.acc;
- }
-
- public void deposit(double amount) {
- acc.setBalance(acc.getBalance() + amount);
- stateCheck();
- }
-
- public void withdraw(double amount) {
- acc.setBalance(acc.getBalance() - amount);
- stateCheck();
- }
-
- public void computeInterest()
- {
- System.out.println("正常状态,无须支付利息!");
- }
-
- //状态转换
- public void stateCheck() {
- if (acc.getBalance() > -2000 && acc.getBalance() <= 0) {
- acc.setState(new OverdraftState(this));
- }
- else if (acc.getBalance() == -2000) {
- acc.setState(new RestrictedState(this));
- }
- else if (acc.getBalance() < -2000) {
- System.out.println("操作受限!");
- }
- }
- }
-
- //透支状态:具体状态类
- class OverdraftState extends AccountState
- {
- public OverdraftState(AccountState state) {
- this.acc = state.acc;
- }
-
- public void deposit(double amount) {
- acc.setBalance(acc.getBalance() + amount);
- stateCheck();
- }
-
- public void withdraw(double amount) {
- acc.setBalance(acc.getBalance() - amount);
- stateCheck();
- }
-
- public void computeInterest() {
- System.out.println("计算利息!");
- }
-
- //状态转换
- public void stateCheck() {
- if (acc.getBalance() > 0) {
- acc.setState(new NormalState(this));
- }
- else if (acc.getBalance() == -2000) {
- acc.setState(new RestrictedState(this));
- }
- else if (acc.getBalance() < -2000) {
- System.out.println("操作受限!");
- }
- }
- }
-
- //受限状态:具体状态类
- class RestrictedState extends AccountState {
- public RestrictedState(AccountState state) {
- this.acc = state.acc;
- }
-
- public void deposit(double amount) {
- acc.setBalance(acc.getBalance() + amount);
- stateCheck();
- }
-
- public void withdraw(double amount) {
- System.out.println("帐号受限,取款失败");
- }
-
- public void computeInterest() {
- System.out.println("计算利息!");
- }
-
- //状态转换
- public void stateCheck() {
- if(acc.getBalance() > 0) {
- acc.setState(new NormalState(this));
- }
- else if(acc.getBalance() > -2000) {
- acc.setState(new OverdraftState(this));
- }
- }
- }
编写如下客户端测试代码:
- class Client {
- public static void main(String args[]) {
- Account acc = new Account("段誉",0.0);
- acc.deposit(1000);
- acc.withdraw(2000);
- acc.deposit(3000);
- acc.withdraw(4000);
- acc.withdraw(1000);
- acc.computeInterest();
- }
- }
策略模式
Sunny软件公司为某电影院开发了一套影院售票系统,在该系统中需要为不同类型的用户提供不同的电影票打折方式,具体打折方案如下:
(1) 学生凭学生证可享受票价8折优惠;
(2) 年龄在10周岁及以下的儿童可享受每张票减免10元的优惠(原始票价需大于等于20元);
(3) 影院VIP用户除享受票价半价优惠外还可进行积分,积分累计到一定额度可换取电影院赠送的奖品。
该系统在将来可能还要根据需要引入新的打折方式。
一般实现:
- //电影票类
- class MovieTicket {
- private double price; //电影票价格
- private String type; //电影票类型
-
- public void setPrice(double price) {
- this.price = price;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-
- public double getPrice() {
- return this.calculate();
- }
-
- //计算打折之后的票价
- public double calculate() {
- //学生票折后票价计算
- if(this.type.equalsIgnoreCase("student")) {
- System.out.println("学生票:");
- return this.price * 0.8;
- }
- //儿童票折后票价计算
- else if(this.type.equalsIgnoreCase("children") && this.price >= 20 ) {
- System.out.println("儿童票:");
- return this.price - 10;
- }
- //VIP票折后票价计算
- else if(this.type.equalsIgnoreCase("vip")) {
- System.out.println("VIP票:");
- System.out.println("增加积分!");
- return this.price * 0.5;
- }
- else {
- return this.price; //如果不满足任何打折要求,则返回原始票价
- }
- }
- }
编写如下客户端测试代码:
- class Client {
- public static void main(String args[]) {
- MovieTicket mt = new MovieTicket();
- double originalPrice = 60.0; //原始票价
- double currentPrice; //折后价
-
- mt.setPrice(originalPrice);
- System.out.println("原始价为:" + originalPrice);
- System.out.println("---------------------------------");
-
- mt.setType("student"); //学生票
- currentPrice = mt.getPrice();
- System.out.println("折后价为:" + currentPrice);
- System.out.println("---------------------------------");
-
- mt.setType("children"); //儿童票
- currentPrice = mt.getPrice();
- System.out.println("折后价为:" + currentPrice);
- }
- }
- //电影票类:环境类
- class MovieTicket {
- private double price;
- private Discount discount; //维持一个对抽象折扣类的引用
-
- public void setPrice(double price) {
- this.price = price;
- }
-
- //注入一个折扣类对象
- public void setDiscount(Discount discount) {
- this.discount = discount;
- }
-
- public double getPrice() {
- //调用折扣类的折扣价计算方法
- return discount.calculate(this.price);
- }
- }
-
- //折扣类:抽象策略类
- interface Discount {
- public double calculate(double price);
- }
-
- //学生票折扣类:具体策略类
- class StudentDiscount implements Discount {
- public double calculate(double price) {
- System.out.println("学生票:");
- return price * 0.8;
- }
- }
-
- //儿童票折扣类:具体策略类
- class ChildrenDiscount implements Discount {
- public double calculate(double price) {
- System.out.println("儿童票:");
- return price - 10;
- }
- }
-
- //VIP会员票折扣类:具体策略类
- class VIPDiscount implements Discount {
- public double calculate(double price) {
- System.out.println("VIP票:");
- System.out.println("增加积分!");
- return price * 0.5;
- }
- }
- import javax.xml.parsers.*;
- import org.w3c.dom.*;
- import org.xml.sax.SAXException;
- import java.io.*;
- class XMLUtil {
- //该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
- public static Object getBean() {
- try {
- //创建文档对象
- DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
- DocumentBuilder builder = dFactory.newDocumentBuilder();
- Document doc;
- doc = builder.parse(new File("config.xml"));
-
- //获取包含类名的文本节点
- NodeList nl = doc.getElementsByTagName("className");
- Node classNode=nl.item(0).getFirstChild();
- String cName=classNode.getNodeValue();
-
- //通过类名生成实例对象并将其返回
- Class c=Class.forName(cName);
- Object obj=c.newInstance();
- return obj;
- }
- catch(Exception e) {
- e.printStackTrace();
- return null;
- }
- }
- }
在配置文件config.xml中存储了具体策略类的类名,代码如下所示:
[html] view plain copy
- <?xml version="1.0"?>
- <config>
- <className>StudentDiscount</className>
- </config>
编写如下客户端测试代码:
- class Client {
- public static void main(String args[]) {
- MovieTicket mt = new MovieTicket();
- double originalPrice = 60.0;
- double currentPrice;
-
- mt.setPrice(originalPrice);
- System.out.println("原始价为:" + originalPrice);
- System.out.println("---------------------------------");
-
- Discount discount;
- discount = (Discount)XMLUtil.getBean(); //读取配置文件并反射生成具体折扣对象
- mt.setDiscount(discount); //注入折扣对象
-
- currentPrice = mt.getPrice();
- System.out.println("折后价为:" + currentPrice);
- }
- }
模板方法模式
在现实生活中,很多事情都包含几个实现步骤,例如请客吃饭,无论吃什么,一般都包含点单、吃东西、买单等几个步骤,通常情况下这几个步骤的次序是:点单 --> 吃东西 --> 买单。在这三个步骤中,点单和买单大同小异,最大的区别在于第二步——吃什么?吃面条和吃满汉全席可大不相同,在软件开发中,有时也会遇到类似的情况,某个方法的实现需要多个步骤(类似“请客”),其中有些步骤是固定的(类似“点单”和“买单”),而有些步骤并不固定,存在可变性(类似“吃东西”),为了提高代码的复用性和系统的灵活性,可以使用一种称之为模板方法模式的设计模式来对这类情况进行设计
访问者模式
Sunny软件公司欲为某银行开发一套OA系统,在该OA系统中包含一个员工信息管理子系统,该银行员工包括正式员工和临时工,每周人力资源部和财务部等部门需要对员工数据进行汇总,汇总数据包括员工工作时间、员工工资等。该公司基本制度如下:
(1) 正式员工(Full time Employee)每周工作时间为40小时,不同级别、不同部门的员工每周基本工资不同;如果超过40小时,超出部分按照100元/小时作为加班费;如果少于40小时,所缺时间按照请假处理,请假所扣工资以80元/小时计算,直到基本工资扣除到零为止。除了记录实际工作时间外,人力资源部需记录加班时长或请假时长,作为员工平时表现的一项依据。
(2) 临时工(Part time Employee)每周工作时间不固定,基本工资按小时计算,不同岗位的临时工小时工资不同。人力资源部只需记录实际工作时间。
人力资源部和财务部工作人员可以根据各自的需要对员工数据进行汇总处理,人力资源部负责汇总每周员工工作时间,而财务部负责计算每周员工工资。
- import java.util.*;
-
- class EmployeeList
- {
- private ArrayList<Employee> list = new ArrayList<Employee>(); //员工集合
-
- //增加员工
- public void addEmployee(Employee employee)
- {
- list.add(employee);
- }
-
- //处理员工数据
- public void handle(String departmentName)
- {
- if(departmentName.equalsIgnoreCase("财务部")) //财务部处理员工数据
- {
- for(Object obj : list)
- {
- if(obj.getClass().getName().equalsIgnoreCase("FulltimeEmployee"))
- {
- System.out.println("财务部处理全职员工数据!");
- }
- else
- {
- System.out.println("财务部处理兼职员工数据!");
- }
- }
- }
- else if(departmentName.equalsIgnoreCase("人力资源部")) //人力资源部处理员工数据
- {
- for(Object obj : list)
- {
- if(obj.getClass().getName().equalsIgnoreCase("FulltimeEmployee"))
- {
- System.out.println("人力资源部处理全职员工数据!");
- }
- else
- {
- System.out.println("人力资源部处理兼职员工数据!");
- }
- }
- }
- }
- }
- import java.util.*;
-
- //员工类:抽象元素类
- interface Employee
- {
- public void accept(Department handler); //接受一个抽象访问者访问
- }
-
- //全职员工类:具体元素类
- class FulltimeEmployee implements Employee
- {
- private String name;
- private double weeklyWage;
- private int workTime;
-
- public FulltimeEmployee(String name,double weeklyWage,int workTime)
- {
- this.name = name;
- this.weeklyWage = weeklyWage;
- this.workTime = workTime;
- }
-
- public void setName(String name)
- {
- this.name = name;
- }
-
- public void setWeeklyWage(double weeklyWage)
- {
- this.weeklyWage = weeklyWage;
- }
-
- public void setWorkTime(int workTime)
- {
- this.workTime = workTime;
- }
-
- public String getName()
- {
- return (this.name);
- }
-
- public double getWeeklyWage()
- {
- return (this.weeklyWage);
- }
-
- public int getWorkTime()
- {
- return (this.workTime);
- }
-
- public void accept(Department handler)
- {
- handler.visit(this); //调用访问者的访问方法
- }
- }
-
- //兼职员工类:具体元素类
- class ParttimeEmployee implements Employee
- {
- private String name;
- private double hourWage;
- private int workTime;
-
- public ParttimeEmployee(String name,double hourWage,int workTime)
- {
- this.name = name;
- this.hourWage = hourWage;
- this.workTime = workTime;
- }
-
- public void setName(String name)
- {
- this.name = name;
- }
-
- public void setHourWage(double hourWage)
- {
- this.hourWage = hourWage;
- }
-
- public void setWorkTime(int workTime)
- {
- this.workTime = workTime;
- }
-
- public String getName()
- {
- return (this.name);
- }
-
- public double getHourWage()
- {
- return (this.hourWage);
- }
-
- public int getWorkTime()
- {
- return (this.workTime);
- }
-
- public void accept(Department handler)
- {
- handler.visit(this); //调用访问者的访问方法
- }
- }
-
- //部门类:抽象访问者类
- abstract class Department
- {
- //声明一组重载的访问方法,用于访问不同类型的具体元素
- public abstract void visit(FulltimeEmployee employee);
- public abstract void visit(ParttimeEmployee employee);
- }
-
- //财务部类:具体访问者类
- class FADepartment extends Department
- {
- //实现财务部对全职员工的访问
- public void visit(FulltimeEmployee employee)
- {
- int workTime = employee.getWorkTime();
- double weekWage = employee.getWeeklyWage();
- if(workTime > 40)
- {
- weekWage = weekWage + (workTime - 40) * 100;
- }
- else if(workTime < 40)
- {
- weekWage = weekWage - (40 - workTime) * 80;
- if(weekWage < 0)
- {
- weekWage = 0;
- }
- }
- System.out.println("正式员工" + employee.getName() + "实际工资为:" + weekWage + "元。");
- }
-
- //实现财务部对兼职员工的访问
- public void visit(ParttimeEmployee employee)
- {
- int workTime = employee.getWorkTime();
- double hourWage = employee.getHourWage();
- System.out.println("临时工" + employee.getName() + "实际工资为:" + workTime * hourWage + "元。");
- }
- }
-
- //人力资源部类:具体访问者类
- class HRDepartment extends Department
- {
- //实现人力资源部对全职员工的访问
- public void visit(FulltimeEmployee employee)
- {
- int workTime = employee.getWorkTime();
- System.out.println("正式员工" + employee.getName() + "实际工作时间为:" + workTime + "小时。");
- if(workTime > 40)
- {
- System.out.println("正式员工" + employee.getName() + "加班时间为:" + (workTime - 40) + "小时。");
- }
- else if(workTime < 40)
- {
- System.out.println("正式员工" + employee.getName() + "请假时间为:" + (40 - workTime) + "小时。");
- }
- }
-
- //实现人力资源部对兼职员工的访问
- public void visit(ParttimeEmployee employee)
- {
- int workTime = employee.getWorkTime();
- System.out.println("临时工" + employee.getName() + "实际工作时间为:" + workTime + "小时。");
- }
- }
-
- //员工列表类:对象结构
- class EmployeeList
- {
- //定义一个集合用于存储员工对象
- private ArrayList<Employee> list = new ArrayList<Employee>();
-
- public void addEmployee(Employee employee)
- {
- list.add(employee);
- }
-
- //遍历访问员工集合中的每一个员工对象
- public void accept(Department handler)
- {
- for(Object obj : list)
- {
- ((Employee)obj).accept(handler);
- }
- }
- }
为了提高系统的灵活性和可扩展性,我们将具体访问者类的类名存储在配置文件中,并通过工具类XMLUtil来读取配置文件并反射生成对象,XMLUtil类的代码如下所示:
[java] view plain copy
- import javax.xml.parsers.*;
- import org.w3c.dom.*;
- import org.xml.sax.SAXException;
- import java.io.*;
- class XMLUtil
- {
- //该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
- public static Object getBean()
- {
- try
- {
- //创建文档对象
- DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
- DocumentBuilder builder = dFactory.newDocumentBuilder();
- Document doc;
- doc = builder.parse(new File("config.xml"));
-
- //获取包含类名的文本节点
- NodeList nl = doc.getElementsByTagName("className");
- Node classNode=nl.item(0).getFirstChild();
- String cName=classNode.getNodeValue();
-
- //通过类名生成实例对象并将其返回
- Class c=Class.forName(cName);
- Object obj=c.newInstance();
- return obj;
- }
- catch(Exception e)
- {
- e.printStackTrace();
- return null;
- }
- }
- }
配置文件config.xml中存储了具体访问者类的类名,代码如下所示:
[html] view plain copy
- <?xml version="1.0"?>
- <config>
- <className>FADepartment</className>
- </config>
编写如下客户端测试代码:
[java] view plain copy
- class Client
- {
- public static void main(String args[])
- {
- EmployeeList list = new EmployeeList();
- Employee fte1,fte2,fte3,pte1,pte2;
-
- fte1 = new FulltimeEmployee("张无忌",3200.00,45);
- fte2 = new FulltimeEmployee("杨过",2000.00,40);
- fte3 = new FulltimeEmployee("段誉",2400.00,38);
- pte1 = new ParttimeEmployee("洪七公",80.00,20);
- pte2 = new ParttimeEmployee("郭靖",60.00,18);
-
- list.addEmployee(fte1);
- list.addEmployee(fte2);
- list.addEmployee(fte3);
- list.addEmployee(pte1);
- list.addEmployee(pte2);
-
- Department dep;
- dep = (Department)XMLUtil.getBean();
- list.accept(dep);
- }
- }