使用PHP 7编写FizzBuzz
大家,如果学会了一种新的语言,就会想要写FizzBuzz对吧。
我也有过狂热地追求最终恶俗FizzBuzz大全的时代。现在正是被承诺每个人都能写出FizzBuzz的2016年,但我仍然对解决逆FizzBuzz问题缺乏自信。
嗯,说到新时代的语言,就是PHP 7。虽然我还是对面向对象感到有些不习惯,但是既然是用新的语言来写FizzBuzz,我还是会兴致勃勃地去做。在工作中,虽然我正在努力准备引入PHP 7,但现在还停留在5系列,有些不安的情绪也存在。
有这样的事情,反正我想要以企业所能承受的品质来写!
由于对设计模式非常苦手,记不住,因此我决定在参考TECHSCORE(テックスコア)的同时,思考并给出类似的类名和方法名。即使阅读书籍也无法完全记住英语和设计模式。就像莫尔摩伊所说的:“英语和爱情都不容易进步”。
从调用方的代码开始。take()是自定义的辅助函数。它只是从数组或可以通过foreach循环迭代的某个东西中获取前n个元素。
<?php
use FizzBuzz;
$map = [
3 => '\FizzBuzz\FizzValue',
5 => '\FizzBuzz\BuzzValue',
];
$factory = new FizzBuzz\FizzBuzzFactory(new FizzBuzz\Mapper($map));
foreach (take($factory->generate(), 100) as $v) {
echo $v, PHP_EOL;
}
exit;
/**
* @param array|\Traversable $iter
* @param int $n
* @return \Generator
*/
function take($iter, $n)
{
$i = 0;
foreach ($iter as $x) {
if ($n <= $i++) { return; }
yield $x;
}
}
FizzBuzz\FizzBuzzFactory是根据FizzBuzzStrategy来执行FizzBuzz的对象。它是Strategy模式和AbstractFactory模式的组合技术(希望如此,但也可能是错误的)。FizzBuzz\Mapper是实现了FizzBuzz\FizzBuzzStrategy的类。
FizzBuzz\Mapper的功能是将关联数组作为参数传递,然后动态指定类名字符串,即“如果是3的倍数,则为\FizzBuzz\FizzValue”,“如果是5的倍数,则为\FizzBuzz\BuzzValue”。
好的,由于这是在企业世界,所以我们将针对表示值的对象而不是简单的字符串进行操作。在这里,我们有一个名为”abstract FizzBuzz\Value”的抽象类,它表示这个对象。我们还准备了继承它的类FizzValue、BuzzValue和NumberValue。它们都有一个__toString()方法,可以将”Fizz”、”Buzz”和数字分别转化为十进制表示的字符串(例如”11″)。
哦哦,Fizz和Buzz已经明白了,那么”FizzBuzz”应该怎么输出呢。正如大家所知,它既是”3的倍数”又是”5的倍数”。而且这次作为限制,除了抽象类外不能继承其他类,所以设置为了final class。其实PHP本身就没有多重继承…这就是为此情况设计的ComposableValue类。有了这个类,无论有多少个实现了FuzzBuzz\Value的类,都可以组合状态了。做到了!
现在解释这个的哥哥也有点厌倦了,所以我给你看代码。
<?php
namespace FizzBuzz
{
// Strategy
interface FizzBuzzStrategy
{
public function fizzbuzzize(int $n) :Value;
}
final class Mapper implements FizzBuzzStrategy
{
/** @var array */
private $fizzbuzz_map;
/**
* @param array $fizzbuzz_map
*/
public function __construct(array $fizzbuzz_map)
{
$this->fizzbuzz_map = $fizzbuzz_map;
}
/**
* @param int $n
* @return Value
*/
public function fizzbuzzize(int $n) :Value
{
$vs = [];
foreach ($this->fizzbuzz_map as $m => $v) {
if ($n % $m == 0) {
$vs[] = ($v instanceof \Closure) ? $v($n) : new $v($n);
}
}
return (count($vs) > 0) ? new ComposableValue($n, ...$vs) : new NumberValue($n);
}
}
// Value
abstract class Value
{
/** @var int */
protected $n;
public function __construct(int $n)
{
$this->n = $n;
}
abstract function __toString();
}
final class NumberValue extends Value
{
public function __toString()
{
return sprintf("%d", $this->n);
}
}
trait SymbolToString
{
public function __toString()
{
return static::SYMBOL;
}
}
final class FizzValue extends Value
{
use SymbolToString;
const SYMBOL = 'Fizz';
}
final class BuzzValue extends Value
{
use SymbolToString;
const SYMBOL = 'Buzz';
}
final class ComposableValue extends Value
{
/** @var Value[] */
private $values = [];
public function __construct(int $n, Value ...$values)
{
parent::__construct($n);
$this->values = array_values($values);
}
public function add(Value $value)
{
$this->values[] = $value;
}
public function __toString()
{
$s = '';
foreach ($this->values as $v) {
$s .= $v;
}
return $s;
}
}
// Factory
final class FizzBuzzFactory
{
/** @var FizzBuzzStrategy */
private $strategy;
public function __construct(FizzBuzzStrategy $strategy)
{
$this->strategy = $strategy;
}
/**
* @param int $from
* @return \Generator
*/
public function generate(int $from = 1)
{
$n = $from;
while (true) {
yield $this->strategy->fizzbuzzize($n++);
}
}
}
}
(待会儿创建一个仓库,放到GitHub上)
我把它上传到了GitHub上,链接是https://github.com/BaguettePHP/FizzBuzz。
composer g require baguette/fizzbuzz
用Composer进行安装。
总结
因为感觉很疲倦,所以我只会列举一下我在这里正在使用的PHP语言功能。ヾ(〃><)ノ゙
PHP: クラスとオブジェクト – Manual
PHP: コンストラクタとデストラクタ – Manual
PHP: プロパティ – Manual
PHP: オブジェクト定数 – Manual
PHP: オブジェクト インターフェイス – Manual
PHP: トレイト – Manual
PHP: クラスの抽象化 – Manual
PHP: オブジェクトの継承 – Manual
PHP: 無名クラス – Manual
PHP: 遅延静的束縛 (Late Static Bindings) – Manual (これは不必要)
PHP: マジックメソッド – Manual #__toString()
PHP: 関数 – Manual
PHP: 関数の引数 – Manual #型宣言
PHP: 無名関数 – Manual
PHP: 可変関数 – Manual
PHP: ジェネレータ – Manual
PHP: ジェネレータとは – Manual
PHP: ジェネレータの構文 – Manual
PHP: 制御構造 – Manual
PHP: while – Manual
PHP: foreach – Manual
请用中文将以下句子进行转述,只需要提供一个选项:
1. The weather today is very hot.
请以中文原生方式给出以下句子的改写,只需提供一种选择:
请原生中文翻译以下句子。
“Can you please provide more details about the project?”
请你帮我把这句话改成中文。
赠品
为什么要特意将FizzBuzz Mapper这个类分离出来呢?而且,上面的代码中根本没有任何匿名类存在。嗯嗯。
这是为了应对头脑聪明的上司对我说:“小张,为这个程序添加一个功能,当数字是7的倍数时,输出“Nass”。
$map = [
3 => '\FizzBuzz\FizzValue',
5 => '\FizzBuzz\BuzzValue',
7 => function (int $n) {
return new class($n) extends FizzBuzz\Value { use FizzBuzz\SymbolToString; const SYMBOL = 'Nass'; };
}
];