代码之家  ›  专栏  ›  技术社区  ›  Roman A. Taycher

是对类(动态类型的OO语言)持有的列表进行迭代的A模式

  •  2
  • Roman A. Taycher  · 技术社区  · 15 年前

    如果我有一个包含一个或多个列表的类,允许其他类获取这些列表(使用getter)更好吗?或实现 doXyzList/eachXyzList 为该列表键入方法,传递函数并对该对象所包含的列表的每个元素调用该函数?

    我写了一个程序,做了很多这样的工作,我讨厌把所有这些列表传来传去,有时在课堂上用方法 A 在类中调用方法 B 返回类中包含的列表 C . 包含一个 C 或多重 C s。

    ( 笔记 这个问题是关于动态类型的OO语言,如Ruby或Smalltalk)

    例(在我的程序中出现的):

    在一 Person 包含调度首选项和 scheduler 需要访问它们的类。

    6 回复  |  直到 15 年前
        1
  •  4
  •   mdma    15 年前

    没有一个单一的答案-设计是关于如何处理优先级、权衡和竞争,以达到在您的环境中工作良好的目标。我将简要介绍使用函数、访问器和完全封装的相对优缺点。

    函子

    使用函数可以很方便,并且避免了样板迭代。这还允许您在执行列表中的每个项时,将正在执行的内容与正在执行的内容完全分开。对于每个循环,这两个循环通常是耦合在一起的。如果需要在多个列表上执行操作,或者从同一对象执行操作,或者从多个对象执行操作,或者只需要列表中的几个元素,那么函数就不起作用。使用functors约束执行顺序-项必须按提供程序迭代的顺序使用。该函数没有控件。这可能是一种祝福,也可能是一种诅咒。

    作为人员、调度首选项和调度程序的示例,调度程序可以为可能的调度时间提供外部迭代器:

       schedules = scheduler.schedules(person.getSchedulePreferred())
    

    getschedulePreferred()返回一个谓词,该谓词从给定人员首选的所有可用计划中选择计划。

    在所有调度之间迭代可能不是实现这一点的最有效方法。比如说,如果这个人只想在6月得到日程表,那么剩下的一年中的所有日程表都将被浪费地重复。

    访问器

    当实现不是类固有的功能时,通过gtters使列表可用是有益的。例如,给定两个订单,查找它们的共同项。如果列表是作为外部遍历的getter提供的,那么实现起来很简单;如果列表是以某个已知的顺序提供的(例如,如果订单具有 getSortedItems() 这里的复杂性是管理列表的可更改性,尽管许多语言直接支持禁用突变(例如C++中的const),或者将列表封装在不可变的包装器中。

    例如,该人员可以公开计划首选项列表,然后由计划程序直接使用。调度程序有机会“智能”地了解如何应用首选项,例如,它可以构建一个对数据库的查询,以根据人员首选项获取匹配的调度。

    包封

    第三种选择是质疑是否需要外部访问。这是一个症状 anemic domain model 这些对象都是属性,没有行为。但是不要为了避免这种反模式而努力将行为放到域对象中——行为应该是该对象职责的自然组成部分。

    我不认为这在这里适用——人员、调度人员和调度人员的偏好明确地履行了不同的角色,并有明确的责任。在试图从另一个实体计算数据的实体上添加方法是不必要的紧密耦合。

    总结

    在这个特定的例子中,我的首选项是getter,因为它允许调度程序更多地控制如何使用调度首选项,而不是通过函数“强制”它们。

        2
  •  3
  •   Mark Peters    15 年前

    最好两者都不要做。封装这些列表,如果可能,将操作本身移动到类中。然后打电话 doSomeOperation() 它将在内部根据需要迭代,而不公开您类型的内部数据结构。

    例如,这样做通常是更好的设计:

    public class ShoppingCart {
       private final List<CartItem> items;
       public double getTotal() {
          double total = 0;
          for ( CartItem item : items ) {
             total += item.getPrice() * item.getQuantity();
          }
          return total;
       }
    }
    

    而不是公开要制作的项目列表 getTotal 购物车外部。

    如果这不可能或非常不切实际,至少只能提供这些列表的不可修改视图。

        3
  •  0
  •   Binary Worrier    15 年前

    我(个人)不喜欢“Doxlist/eachxlist”和 list.for_each(Action) 功能。
    根据定义,这些方法执行副作用,副作用应该被标记。

    一般语言特征 foreach(item in collection) 是显而易见的,众所周知的,在我看来,足够了。

    这是个人决定,埃里克·利珀特在 this blog post . 文章是C特定的,但是这些参数一般适用于语言,即使它们是用C和C编译器来表达的。

    希望这有帮助

        4
  •  0
  •   Robert    15 年前

    我认为使用吸气剂是很讨厌的——它违反了知识最少的原则。所以函数在这方面更好,但我认为它仍然可以更灵活。您可以获取一个策略对象,并将该列表传递给它,以便它转换它——而不是获取一个函数并迭代该列表以及在上面运行该函数。

    class A{
    
        List<B> bs;
        List<C> cs;
    
        // better than getter but could be better
        void transformBs(Functor f){
            for(B b : bs){
                f.transform(b);
            }
        }
    
        // more flexible
        void transformCs(Strategy s){
            s.transform(cs);
        }
    }
    interface Functor{ <T> void transform(T t);}
    interface Strategy{ <T> void transform(List<T> list); }
    
        5
  •  0
  •   Martijn Courteaux    15 年前

    你的问题是:

    是迭代列表的A模式

    我想只有一个叫做“迭代器”的简单模式,不是吗?

    类似的东西(例如在Java中)

    public class ListHolder<T>
    {
        private List<T> list = new ArrayList<T>();
    
        public Iterator<T> newIterator()
        {
            return new Iterator();
        }
    
        public class Iterator <T>
        {
            int index = 0;
            public T next()
            {
                return list.get(index++);
            }
            public boolean hasNext()
            {
                return list.size() > index;
            }
        }
    }
    
        6
  •  0
  •   OscarRyz    15 年前

    我认为最好将带有逻辑的对象传递给容器类,并让它调用该方法。

    例如,如果 Scheduler 是逻辑类, Person 包含集合的类,以及 SchedulePref 我宁愿收藏:

     Person {
        - preferences: Preference[]
    
         + scheduleWith( scheduler: Scheduler ) {
                preferences.each ( pref: Preference ) {
                      scheduler.schedule( pref )
                }      
           }
      }
    

    在客户中

     Client {
          main() {
              scheduler = SchoolScheduler.new
              person    = Person.withId( 123 )
              person.scheduleWith( scheduler )
          }
     }
    

    并且有一个混凝土的子类 调度程序 每个都有不同的算法。

    它就像接收块,但不是暴露支架的内部( )以及逻辑的内部 调度程序 )您传递的函数也封装在“逻辑类”中 调度程序 )

    想想这个。如果你生病了,你吃了一粒药丸,在体内,你可能认为它作用于你的内部(可能是,在体内,你知道一些药丸必须进入肺部而不是肝脏等)。

    另一种方式是:把你所有的器官都给我,然后在体外对它们做些什么。这可不是什么好事。

    推荐文章