没有单一的答案 - 设计是关于兼顾优先级、权衡和妥协,以达到在您的情况下运作良好的东西。我将简要介绍使用函子、访问器和完全封装的相对优点和缺点。
函子
使用仿函数很方便,并且避免了样板迭代。这还允许您将列表中每个项目的执行内容与执行时完全分开。在 for-each 循环中,两者最常耦合在一起。仿函数不起作用的地方是,如果您需要对多个列表执行操作,无论是来自同一个对象,还是来自多个对象,或者您只需要列表的几个元素。仿函数的使用限制了执行顺序 - 项目必须按照提供者迭代的顺序使用。函子无法控制。这可能是一种祝福,也可能是一种诅咒。
以人、调度偏好和调度器为例,调度器可以为可能的调度时间提供一个外部迭代器:
schedules = scheduler.schedules(person.getSchedulePreferred())
getSchedulePreferred() 返回一个谓词,该谓词从给定人员首选的所有可用时间表中选择时间表。
遍历所有计划可能不是实现这一点的最有效方式。比如说,如果这个人只想要 6 月份的时间表,那么今年剩余时间的所有时间表都将被浪费地迭代。
访问器
在实现非类固有的功能时,通过 gtters 使列表可用是有益的。例如,给定两个订单,找出它们共有的项目。如果列表作为外部遍历的 getter 提供,这很容易实现,如果列表以某种已知的顺序提供(例如,如果 Order 有getSortedItems()
方法)则非常简单。这里的复杂性是管理列表的可变性,尽管许多语言直接支持禁用突变(例如 C++ 中的 const)或将列表包装在不可变包装器中。
例如,该人可以公开日程偏好列表,然后由日程安排程序直接使用。调度程序有机会“聪明”地了解如何应用偏好,例如,它可以构建对数据库的查询,以根据个人偏好获取匹配的调度。
封装
第三种选择是质疑是否需要外部访问。对象都是属性而没有行为是贫血域模型的一个症状。但是不要为了避免这种反模式而努力将行为放在域对象中 - 行为应该是该对象职责的自然部分。
我认为这不适用于这里 - 人员、调度员和调度偏好显然履行不同的角色并有明确的职责。在一个实体上添加一个尝试从另一个实体计算数据的方法将是不必要的紧密耦合。
概括
在这种特殊情况下,我更喜欢 getter,因为它允许调度程序更好地控制调度首选项的使用方式,而不是通过仿函数“强制输入”它们。