今天在网上看到FizzBuzzWhizz这个代码挑战啊。觉得很有意思。开始没有看其他人的文章。写完之后,好像大家都是从算法的角度去解决问题啊。我却是一开始从设计的角度去解决问题。不知道他们出这道题到底是要考验算法呢?还是面向对象的设计呢?但是我看要求里写了要体现超赞的面向对象设计啊。而且我看了他们公司,也是一个以设计专长的公司。是要考虑算法的问题吗?
首先,我看到这道题。现在是有三种特殊数字。分别是Fizz、Buzz、和WHizz。这就是对应三个类吧。至于当某一个数字即是第一个数的倍数又是第二个数的倍数时,就要调用Fizz类和Buzz类来处理。所以我想。某一个数字要走的流程应该是先从Fizz类处理事件,再到Buzz类处理事件,最后到Whizz类处理。是穿透的。这样的流程,类似于行为型的设计模式吧。有哪些,脑子里应该有个大概的轮廓了。可能有责任链模式、装饰者模式、状态模式等。当然这些东西不重要。重要的是思想,不能死套用模式。本来想用类似于事件机制来实现的。可是发现,这样一来,Fizz、Buzz这些类就跟事件类耦合了。这样不好。并且,我想他们要考察的不是完成他们明文写的需求上的东西吧。更要考虑的是以后那些可以预见的需求变化。比如说,我可能这个时候不要输出字符了,我要进行别的操作。又或者是我要增加一个特殊数字。变成4个。这样一来,那么写算法的同学们是不是会非常的苦恼呢?至于,我用的什么模式,我只想说,我不清楚。我做设计的时候,从来不从模式出发,我是从灵活度和扩展性出发。如果现有代码不能满足可预见的变化。那就改成可以适应变化的。所以,代码的设计,其实并不是一种设计模式就能搞定的。是设计思想的融合。
我的思路是,将某个数字被某个特殊数字捕捉到要处理的情况。原先是直接输出字符。我想以后可能会不光是输出字符。所以,我将这部分的行为放到了外面。通过委托的方式调用。而且,考虑到这个题目本身就是输出字符,如果,我要在外部定义委托。那么对外的接口,就会变得复杂。我希望的是,对外的接口变得简单。就类似于外观模式。
在考虑到,以后如果要增加一个特殊数字,我希望能自由的组合这些流程。所以,就有了类似于装饰着模式或者责任链模式的设计。
/// <summary> /// 第二个特殊数字类 /// </summary> public class Buzz : IFizzBuzzWhizz { /// <summary> /// 创建第二个特殊数字处理类实例 /// </summary> /// <param name="specialNumber">特殊数字</param> /// <param name="nextFlow">下一个流程,这里指第三个特殊字处理类</param> /// <param name="multipleAction">当某个数字被这个类Handle的时候,要执行的事件(这里默认是如果某个数字是这个特殊数字的倍数,就在控制台输出一段字符)</param> public Buzz(int specialNumber, IFizzBuzzWhizz nextFlow, Action<int, bool> multipleAction) { this.SpecialNumber = specialNumber; this.NextFlow = nextFlow; this.MultipleAction = multipleAction; } public Buzz(int specialNumber, Action<int, bool> multipleAction) : this(specialNumber, new Whizz(7), multipleAction) { } public Buzz(int specialNumber) : this(specialNumber, new Whizz(7), (number, isHandle) => Console.Write("{0}Buzz", isHandle ? string.Empty : " ")) { } /// <summary> /// 当某个数字是这个特殊数字的倍数的时候,要执行的事件 /// </summary> public Action<int, bool> MultipleAction { get; set; } /// <summary> /// 特殊数字 /// </summary> public int SpecialNumber { get; set; } /// <summary> /// 下一个流程,这里指第三个特殊数字处理类 /// </summary> public IFizzBuzzWhizz NextFlow { get; set; } /// <summary> /// 处理方法 /// </summary> /// <param name="number">数字</param> /// <param name="isHandle">是否被处理过</param> public void Process(int number, bool isHandle) { //如果当前的数字是这个特殊数字的倍数,就执行事件。并且标记isHandle为True,传递到下一个流程。 if (number % this.SpecialNumber == 0) { if (this.MultipleAction != null) this.MultipleAction(number, isHandle); isHandle = true; } if (this.NextFlow != null) this.NextFlow.Process(number, isHandle); } }
/// <summary> /// 特殊数字第一个类 /// </summary> public class Fizz : IFizzBuzzWhizz { /// <summary> /// 构造一个第一个特殊数字的处理类实例 /// </summary> /// <param name="specialNumber">特殊数字</param> /// <param name="nextFlow">下一个流程,这里值第二个特殊数字处理类实例</param> /// <param name="containsSpecialNumberAction">当某个数字包含当前的特殊数字时,要执行的事件,这里默认是输出一段字符</param> /// <param name="multipleAction">当某个数字是当前特殊数字的倍数时,要执行的事件</param> public Fizz(int specialNumber, IFizzBuzzWhizz nextFlow, Action<int> containsSpecialNumberAction, Action<int, bool> multipleAction) { this.SpecialNumber = specialNumber; this.NextFlow = nextFlow; this.ContainsSpecialNumberAction = containsSpecialNumberAction; this.MultipleAction = multipleAction; } public Fizz(int specialNumber, Action<int> containsSpecialNumberAction, Action<int, bool> multipleAction) : this(specialNumber, new Buzz(5), containsSpecialNumberAction, multipleAction) { } public Fizz(int specialNumber) : this(specialNumber, new Buzz(5), delegate { Console.Write(" Fizz"); }, (number, isHandle) => Console.Write(" Fizz")) { } /// <summary> /// 当某个数字包含当前的特殊数字时,要执行的事件 /// </summary> public Action<int> ContainsSpecialNumberAction { get; set; } /// <summary> /// 当某个数字是当前特殊数字的倍数时,要执行的事件 /// </summary> public Action<int, bool> MultipleAction { get; set; } /// <summary> /// 特殊数字 /// </summary> public int SpecialNumber { get; set; } /// <summary> /// 下一个特殊数字处理类,这个默认值第二个特殊数字处理类 /// </summary> public IFizzBuzzWhizz NextFlow { get; set; } /// <summary> /// 处理特殊数字 /// </summary> /// <param name="number">数字</param> /// <param name="isHandle">是否被处理</param> public void Process(int number, bool isHandle = false) { //如果某个数字包含当前的特殊数字,那么就执行当某个数字包含当前特殊数字的事件,并且。终止往后调用。 if (number.ToString(CultureInfo.InvariantCulture).Contains(this.SpecialNumber.ToString(CultureInfo.InvariantCulture))) { if (this.ContainsSpecialNumberAction != null) this.ContainsSpecialNumberAction(number); return; } //如果某个数字是当前特殊数字的倍数的时候,就执行是倍数时要执行的事件。并且标记ishandle为true。传递到下一个流程 if (number % this.SpecialNumber == 0) { if (this.MultipleAction != null) this.MultipleAction(number, isHandle); isHandle = true; } //调用下一个流程的方法 if (this.NextFlow != null) this.NextFlow.Process(number, isHandle); } }
/// <summary> /// 第三个特殊数字处理类 /// </summary> public class Whizz : IFizzBuzzWhizz { /// <summary> /// 构造一个第三个特殊数字处理类 /// </summary> /// <param name="specialNumber">特殊数字</param> /// <param name="nextFlow">下一个流程,这里默认是null,因为目前就三种情况</param> /// <param name="multipleAction">当某个数字是特殊数字的倍数时,所要触发的事件</param> /// <param name="neverHandleAction">当从来没用被任何一个特殊数字类处理过的时候要执行事件(这里就是指,这个数字既不是第一个特殊数字的倍数,也不是第二个特殊数字的倍数,更不是第三个特殊数字的倍数)</param> public Whizz(int specialNumber, IFizzBuzzWhizz nextFlow, Action<int, bool> multipleAction, Action<int, bool> neverHandleAction) { this.SpecialNumber = specialNumber; this.NextFlow = nextFlow; this.MultipleAction = multipleAction; this.NeverHandleAction = neverHandleAction; } public Whizz(int specialNumber, Action<int, bool> multipleAction, Action<int, bool> neverHandleAction) : this(specialNumber, default(IFizzBuzzWhizz), multipleAction, neverHandleAction) { } public Whizz(int specialNumber) : this(specialNumber, default(IFizzBuzzWhizz), (number, isHandle) => Console.Write("{0}Whizz", isHandle ? string.Empty : " "), (number, isHandle) => Console.Write("{0}{1}", isHandle ? string.Empty : " ", number)) { } public Action<int, bool> MultipleAction { get; set; } public Action<int, bool> NeverHandleAction { get; set; } public int SpecialNumber { get; set; } public IFizzBuzzWhizz NextFlow { get; set; } public void Process(int number, bool isHandle) { if (number % this.SpecialNumber == 0) { if (this.MultipleAction != null) this.MultipleAction(number, isHandle); isHandle = true; } if (!isHandle) { if (this.NeverHandleAction != default(Action<int, bool>)) this.NeverHandleAction(number, isHandle); } if (this.NextFlow != null) this.NextFlow.Process(number, isHandle); } }
/// <summary> /// 游戏控制者 /// </summary> public class GameController { public string StartGame(int players, params int[] numbers) { var fizz = new Fizz(numbers[0]); var buzz = new Buzz(numbers[1]); var whizz = new Whizz(numbers[2]); fizz.NextFlow = buzz; buzz.NextFlow = whizz; var result = new StringBuilder(); for (var i = 1; i <= players; i++) { fizz.Process(i); } return result.ToString(); } }
下面附上我的代码。欢迎大家提供不同的面向对象设计思路。