C# 重构乐趣-素数

C# 重构乐趣-素数,c#,refactoring,C#,Refactoring,从Bob叔叔的书Clean Code(示例是Java,所以这是我的第一个Java翻译)中,我一直在玩素数的重构示例 问题是:您将如何重构以下代码 这里有4个版本:UglyVersion:-),BobVersion,PeteVersion,PeteVersionClassMember。我这样做是为了好玩,希望你也喜欢:-) 类程序错误 { 静态void Main() { int[]结果=生成时间。生成时间(30); foreach(结果中的var i) Console.Write(i.ToStri

从Bob叔叔的书Clean Code(示例是Java,所以这是我的第一个Java翻译)中,我一直在玩素数的重构示例

问题是:您将如何重构以下代码

这里有4个版本:UglyVersion:-),BobVersion,PeteVersion,PeteVersionClassMember。我这样做是为了好玩,希望你也喜欢:-)

类程序错误
{
静态void Main()
{
int[]结果=生成时间。生成时间(30);
foreach(结果中的var i)
Console.Write(i.ToString()+“,”);
}
}
/// 
///给定一个从2开始的整数数组,去掉2的倍数。第二天好
///取消交叉整数,并将其所有倍数都交叉掉。
///重复此操作,直到超过最大值的平方根
/// 
公共类生成时间
{
公共静态int[]生成时间(int maxValue)
{
if(maxValue>=2)//唯一有效的情况
{
//声明
int s=maxValue+1;//数组的大小
bool[]f=新bool[s];
int i;
//将数组初始化为true
对于(i=0;i对于(i=0,j=0;i我唯一会做的不同的事情是在您的最终版本中保留crossedOut布尔数组作为类成员。其余的方法都是私有的,因此在每个方法中传入和传出数组不会有太多好处。

只是为了澄清Maxwells的意见:

这很糟糕:

if (maxValue >= 2) 
    { blah }
else
    return new int[];
这很好

if (maxValue < 2) 
    return new int[0];

blah
if(最大值<2)
返回新整数[0];
废话

老实说?我根本不会重构。也许这只是因为我以前是个数学高手,但我发现第一个版本更容易阅读

重构算法通常没有多大意义。当重构代码时,意味着您希望重用或更改其中的一部分。在这种情况下,整个代码块是静态的、不可变的-您唯一可以更改的是使用不同的算法替换整个函数。单行函数如
notCrossed
seem特别无用;它们只会使代码更加冗长,而不会帮助解释任何不明显的东西

实际上,我可能会做两种重构:

  • 将类名
    GeneratePrimes
    更改为
    PrimeGenerator
    ,就像您已经做的那样。动词类名称总是让我产生循环-它是类还是方法

  • 将其更改为返回
    IList
    IEnumerable
    。返回的数组类型为

  • 编辑-第三个重构是删除一些无用的注释,并使用有意义的变量名(在适当的情况下)

除此之外-坚持原来的版本


编辑-事实上,我越看原稿,就越不喜欢它。不是因为它的组织方式,而是因为它的写作方式。以下是我的重写:

public class PrimeGenerator
{
    public static IEnumerable<int> GeneratePrimes(int maxValue)
    {
        if (maxValue < 2)
            return Enumerable.Empty<int>();

        bool[] primes = new bool[maxValue + 1];
        for (int i = 2; i <= maxValue; i++)
            primes[i] = true;

        for (int i = 2; i < Math.Sqrt(maxValue + 1) + 1; i++)
        {
            if (primes[i])
            {
                for (int j = 2 * i; j <= maxValue; j += i)
                    primes[j] = false;
            }
        }

        return Enumerable.Range(2, maxValue - 1).Where(i => primes[i]);
    }
}
公共类素数生成器
{
公共静态IEnumerable GenerateTime(int maxValue)
{
如果(最大值<2)
返回可枚举的.Empty();
bool[]primes=新bool[maxValue+1];

对于(int i=2;i使用LINQ的优雅解决方案:

static IEnumerable<int> PrimeNumbers(int maxValue)
{
    if (maxValue > 1 && maxValue < int.MaxValue)
    {
        var integers = Enumerable.Range(2, maxValue);
        for (;;)
        {
            int item = integers.FirstOrDefault();
            if (item == 0)
            {
                break;
            }

            yield return item;
            integers = integers.Where(x => x % item != 0);
        }
    }
}
静态IEnumerable素数(int maxValue)
{
if(maxValue>1&&maxValuex%item!=0);
}
}
}

这应该是社区wiki。我最喜欢坏版本:-P(倒转IF除外)将其保留为类成员也可以缓存,而不是每次都重新生成。为什么一个比另一个好?大括号可以封装许多行。当它们之间只有一行时,if语句与另一个语句配对更为明显。返回值接近顶部也很好。如果当你追踪代码时,你可以立即说“好吧,如果小于2,我们只返回一个空数组”,而不需要进一步阅读。当然,这违反了“应该只有一个return语句,它应该在函数的末尾”的规则我碰巧不喜欢这个规则。@MatrixFrog:这通常被称为一个守卫,实际上是一个很好的澄清控制流的方法。“单返回”规则很愚蠢。我也真的不喜欢单返回规则。我过去一直遵循这一规则,它通常会导致深度嵌套,甚至重构出来,一点都不简单。我不同意重构以重用零件。重构有很多原因,其中之一可能是可读性。提取描述他们以自己的名义所做的事情可以让算法更容易理解,因为你可以关注编写它的人的意图,而不是意图的实现
if (maxValue >= 2) 
    { blah }
else
    return new int[];
if (maxValue < 2) 
    return new int[0];

blah
public class PrimeGenerator
{
    public static IEnumerable<int> GeneratePrimes(int maxValue)
    {
        if (maxValue < 2)
            return Enumerable.Empty<int>();

        bool[] primes = new bool[maxValue + 1];
        for (int i = 2; i <= maxValue; i++)
            primes[i] = true;

        for (int i = 2; i < Math.Sqrt(maxValue + 1) + 1; i++)
        {
            if (primes[i])
            {
                for (int j = 2 * i; j <= maxValue; j += i)
                    primes[j] = false;
            }
        }

        return Enumerable.Range(2, maxValue - 1).Where(i => primes[i]);
    }
}
static IEnumerable<int> PrimeNumbers(int maxValue)
{
    if (maxValue > 1 && maxValue < int.MaxValue)
    {
        var integers = Enumerable.Range(2, maxValue);
        for (;;)
        {
            int item = integers.FirstOrDefault();
            if (item == 0)
            {
                break;
            }

            yield return item;
            integers = integers.Where(x => x % item != 0);
        }
    }
}