使用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'; };
    }
];
广告
将在 10 秒后关闭
bannerAds