【问题标题】:What should I name a class whose sole purpose is procedural?我应该如何命名一个唯一目的是程序的类?
【发布时间】:2012-11-07 23:54:36
【问题描述】:

在 OO 模式方面我有很多东西要学习,这是我多年来遇到的一个问题。我最终会遇到我的课程的唯一目的是程序性的情况,基本上只是将一个程序包装在一个类中。这似乎不是正确的 OO 做事方式,我想知道是否有人对这个问题有足够的经验来帮助我以不同的方式考虑它。下面是我在当前应用程序中的具体示例。

在我的应用程序中,我从工程测量设备中获取一组点,并将它们标准化以在程序的其他地方使用。 “规范化”是指整个数据集的一组转换,直到达到目标方向。

每个转换过程都将接受一个点数组的输入(即class point { float x; float y; float z; } 的形式)并返回一个长度相同但值不同的数组。例如,point[] RotateXY(point[] inList, float angle) 之类的转换。另一种过程是分析类型的,用于补充规范化过程并决定下一步要进行什么转换。这种类型的过程接受与参数相同的点,但返回不同类型的数据集。

我的问题是,在这种情况下使用什么好的模式?我要编写的代码是一个 Normalization 类,它继承了 RotationXY 的类类型。但是 RotationXY 的唯一目的是旋转点,所以它基本上是实现一个功能。不过,由于我在第一段中提到的原因,这似乎不太好。

提前致谢!

【问题讨论】:

  • 这并不能真正解决它——让我们假设它是一个静态类,在这种情况下,规范化是一个静态类,它的过程在子命名空间的其他地方都定义了。也许我只是想多了。

标签: oop design-patterns 3d modeling


【解决方案1】:

通常,当涉及到只包含静态方法的类时,我将它们命名为 Util,例如DbUtil 用于外观 DB 访问,FileUtil 用于文件 I/O 等。因此,找到所有方法共有的术语并将其命名为 Util。也许在你的情况下 GeometryUtil 或类似的东西。

【讨论】:

  • 谢谢,知道在这种情况下经常使用 Util 名称很有帮助。我会开始做的。
【解决方案2】:

由于您应用的转换的细节似乎是针对问题的临时性的,并且将来可能容易发生变化,您可以将它们编码在配置文件中。

点的客户端将从文件中读取并知道要做什么。至于旋转或任何其他变换方法,它们可以很好地作为 Point 类的一部分。

【讨论】:

  • 谢谢,Jubbat。我将定义转换作为点类的一部分来实现,并且没有考虑过做一个配置文件。
【解决方案3】:

我认为只有一个成员的类/接口没有什么特别的问题。

在您的情况下,该成员是“带有返回相同类型的一种类型的某些参数的操作”-对于某些数学/功能问题很常见。您可能会发现拥有将多个转换类组合在一起以形成更复杂的转换的接口/基类和辅助方法很方便。

替代方法:如果您的语言支持,则完全采用函数式风格(类似于 C# 中的 LINQ)。

关于函数式风格建议:我从以下基本函数开始(可能只是在该语言的标准库中找到它们)

  • collection = map(collection, perItemFunction) 转换集合中的所有项目(C# 中的Select
  • item = reduce (collection, agregateFunction) 将所有项目简化为单个实体(C# 中的Aggregate
  • 在项目funcOnItem = combine(funcFirst, funcSecond) 上组合2 个功能。可以在 C#Func<T,T> combined = x => second(first(x)) 中表示为 lambda。
  • “bind”/curry - 修复函数functionOfOneArg = curry(funcOfArgs, fixedFirstArg) 的参数之一。可以在 C# 中表示为 lambda Func<T,T> curried = x => funcOfTwoArg(fixedFirstArg, x)

此列表可让您执行类似“将 X 轴上的集合中的所有点旋转 10 并将 Y 移动 15”之类的操作:map(points, combine(curry(rotateX, 10), curry(shiftY(15)))

语法取决于语言。 IE。在 JavaScript 中,您只需传递函数(并且 map/reduce 已经是语言的一部分),C# - lambda 和 Func 类(如参数函数 - Func<T,R>)是一个选项。在某些语言中,您必须显式使用类/接口来表示“函数”对象。

替代方法:如果您实际处理点和转换,另一种传统方法是使用 Matrix 来表示所有线性运算(如果您的语言支持自定义运算符,您将获得看起来非常自然的代码)。

【讨论】:

  • Alexei:我真的很喜欢像 LINQ 一样采用函数式风格的想法。你能举个例子说明我在这里怎么做吗?我认为定义 Linq 表达式的唯一方法是将内联作为类的成员。
【解决方案4】:

在您的问题域中查找候选类的最常见/自然的方法是look for nouns,然后扫描与这些名词相关的动词/动作以查找每个类应实现的行为。虽然这通常是一个很好的建议,但这并不意味着您的对象只能代表具体元素。当流程(通常被建模为方法)开始增长并变得复杂时,最好将它们建模为对象。因此,如果您的转换本身具有权重,则可以将其建模为对象并执行以下操作:

class RotateXY
{
public function apply(point p)
{
//Apply the transformation
}
}

t = new RotateXY();
newPoint = t->apply(oldPoint);

如果您有许多转换,您可以创建一个多态层次结构,甚至可以将一个转换一个接一个地链接起来。如果您想更深入地挖掘,还可以查看与此密切相关的 Command 设计模式。

一些最终的cmets:

  • 如果适合您的情况,最好在点级别对转换进行建模,然后将其应用于点集合。这样,您可以正确隔离转换概念,并且更容易编写测试用例。如果需要,您以后甚至可以创建 Composite 的转换。
  • 我通常不喜欢带有一堆静态方法的 Utils(或类似)类,因为在大多数情况下,这意味着您的模型缺少应该承载该行为的抽象。

HTH

【讨论】:

  • 非常有帮助,谢谢。这解决了我对静态类层次结构的不适,并为命名和模式提供了一些好主意。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多