从易于扩展扩展的角度来设计FizzBuzzWhizz

简介:

序言

最近FizzBuzzWhizz比较热,很多OSCER们也写出了自己的版本,有写的最快的,有写的最短的。

前面写过一篇文章叫 悠然乱弹:拉钩网FizzBuzzWhizz试题之悠然版解答,是悠然闲来无事写的一种算法,当时的文章只有写了实现与结果,但是没有详细说明作者为什么这么设计,所以导致一些人可能没有看明白,觉得有些设计是脱裤子放屁,多此一举。

今天悠然就来谈谈,悠然为什么这么设计,这么设计有什么好处?以与广大朋友们分享。

从题目来看,并不复杂,就是几种报数规则,并且有一些解决冲突时的规则,然后同学们就可以按规则进行游戏了。但是很显然,体育老师不希望每天玩的游戏都一样,同学们也不希望每天玩一样的游戏,这样就可能导致一个必然会出现的结果,那就是:游戏规则及解决冲突的规则可以方便的进行变换。

示例

但是这个时候,程序架构必须要保证,当游戏规则或冲突解决规则出现的时候,程序的代码修改量及修改范围要最小化,如果能达到这一目标,说明程序的架构与设计是合理的。

由于输出100,数量太多,因此悠然把输出数量调整为20,来看看悠然的FizzBuzzWhizz是怎么玩的:

1.只加入普通读法

?
1
2
3
4
5
6
7
public static void main(String[] args) {
        NumberReaderEngine numberReaderEngine = new NumberReaderEngine();
        numberReaderEngine.add( new CommonNumberReader( 1 ));
        for ( int i = 1 ; i <= 20 ; i++) {
            numberReaderEngine.readNumber(i);
        }
    }

运行结果:

?
1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

2. 只加入普通读法及一个能整除某个数字的规则

a.只加入普通读法及整除数字3的规则

?
1
2
3
4
5
6
7
8
public static void main(String[] args) {
        NumberReaderEngine numberReaderEngine = new NumberReaderEngine();
        numberReaderEngine.add( new MultipleNumberReader( 2 , 3 , "Fizz" ));
        numberReaderEngine.add( new CommonNumberReader( 1 ));
        for ( int i = 1 ; i <= 20 ; i++) {
            numberReaderEngine.readNumber(i);
        }
    }

运行结果:

?
1
1 2 Fizz 4 5 Fizz 7 8 Fizz 10 11 Fizz 13 14 Fizz 16 17 Fizz 19 20



b.只加入普通读法及整除数字3,5的规则

?
1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
        NumberReaderEngine numberReaderEngine = new NumberReaderEngine();
        numberReaderEngine.add( new MultipleNumberReader( 2 , 3 , "Fizz" ));
        numberReaderEngine.add( new MultipleNumberReader( 2 , 5 , "Buzz" ));
numberReaderEngine.add( new CommonNumberReader( 1 ));
        for ( int i = 1 ; i <= 20 ; i++) {
            numberReaderEngine.readNumber(i);
        }
    }
运行结果:
?
1
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz 

c.只加入普通读法及整除数字3,7的规则

?
1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
        NumberReaderEngine numberReaderEngine = new NumberReaderEngine();
        numberReaderEngine.add( new MultipleNumberReader( 2 , 3 , "Fizz" ));
        numberReaderEngine.add( new MultipleNumberReader( 2 , 5 , "Buzz" ));
        numberReaderEngine.add( new MultipleNumberReader( 2 , 7 , "Whizz" ));
numberReaderEngine.add( new CommonNumberReader( 1 ));
        for ( int i = 1 ; i <= 20 ; i++) {
            numberReaderEngine.readNumber(i);
        }
    }
运行结果:
?
1
1 2 Fizz 4 Buzz Fizz Whizz 8 Fizz Buzz 11 Fizz 13 Whizz FizzBuzz 16 17 Fizz 19 Buzz 

3.加入普通规则及整除规则及包含规则
?
1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {
        NumberReaderEngine numberReaderEngine = new NumberReaderEngine();
        numberReaderEngine.add( new MultipleNumberReader( 2 , 3 , "Fizz" ));
        numberReaderEngine.add( new MultipleNumberReader( 2 , 5 , "Buzz" ));
        numberReaderEngine.add( new MultipleNumberReader( 2 , 7 , "Whizz" ));
        numberReaderEngine.add( new IncludeNumberReader( 3 , 3 , "Whizz" ));
        numberReaderEngine.add( new CommonNumberReader( 1 ));
        numberReaderEngine.sortNumberReader();
        for ( int i = 1 ; i <= 20 ; i++) {
            numberReaderEngine.readNumber(i);
        }
    }

