【问题标题】:Need advice on wordy naming of methods需要关于方法的冗长命名的建议
【发布时间】:2009-03-10 16:01:48
【问题描述】:

我正在编写一个用于创建几何形状的 API,但在命名我的方法时遇到了一些困难。

让我们举一个简单的例子:创建一个圆圈。我们大多数人可能都熟悉graphics.drawEllipse(x, y, w, h) 之类的方法。要画一个圆,你需要知道左上角的坐标,以及圆的宽和高。

我的 API 旨在让开发人员可以轻松地使用各种信息绘制形状,而无需进行大量数学运算 - 这对于圆形来说是微不足道的,但对于其他形状来说就更复杂了。例如,您还应该能够根据圆心坐标和半径,或左上角和右下角坐标绘制一个圆。

所以我有一个带有工厂方法的Circle 类,例如:

Circle.createWithCenterAndRadius(cx, cy, r)
Circle.createWithBoundingBox(x1, y1, x2, y2)
Circle.createWithWidthAndHeight(x, y, w, h)

我觉得这里可能有“代码味道”,但我不确定。一方面,这些工厂方法必须是描述性的。另一方面,我可以预见这些方法名称会失控。例如,如何命名一个 Triangle 工厂方法,该方法在给定一个点、一侧的长度、一个角度和另一侧的长度的情况下创建一个三角形? Triangle.createWithPointSideAngleAndSide(x, y, side1, angle, side2)?那只是邪恶的吗?

如果您要使用此 API,您可以使用这样的方法名称吗?您对如何使方法名称更合理有什么建议吗?

【问题讨论】:

  • 我正在将方法更改为“来自”,如 Jason 的帖子中所示。根据 Joe_M 对 kevchadder 帖子的评论,我正在删除一些不必要的“和”。我考虑过你流畅的界面想法;我使用的是 Java,所以没有命名参数。我喜欢 Slim 的想法,看看是否可行。
  • 而且,我确实计划在我完成 API 草案后跟进,我应该很快就会完成。

标签: api naming-conventions


【解决方案1】:

您可以将您的圈子方法更改为

Circle.FromCenterAndRadius(...)
Circle.FromBoundingBox(...)
Circle.FromWidthAndHeight(...)

这意味着您正在以一种简洁的方式从不同的表示中创建圆圈...

