【问题标题】:What's the most appropriate way to expose lists inside a class?在类中公开列表的最合适方法是什么?
【发布时间】:2010-01-12 11:21:36
【问题描述】:

想象以下模型:

  • 一个有很多
  • 一个有很多单元格

以“面向对象的方式”处理这些类的更好接口是什么?

1 - 提供对属性 rows / cells 的访问(不一定公开底层数据结构,但创建例如类 RowCollection...)

my_table = new Table()
my_table.rows.add([1,2,3])
my_row = my_table.rows.get(0)
my_row.cells.get(0)
for(cell in my_row.cells) {}
...

2 - 或者直接在 TableRow 类中提供方法

my_table = new Table()
my_table.add_row([1,2,3])
my_row = my_table.get_row(0)
my_row.get_cell(0)
for(cell in my_row.get_cells) {}
...

3 - 以上都不是...

【问题讨论】:

  • 主要用例是添加、排序和迭代值

标签: language-agnostic oop encapsulation information-hiding


【解决方案1】:

我认为答案在很大程度上是主观的。如果我们以您的示例为例,提供类的方法或属性以通过行/列引用返回值可能是合适的。这些可以同时实现,例如:

myClass.Row[x].Column[y]    
myClass.Column[y].Row[x]    
myClass.Cell[x,y]

如果数据“行”是有限的,您可能还决定直接公开列表更好:

myClass.SomeArrayOfValues[itemIndex]

我注意到您使用“表”和“行”之类的短语,因此我可能假设您希望您的类表示数据库或类似结构,但您可能会发现虽然以这种方式存储数据可能很有效,您可能会发现以其他形式公开数据可能对您的班级用户更有意义。

最后,您应该如何选择这样做,以反映数据本身和您正在建模的系统的目的,这只能根据具体情况来决定。

【讨论】:

  • 既然如此,如果这样做对您有意义,您甚至可以公开一个方法或属性,该方法或属性使用来自您的应用程序的字符串,该应用程序使用标准电子表格单元格表示法(例如:“A1” ,“D3”等...)。不过,这可能是最好的扩展或包装,就像我在上面的回答中给出的示例一样。
【解决方案2】:

根据您对用法的评论,“主要用例是添加、排序和迭代值”,我可能不允许检索单个元素,而是让用户提供一个仿函数来对存储的元素进行操作元素。用 C++ 表达。

class Table
{
public:
    Table();
    //Table(unsigned int numberOfRows, unsigned int numberOfCells);

    void addRow();
    void addCell();

    //Throw exception if out of range or don't supply these functions if not needed by user.
    void removeRow(unsigned int rowNumber);
    void removeCell(unsigned int rowNumber, unsigned int cellNumber);

    //Iterate over entire table
    template<class Pred>
    void forEach(Pred pred);

    //Iterate over a specific row, throw exception if row is out of range.
    template<class Pred>
    void forEach(unsigned int row, Pred pred);
}

您必须根据计划输入/更新数据的方式来定制添加/更新/删除调用。这种设计强烈倾向于操纵元素的集合。这种设计的好处是你没有让你的用户接受你如何表示表格的特定底层结构。这符合得墨忒耳法则。

如果您需要访问特定的单个元素,您将需要一种不同的方法或使其成为此处已提供内容的扩展。

【讨论】:

    【解决方案3】:

    考虑一下您可以摆脱多少个 getter 和 setter。一个健壮的 OO 设计具有相互导出行为的对象,而不是数据。例如,Person 的 getter/setter 模型的骨架:

    class Person:
      def set_name(value):
      def get_name:
      def set_age(value):
      def get_age:
      def set_drink(value):
      def get_drink:
      def set_favorite_drink(value):
      def get_favorite_drink:
    

    下面是一些使用 Person 的(伪)代码:

    def order_drink(person)
      if person.age >= 21:
        puts "#{person.name} can have a drink"
        person.drink = bar.order(person.favorite_drink)
      else:
        puts "#{person.name} cannot drink (legally)."
    

    以下是您如何让一个没有吸气剂或设置剂的人参与点酒:

    class Person:
      def order_drink_from(bar):
        if self.age >= 21:
          puts "#{self.name} can have a drink"
          self.drink = bar.order(favorite_drink)
        else:
          puts "#{self.name} cannot drink (legally)"
    

    这样使用:

    person.order_drink_from(bar)
    

    我不会说您在 OO 程序中永远不需要 getter。但我要说的是:尤其是二传手,应该让你重新思考设计。每次编写 getter 或 setter 时,请让脑后有个小声音问您是否有办法让对象导出行为而不是数据。

    【讨论】:

      【解决方案4】:

      这取决于您打算如何访问行/单元格。

      没有一种正确的方法 - 您需要决定如何访问它们并构建您的对象以您希望使用它们的方式公开它们。

      【讨论】:

        【解决方案5】:

        这在很大程度上取决于数据以及您打算如何处理这些数据。 用户会想要单个单元格吗?单独的行/列?行/列的子部分?

        可能最简洁的方法是提供函子接口。提供一个或多个函数,这些函数将在每个元素上运行仿函数,或者在仿函数中定义的子集上运行仿函数。

        如果用户需要访问复杂的单元格组合,这可能效果不佳。

        【讨论】:

          猜你喜欢
          • 2017-12-30
          • 1970-01-01
          • 2017-11-09
          • 2015-12-25
          • 2013-06-07
          • 1970-01-01
          • 1970-01-01
          • 2011-07-10
          • 1970-01-01
          相关资源
          最近更新 更多