补了一次Thoughtworks的招人题,用了下状态模式,当然你也可以换成职责链~

3012 次阅读 by 九九 2014-05-08 | 标签:Java 玩具

前两天在浪的时候就有看到群友们发了这个链接,当时看的时候觉得这种场景可以用下状态模式或是职责链接的方式来处理,应该会很方便。当时也在浪,所以没什么时间去实现,昨天晚上玩了一下,觉得有些部分还超出了之前的预想,贴粗来让你批斗一下。 先上一下题目:https://www.jinshuju.net/f/EGQL3D/

FizzBuzzWhizz 你是一名体育老师,在某次课距离下课还有五分钟时,你决定搞一个游戏。此时有100名学生在上课。游戏的规则是:

1. 你首先说出三个不同的特殊数,要求必须是个位数,比如3、5、7。
2. 让所有学生拍成一队,然后按顺序报数。
3. 学生报数时,如果所报数字是第一个特殊数(3)的倍数,那么不能说该数字,而要说Fizz;如果所报数字是第二个特殊数(5)的倍数,那么要说Buzz;如果所报数字是第三个特殊数(7)的倍数,那么要说Whizz。
4. 学生报数时,如果所报数字同时是两个特殊数的倍数情况下,也要特殊处理,比如第一个特殊数和第二个特殊数的倍数,那么不能说该数字,而是要说FizzBuzz, 以此类推。如果同时是三个特殊数的倍数,那么要说FizzBuzzWhizz。 5. 学生报数时,如果所报数字包含了第一个特殊数,那么也不能说该数字,而是要说相应的单词,比如本例中第一个特殊数是3,那么要报13的同学应该说Fizz。如果数字中包含了第一个特殊数,那么忽略规则3和规则4,比如要报35的同学只报Fizz,不报BuzzWhizz。
现在,我们需要你完成一个程序来模拟这个游戏,它首先接受3个特殊数,然后输出100名学生应该报数的数或单词。比如,
输入
3,5,7
输出(片段)
1 2 Fizz 4 Buzz Fizz Whizz 8 Fizz Buzz 11 Fizz
Fizz Whizz FizzBuzz 16 17 Fizz 19 Buzz …
一直到100
我的思路,找出所有的状态:
  1. 找出最大权重的状态,分析从第5点得到;
  2. 找出第二权重的状态,分析从第4点中得到;
  3. 找出最弱的权重状态,分析从第3点得到。
找到了状态后,再把这些状态代码化,即:
  开始状态:
package net.hongjuzi.student;

/**
 * 最终的状态
 * 
 * @author 		xjiujiu
 * @date 		2014年5月8日 上午12:08:11 
 * @version 	1.0
 */
public class StartStatus  extends BaseStatus
{

	/**
	 * 最开始Status
	 *
	 * @author xjiujiu 
	 */
	public StartStatus(int [] numbers)
	{
		super(numbers);
	}

	@Override
	public String excute(int loc)
	{
		if(0 < = String.valueOf(loc).indexOf(String.valueOf(_numbers[0]))) {
			return StudentN.words[0];
		}
		
		return new TwoStatus(_numbers).excute(loc);
	}

}
第二种状态:
package net.hongjuzi.student;

/**
 * 第二种状态
 * 
 * @author 		xjiujiu
 * @date 		2014年5月8日 上午12:18:32 
 * @version 	1.0
 */
public class TwoStatus extends BaseStatus
{

	/**
	 * 构造函数
	 *
	 * @author xjiujiu 
	 * @param numbers 需要处理的特殊数字
	 */
	public TwoStatus(int[] numbers)
	{
		super(numbers);
	}

	@Override
	public String excute(int loc)
	{
		StringBuffer result 	= new StringBuffer();
		for(int i = 0; i < 3; i ++) {
			if(loc % _numbers[i] != 0) {
				continue;
			}
			result.append(StudentN.words[i]);
		}
		if(0 < result.length()) {
			return result.toString();
		}
		
		return String.valueOf(loc);
	}

}
所有状态的共有特性抽象父类:
package net.hongjuzi.student;

/**
 * 特殊数字情况
 * 
 * @author 		xjiujiu
 * @date 		2014年5月7日 下午11:56:24 
 * @version 	1.0
 */
abstract public class BaseStatus implements Status
{
		
	/**
	 * 用户定义的三个数
	 */
	protected int [] _numbers;
	
	/**
	 * 构造函数
	 *
	 * @author xjiujiu 
	 */
	public BaseStatus(int [] numbers)
	{
		_numbers 	= numbers;
	}

