编写转换函数的最佳方法

9
假设我正在编写一个函数来转换温度单位。我希望支持至少摄氏度、华氏度和开尔文。是将源单位和目标单位作为函数的分离参数传递,还是一种组合参数呢?
示例1 - 分离参数: function convertTemperature("celsius", "fahrenheit", 22)
示例2 - 组合参数: function convertTemperature("c-f", 22)
函数内部的代码可能是最重要的。使用两个参数时,确定我们将使用哪个公式的逻辑稍微复杂一些,但单个参数在某种程度上感觉不对。
你有什么想法?
18个回答

15

选择第一个选项,但是不要使用字面字符串(这种方式容易出错),而应该使用常量或者枚举类型(如果你所使用的编程语言支持的话),就像这样:

convertTemperature (TempScale.CELSIUS, TempScale.FAHRENHEIT, 22)

哈哈...我开始回答的时候你的答案还没有出现。你比我快了! :-) - Kilhoffer

5

这取决于编程语言。

通常,我会使用带有枚举类型的单独参数。

如果是面向对象的语言,我建议使用一个温度类,内部以任何你喜欢的方式存储温度,然后编写函数将其输出为所需的任何单位:

temp.celsius(); // 返回以摄氏度表示的温度


3

在编写这样的设计时,我喜欢想到自己:“如果我需要添加一个额外的单位,哪种设计会使它最容易?” 这样做,我得出结论,枚举类型会是以下原因最容易的选择:

1)添加新值很容易。 2)我避免进行字符串比较。

然而,如何编写转换方法呢?3p2等于6。这意味着有6种不同的摄氏度、华氏度和开氏度的组合。如果我想要添加一种新的温度格式“foo”呢?那就是4p2,即12!再来两个?5p2=20组合。三个?6p2=30组合!

您可以快速看到每个附加修改都需要越来越多的代码更改。出于这个原因,我不直接进行转换!相反,我进行中间转换。我会选择一个温度,比如开氏度。最初,我会将其转换为开氏度。然后,我会将开氏度转换为所需的温度。是的,这确实会导致额外的计算。但是,它使得扩展代码变得非常容易。添加一个新的温度单位只会导致对代码进行两个新的修改。简单明了。


2

几点建议:

  • 我会使用枚举类型,它可以被语法检查器或编译器检查,而不是字符串,因为字符串容易打错。伪PHP代码如下:

    define ('kCelsius', 0); define ('kFarenheit', 1); define ('kKelvin', 2); $a = ConvertTemperature(22, kCelsius, kFarenheit);

此外,在我的看法中,更自然的做法是将要转换的温度作为操作对象放在首位。这样可以给你的参数排序(转换-什么?从哪里?到哪里?),并有助于记忆。


2

如果您使用第一种方法,您的功能将更加健壮。如果您需要添加另一个比例尺,则需要处理一个更多的参数值。在第二种方法中,添加另一个比例尺意味着要添加与列表上已有比例尺数量相同的值乘以2。(例如,要添加K到C和F,您必须添加K-C、K-F、C-K和C-F。)

构建程序的一个不错的方式是先将任何输入转换为任意选择的中间比例尺,然后从该中间比例尺转换到输出比例尺。

更好的方法是为各种比例尺制定一些斜率和截距的小型库,只需查找输入和输出比例尺的数字,然后在一个通用步骤中进行计算。


2

在C#(以及可能是Java)中,最好创建一个Temperature类,将温度私有地存储为摄氏度(或其他单位),并且该类应该具有Celcius、Fahrenheit和Kelvin属性,在它们的get和set语句中完成所有转换。


1

这是我的看法(使用PHP):

function Temperature($value, $input, $output)
{
    $value = floatval($value);

    if (isset($input, $output) === true)
    {
        switch ($input)
        {
            case 'K': $value = $value - 273.15; break; // Kelvin
            case 'F': $value = ($value - 32) * (5 / 9); break; // Fahrenheit
            case 'R': $value = ($value - 491.67) * (5 / 9); break; // Rankine
        }

        switch ($output)
        {
            case 'K': $value = $value + 273.15; break; // Kelvin
            case 'F': $value = $value * (9 / 5) + 32; break; // Fahrenheit
            case 'R': $value = ($value + 273.15) * (9 / 5); break; // Rankine
        }
    }

    return $value;
}

基本上,$input 值被转换为标准摄氏度,然后再转换回 $output 比例 - 一个函数统治它们所有。=)


1

这取决于你将有多少个转换。我可能会选择一个参数,以枚举的形式给出:考虑到这个转换的扩展版本。

enum Conversion
{
  CelsiusToFahrenheit,
  FahrenheitToCelsius,
  KilosToPounds
}

Convert(Conversion conversion, X from);

现在,在调用点处您拥有了合理的类型安全性——不能提供正确类型的参数,从而产生不正确的运行时结果。考虑其他选择。

enum Units
{
  Pounds,
  Kilos,
  Celcius,
  Farenheight
}

Convert(Unit from, Unit to, X fromAmount);

我可以安全地调用

Convert(Pounds, Celcius, 5, 10);

但结果是毫无意义的,你最终会在运行时失败。是的,我知道你现在只处理温度,但一般概念仍然适用(我相信)。


你正在做一个不必要的假设,认为你必须使用一个通用的单位枚举,而不是更适合目的的东西,比如温度单位枚举。 - Isaac Moses

1
我会将温度类型枚举出来,并传入两个刻度参数。类似以下的C#代码:


public void ConvertTemperature(TemperatureTypeEnum SourceTemp,
                               TemperatureTypeEnum TargetTemp, 
                               decimal Temperature)
{}


1
我一直在寻找使用对象解决编程问题的方法。我希望这意味着我比只使用函数解决问题时更加面向对象,但这还有待验证。
在C#中:
interface ITemperature
{
     CelciusTemperature ToCelcius();
     FarenheitTemperature ToFarenheit();
}

struct FarenheitTemperature : ITemperature
{
    public readonly int Value;
    public FarenheitTemperature(int value)
    {
        this.Value = value;
    }

    public FarenheitTemperature ToFarenheit() { return this; }
    public CelciusTemperature ToCelcius()
    {
        return new CelciusTemperature((this.Value - 32) * 5 / 9);
    }

}

struct CelciusTemperature
{
    public readonly int Value;
    public CelciusTemperature(int value)
    {
        this.Value = value;
    }

    public CelciusTemperature ToCelcius() { return this; }
    public FarenheitTemperature ToFarenheit()
    {
        return new FarenheitTemperature(this.Value * 9 / 5 + 32);
    }
}

还有一些测试:

        // Freezing
        Debug.Assert(new FarenheitTemperature(32).ToCelcius().Equals(new CelciusTemperature(0)));
        Debug.Assert(new CelciusTemperature(0).ToFarenheit().Equals(new FarenheitTemperature(32)));

        // crossover
        Debug.Assert(new FarenheitTemperature(-40).ToCelcius().Equals(new CelciusTemperature(-40)));
        Debug.Assert(new CelciusTemperature(-40).ToFarenheit().Equals(new FarenheitTemperature(-40)));

还有一个这种方法避免的错误示例:

        CelciusTemperature theOutbackInAMidnightOilSong = new CelciusTemperature(45);
        FarenheitTemperature x = theOutbackInAMidnightOilSong; // ERROR: Cannot implicitly convert type 'CelciusTemperature' to 'FarenheitTemperature'

将开尔文转换添加入其中,留作练习。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接