美文网首页
kata-rectangle-sprint3 & refacto

kata-rectangle-sprint3 & refacto

作者: 码农练功房 | 来源:发表于2021-02-12 16:56 被阅读0次

sprint3:

(1)宽的取值范围为(0,100],高的取值范围为(0,75)。当值不在这个范围内,则认为矩形非法;

(2)非法的矩形的面积和周长为0;

按照需求设计出测试用例:

TEST("should calc rectangle width & height range succ")
{
    Rectangle rec(100 , 75);
    ASSERT_THAT(rec.getWidth(), eq(100));
    ASSERT_THAT(rec.getHeight(), eq(0));
    ASSERT_THAT(rec.area(), eq(0));
    ASSERT_THAT(rec.perimeter(), eq(0));
}

按照需求快速实现:

namespace
{
    const double MIN_WIDTH = 0;
    const double MAX_WIDTH = 100;
    const double MIN_HEIGHT = 0;
    const double MAX_HEIGHT = 75;
}

Rectangle::Rectangle(double width, double height)
{
    double m_width_result = processPrecision(width, 2, 0);
    if(m_width_result > MIN_WIDTH and m_width_result <= MAX_WIDTH) m_width = m_width_result;
    else m_width = 0;

    double m_height_result = processPrecision(height, 2, 0);
    if(m_height_result > MIN_HEIGHT and m_height_result < MAX_HEIGHT) m_height = m_height_result;
    else m_height = 0;
}

double Rectangle::area() const
{
    return processPrecision(getHeight() * getWidth(), 2, 0.5);
}

double Rectangle::perimeter() const
{
    if(getHeight() == 0 or getWidth() == 0)
        return 0;
    return 2 * (getHeight() + getWidth());
}

refactor:去除重复

从上述代码中可以看出,计算长方形长、宽的逻辑基本一致:

如果长(宽)在取值范围内,那么则取对应的值为最终结果,否在最终结果为0。

唯一存在差异的地方是长方形的长、宽对合法取值范围的定义是不一样的。

我们这里可以提取出一个inRange的概念,这是个相对稳定的概念,而getResult依赖于这个稳定的概念,这样下来代码是稳定的。WidthHeight对于inRange这个概念的实现都有自己的定义。

namespace
{
    struct Width
    {
        Width(double value):value(value){}
        bool inRange() const
        {
            return value > MIN_WIDTH and value <= MAX_WIDTH;
        }
    private:
        double value;
    };

    struct Height
    {
        Height(double value):value(value){}
        bool inRange() const
        {
            return value > MIN_HEIGHT and value < MAX_HEIGHT;
        }
     private:
         double value;
     };

    template<typename Value>
    double getResult(double value)
    {
        // 对Value类的接口存在隐式约束:必须实现inRange,否则编译失败。
        return Value(value).inRange() ? processPrecision(value, 2, 0): 0;
    }
}

Rectangle::Rectangle(double width, double height)
{
    m_width = getResult<Width>(width);
    m_height = getResult<Height>(height);
}

refactor:分离精度算法

精度算法是比较常用的,如果将其分离出去,能够提高函数的可重用性:

//util.h
#ifndef H44DCD35C_F5D5_4801_8E16_AF7DB6641C5A
#define H44DCD35C_F5D5_4801_8E16_AF7DB6641C5A

double floor(double value, const unsigned int precision);
double round(double value, const unsigned int precison);

#endif /* H44DCD35C_F5D5_4801_8E16_AF7DB6641C5A */

//util.cpp
#include <cmath>
#include "util.h"
namespace
{
    double processPrecision(double value, const unsigned int precision, double compensation)
    {
        double factor = ::pow(10, precision);
        return ::floor(value * factor + compensation)/factor;
    }
}

double floor(double value, const unsigned int precision)
{
    return processPrecision(value, precision, 0);
}

double round(double value, const unsigned int precision)
{
    return processPrecision(value, precision, 0.5);
}
// Rectangle.cpp 对应调用的地方修改为:
template<typename Value>
double getResult(double value)
{
    return Value(value).inRange() ? floor(value, 2): 0;
}

double Rectangle::area() const
{
    return round(getHeight() * getWidth(), 2);
}

这里除了对精度算法进行了分离,还对processPrecision(value, precision, 0)processPrecision(value, precision, 0.5)进行了封装,这样做的好处有:

  1. floorround较好地表达了向下取整、四舍五入的语义。
  2. 降低客户端调用的心智包袱。

杜绝潜规则:

在上节,我们将非法的长度、宽度的值设置为0,需求里也没有具体提及到可以这样处理。所以我们不应该投机取巧(不要将非法长度设为0),把偶然当必然(非本质的关系不稳定)。我们做如下修改:

namespace
{
    struct Width
    {
        Width(double value):value(value){}
        bool isVaild() const
        {
            return value > MIN_WIDTH and value <= MAX_WIDTH;
        }
    private:
        double value;
    };

    struct Height
    {
        Height(double value):value(value){}
        bool isVaild() const
        {
            return value > MIN_HEIGHT and value < MAX_HEIGHT;
        }
     private:
         double value;
     };
}

Rectangle::Rectangle(double width, double height):
m_width(floor(width, 2)),m_height(floor(height, 2)){}

double Rectangle::area() const
{
    if(not isVaild())
        return 0;
    return round(getHeight() * getWidth(), 2);
}

double Rectangle::perimeter() const
{
    if(not isVaild())
        return 0;
    return 2 * (getHeight() + getWidth());
}

bool Rectangle::isVaild() const
{
    return Width(m_width).isVaild() and Height(m_height).isVaild();
}

相关文章

网友评论

      本文标题:kata-rectangle-sprint3 & refacto

      本文链接:https://www.haomeiwen.com/subject/iktjxltx.html