	public abstract String excute(int loc);

}
为了保证执行的接口一致性,我定义了一个接口约束:
package net.hongjuzi.student;

/**
 * 状态接口
 * 
 * @author 		xjiujiu
 * @date 		2014年5月7日 下午11:54:50 
 * @version 	1.0
 */
public interface Status
{
	/**
	 * 执行状态动作
	 *  
	 * @author xjiujiu 
	 */
	String excute(int loc);
}
完成了状态的实现,再继续完成入口类的实现:
package net.hongjuzi.student;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 学生报数
 * 
 * @author 		xjiujiu
 * @date 		2014年5月7日 下午11:52:11 
 * @version 	1.0
 */
public class StudentN
{
	/**
	 * @Fields 定义好的字符组
	 */
	public final static String [] words 	= new String [] { "Fizz", "Buzz", "Whizz" };
	
	/**
	 * 构造函数
	 *
	 * @author xjiujiu 
	 */
	public StudentN() { }

	/**
	 * 开始执行
	 *  
	 * @author xjiujiu 
	 * @param args 命令行执行得到的变量
	 */
	public static void main(String[] args)
	{
		BufferedReader br 	= new BufferedReader(new InputStreamReader(System.in));
		do {
			try {
				System.out.print("请输入三个数字且用逗号隔开(如:3,5,7):");
				int[] numbers 		= _parseNumbers(br.readLine());
				_doTest(numbers);
			} catch (VerifyException e) {
				System.out.print(e.getMessage());
			} catch (NumberFormatException e) {
				System.out.print("输入有误!请重新输入三个数字且用逗号隔开(如:3,5,7):");
				continue;
			} catch (IOException e) {
				System.out.print("输入有误!请重新输入三个数字且用逗号隔开(如:3,5,7):");
				continue;
			}
		} while(true);
	}

	/**
	 * 解析用户输入的数据
	 *  
	 * @author xjiujiu 
	 * @param str 需要处理的字符串
	 * @return 处理后的数据组
	 * @throws NumberFormatException 数据格式化异常
	 * @throws VerifyException 验证异常
	 */
	protected static int[] _parseNumbers(String str) throws NumberFormatException, VerifyException
	{
		int i 				= 0;
		int [] numbers  	= new int[3];
		for(String number : str.split(",")) {
			numbers[i] 		= Integer.parseInt(number);
			i ++;
		}
		if(3 != i) {
			throw new VerifyException("特殊数字个数不对,只能为3个!(如:3,5,7):");
		}
		
		return numbers;
	}
	
	/**
	 * 执行测试
	 *  
	 * @author xjiujiu 
	 * @param numbers 用户输入的测试数据
	 */
	private static void _doTest(int [] numbers)
	{
		StartStatus startStatus 	= new StartStatus(numbers);
		for(int i = 1; i < = 100; i ++) {
			System.out.println(startStatus.excute(i));
		}
	}

}
这边受马丁花大神的教育,所以代码里尽可能的少行数,以及少if () {} else {} 的东西。 最后我还加了一个自定义的异常,辅助错误提示用:
package net.hongjuzi.student;

/**
 * 验证异常类
 * 
 * @author 		xjiujiu
 * @date 		2014年5月8日 上午12:58:36 
 * @version 	1.0
 */
@SuppressWarnings("serial")
public class VerifyException extends Exception
{

	/**
	 * 构造函数
	 *
	 * @author xjiujiu 
	 */
	public VerifyException()
	{
	}

	public VerifyException(String message)
	{
		super(message);
	}

	public VerifyException(Throwable cause)
	{
		super(cause);
	}

	public VerifyException(String message, Throwable cause)
	{
		super(message, cause);
	}

}
实现基于:jdk1.6 64bit、win8 64bit; PS:状态模式的好处是可以让状态无限,灰常的好扩展,如果这个状态达到了那么当前状态就负责处理当前状态的任务即可,可以不用管其它的部分,如果不是当前状态的内容,那么它可以把任务转效给下面的状态继续执行,一直到找到了对应的状态才结束。 这里让我没有发现的效果是,写完第2个状态,我发现那个for循环的代码可以反“3”所描述的状态也一起都处理了,所以这里的状态由3个也就减少到了2个状态类。 欢迎关注,欢迎拍砖~ :D

评论(3)

Latricia To 九九 (2017-06-14)

Hoje é a segunda vez que você posta algo que serve de in!ipraçãosE é sempre bom isoo pra dar aquele empurrão que a gente precisa.Abç e até amanhã Obs: Tá Dando 6:00 h da tarde e eu vou correr antes que o chefe me chame.Fui

PS:多打字可以减肥哦~234字以内。支持表情:


Top