【讨论】:

    【解决方案2】:

    任何不支持命名参数的语言都可以。如果语言支持命名参数,我更喜欢简短的 Create 并且只是具有明显的参数名称。

    对于具有命名参数的语言,您会:

    Circle.Create(
       centerX = cx, 
       centerY = cy, 
       radius = r
    );
    

    另一个更复杂的选择是流畅的界面,例如(但这可能太多了):

    circleBuilder.Center(cx,cy).Radius(r)
    circleBuilder.Center(x,y).Width(w).Height(y)
    circleBuilder.BoundWith().Left(x1,y1).Right(x2,y2)
    

    Center 返回一个只允许 Radius 或 Width 的中间类的实例。并且 BoundWith 返回一个只允许 Left 的。

    【讨论】:

    • 要创建大量代码。我认为您会浪费大量时间来创建该代码,然后尝试对其进行泛化,以便在创建矩形或三角形时它也可以工作。
    • @Joe,将其从“但那可能太多了”更新为“但那可能太多了”。
    【解决方案3】:

    我认为您的描述性方法没有任何问题 - 它们是紧凑的并且准确地描述了正在发生的事情。库的用户不会怀疑您的方法的功能,维护程序员也不会怀疑。

    如果你真的担心暴露大量的工厂方法,你也可以在这里应用一些设计模式——比如拥有带有属性类的工厂方法。你可以有一个具有 CenterX、CenterY、Radius、(bool)UseCenterX、(bool)UseCenterY 等属性的 CircleProperties 类,然后将其传递给公共工厂方法,该方法将确定使用哪个(私有)工厂方法。

    假设 C#:

    var circleProperties = new CircleProperties()
    {
       CenterX = 10,
       CenterY = -5,
       Radius = 8,
       UseCenterX = true,
       UseCenterY = true,
       UseCenterRadius = true
    };
    
    var circle = Circle.Create(circleProperties);
    

    【讨论】:

    • 你真的需要“UseX”属性吗?您不能简单地检查已设置的属性(非空)吗?或者在其他属性的设置器中设置它们(例如,setCenterX() 设置 UseCenterX)?
    • TMN:你可以,但是你真的必须正确选择你的幻数……显然,0.0 是一个完全有效的坐标。不过,在适当的 setter 中自动设置这些布尔值是个好主意。
    【解决方案4】:

    我的第一直觉是拥有更多类型,这将允许更直观的方法重载。

    // instead of Circle.createWithCenterAndRadius(cx, cy, r)
    Circle.create( new Point(cx,xy), r);
    
    // instead of Circle.createWithBoundingBox(x1, y1, x2, y2)
    Circle.create( new Point(x1,y1), new Point(x1,y1) );
    // or even...
    Circle.create( new Box(p1,p2));
    
    // instead of Circle.createWithWidthAndHeight(x, y, w, h)
    Circle.create( new Point(x,y), w, h);
    

    除了点之外,您还可以定义距离(允许使用不同的单位)

    如果这种风格适合你,请考虑一下为什么需要工厂方法而不是构造函数。

    Circle c = new Circle(new Point(cx,xy), r);
    

    【讨论】:

      【解决方案5】:

      对于不支持命名参数的语言,将方法名称设为非常简单的名称会不会更简洁,例如 Circle.create 然后只需添加一个额外的输入标志字符串(例如“center”或“边界”)表示对于仅基于输入变量编号和类型难以区分的情况,应如何解释输入值?这样做的缺点是它需要方法内部的额外逻辑来处理不同类型的输入参数,并且还需要用户记住标志选项。

      【讨论】:

        【解决方案6】:

        我会有 CreateTriangle 方法并让重载显示所需的不同信息。

        例如

        Circle.CreateCircle(cx, cy, r)
        Circle.CreateCircle(point1, point2)
        Circle.CreateCircle(point, width, height)
        

        【讨论】:

        • +1:同意。我也是这样做的……让重载的参数自己说话。
        • er,这里有两个方法,参数相同(假设所有参数都是整数类型)。
        • 你说得对,最好使用 Point 类而不是两个 int 来区分
        • 您实际上可以同时使用 Point 和 Size 使其更清晰。 (点,半径),(点,点)和(点,大小)
        【解决方案7】:

        是的,这更像是一个元答案,但我建议你看看 Apple's Cocoa 中的命名是如何完成的。

        【讨论】:

          【解决方案8】:

          你的直觉是正确的——以这种方式创造事物的整个模式是——不确定的。

          除非它们只使用一次或两次,否则它们会变得非常混乱。如果您要创建一个包含 5 个圆形和 3 个三角形的形状,那将是一团糟。

          除了一个简单的例子之外,任何事情都可能最好通过某种数据驱动的实现来完成。

          为此,使用字符串、散列或 XML 来定义形状可能非常有用。

          但这一切都取决于您希望如何使用它们。

          在 Java 中创建 Swing 控件时,我遇到了同样的问题。您最终会得到一行又一行的“new Button()”,然后是一堆 .set 属性调用以及一行代码将值复制到对象(或添加侦听器),以及一行重置价值..

          这种样板文件不应该出现在代码中,所以我通常会尝试找到一种方法来使用数据驱动它,动态地将控件绑定到对象——为此,基于字符串的描述性语言将非常有帮助.

          【讨论】:

          • 你的建议绝对有道理。您能否举一个数据驱动实现的简短示例?对于“字符串”,您是否暗示类似领域特定语言 (DSL)?
          【解决方案9】:

          我知道,我知道。对于 C/C++/Java 人来说,这听起来完全是疯狂的,但是问题中给出的示例以及所有这些答案中的示例清楚地表明了 CamelCaseNaming 真正的糟糕、糟糕的约定。

          让我们再看一下原来的例子:

          Circle.createWithCenterAndRadius(cx, cy, r)  
          Circle.createWithBoundingBox(x1, y1, x2, y2)
          Circle.createWithWidthAndHeight(x, y, w, h)
          

          现在让我们摆脱那种驼峰式表示法

          Circle.create_with_center_and_radius(cx, cy, r)  
          Circle.create_with_bounding_box(x1, y1, x2, y2)
          Circle.create_with_width_and_height(x, y, w, h)
          

          这可能看起来非常陌生,但说实话:哪个版本更容易阅读?

          【讨论】:

          • 不错的例子——它实际上更容易阅读——但完全无关紧要。这不像任何人会切换或任何东西(如果有人这样做会很糟糕!)那么你想要完成什么?
          • 注意:当我使用 Java 时,我使用 Java 语法,当我使用 Ruby 时,我使用 Ruby 语法。我觉得没有必要将一个用户切换到另一个用户——两者都非常易读。附言。虽然 ruby​​ 更容易阅读,但 java 更容易输入。
          猜你喜欢
          • 2014-05-21
          • 1970-01-01
          • 2012-08-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多