运行结果:

?
1
1 2 Whizz 4 Buzz Fizz Whizz 8 Fizz Buzz 11 Fizz Whizz Whizz FizzBuzz 16 17 Fizz 19 Buzz

目前为止,体育老师拿到悠然写的程序,通过调整游戏规则及其优先级,可以有N种玩法,但是除了调用代码之外,不必修改任何代码。

调用代码是什么?是体育老师在开始玩游戏之前宣布的游戏规则,而游戏规则是要经常变化的,要不就没有新意了(具体到业务中,就是无法适应业务的变化了)。

小结

FizzBuzzWhizz确实是一道非常有代表意义的试题,它可以做得很简单,也可以做得很复杂。

悠然把游戏运行机理的内容归到不变的部分,把游戏规则的扩展及游戏规则的声明归到变的部分。从而保证了FizzBuzzWhizz具有良好的架构稳定性及扩展性,同时也对游戏的可玩性提供了良好的支持。

有的同学问,为什么在玩游戏之前要执行一下下面的语句:

?
1
numberReaderEngine.sortNumberReader();

有几种做法,一种是把直接放到readNumber方法中,好处是对调用者不可见,缺点是性能会稍差。

一种做法是把规则列表直接通过构造方法传入,但是带来的问题是规则不可以后续进行调整。

另外一种是通过set方法设置进去,然后在里面进行排序,这种就需要,每次整个传入。

最后一种是放在add方法之内,每个添加一个规则进行进行一次排序,这同样会导致性能会差一点点。

当然这里只是一个示例,因此在这里单独调用一下,也没有太大问题。

同样做了  FizzBuzzWhizz试题的同学,也可以思考一下,如果也要完成上面的各种游戏变化,代码上的变化是否容易呢?
相关文章
|
7月前
|
设计模式 uml C++
C++中的装饰器模式:灵活地扩展功能
C++中的装饰器模式:灵活地扩展功能
105 0
|
存储 Cloud Native Linux
软件开发方法:复用与扩展
软件开发方法:复用与扩展
|
6月前
|
算法 Linux C++
C++框架设计中实现可扩展性的方法
在软件开发中,可扩展性至关重要,尤其对于C++这样的静态类型语言。本文探讨了在C++框架设计中实现可扩展性的方法:1) 模块化设计降低耦合;2) 使用继承和接口实现功能扩展;3) 通过插件机制动态添加功能;4) 利用模板和泛型提升代码复用;5) 遵循设计原则和最佳实践;6) 应用配置和策略模式以改变运行时行为;7) 使用工厂和抽象工厂模式创建可扩展的对象;8) 实现依赖注入增强灵活性。这些策略有助于构建适应变化、易于维护的C++框架。
510 2
|
消息中间件 存储 数据可视化
【结合业务需求给出合理的技术解决方案,改进现有模块功能,提高系统的可扩展性,封装性,稳定性】
【结合业务需求给出合理的技术解决方案,改进现有模块功能,提高系统的可扩展性,封装性,稳定性】
134 1
|
安全 编译器 C语言
c++学习之c++对c的扩展1
c++学习之c++对c的扩展1
115 0
|
消息中间件 设计模式 缓存
聊聊结合业务需求给出合理的技术解决方案,改进现有模块功能,提高系统的可扩展性,封装性,稳定性
聊聊结合业务需求给出合理的技术解决方案,改进现有模块功能,提高系统的可扩展性,封装性,稳定性
|
编译器 C语言 C++
c++学习之c++对c的扩展2
c++学习之c++对c的扩展2
81 0
|
设计模式 算法
如何优雅地使用策略模式来实现更灵活、可扩展和易于维护的代码?
如何优雅地使用策略模式来实现更灵活、可扩展和易于维护的代码?
96 0
|
存储 移动开发 运维
高可扩展性系统的设计(下)
高可扩展性系统的设计(下)
187 0
高可扩展性系统的设计(下)
|
存储 缓存 负载均衡
高可扩展性系统的设计(上)
高可扩展性系统的设计(上)
329 0