Шаблон для перебора списков, содержащихся в классе (динамически типизированные ОО-языки) - PullRequest
2 голосов
/ 05 июня 2010

Если у меня есть класс, который содержит один или несколько списков, лучше ли разрешить другим классам извлекать эти списки (с помощью метода получения)?Или реализовать метод типа doXyzList/eachXyzList для этого списка, передавая функцию и вызывая эту функцию для каждого элемента списка, содержащегося в этом объекте?

Я написал программу, которая сделала тонну этого, и я ненавиделобойти все эти списки, иногда с помощью метода в классе A, вызывая метод в классе B для возврата списков, содержащихся в классе C.B содержит C или несколько C.

( note Этот вопрос касается динамически типизированных языков OO, таких как ruby ​​или smalltalk)

ех.(это появилось в моей программе):

для класса Person, содержащего параметры планирования и класс scheduler, для которого необходим доступ.

Ответы [ 6 ]

4 голосов
/ 08 июня 2010

Единого ответа не существует - дизайн состоит в том, чтобы совмещать приоритеты, компромиссы и компромиссы, чтобы получить что-то, что хорошо работает в вашей ситуации. Я кратко расскажу об относительных достоинствах и недостатках использования функторов, методов доступа и полной инкапсуляции.

функторы

Использование функторов может быть удобным и позволяет избежать итерации. Это также позволяет вам четко отделить то, что вы выполняете для каждого элемента в списке, от момента его выполнения. С циклом for-each эти два чаще всего связаны вместе. Функторы не работают, если вам нужно выполнить операцию над несколькими списками, либо из одного и того же объекта, либо из нескольких объектов, либо если вам нужны только несколько элементов списка. Использование функторов ограничивает порядок выполнения - элементы должны использоваться в порядке, повторяемом поставщиком. Функтор не имеет контроля. Это может быть благословением, а также проклятием.

В качестве примера Person, предпочтений планирования и планировщика, планировщик может предоставить внешний итератор для возможных времен расписания:

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

getSchedulePreferred () возвращает предикат, который выбирает расписания из всех доступных, которые предпочитает данный человек.

Итерации по всем графикам могут быть не самым эффективным способом реализации этого. Скажем, если человек хочет только графики в июне, то все графики на оставшуюся часть года будут расточительно повторяться.

Accessors

Создание списков, доступных через gtters, может быть полезно при реализации функциональности, не свойственной классу. Например, с учетом двух Орденов найдите общие для них предметы. Это просто реализовать, если списки предоставляются как получатели для внешнего обхода, и очень просто, если списки предоставляются в некотором известном порядке (например, если у Order есть метод getSortedItems().) Сложность здесь заключается в управлении изменчивостью списка, хотя многие языки имеют прямую поддержку для отключения мутации (например, const в C ++) или обертывания списка в неизменяемую оболочку.

Например, человек может предоставить список предпочтений расписания, которые затем будут использованы непосредственно планировщиком. Планировщик имеет возможность быть «умным» в отношении того, как применяются предпочтения, например, он может создать запрос к базе данных для получения соответствующих расписаний на основе предпочтений сотрудников.

Encpasulation

Третий вариант - вопрос, требуется ли внешний доступ. Это один из признаков модели анемичной области , что все объекты имеют свойства и не имеют поведения. Но не пытайтесь поместить поведение в объект домена только для того, чтобы избежать этого анти-паттерна - поведение должно быть естественной частью ответственности этого объекта.

Я не думаю, что это применимо здесь - предпочтения человека, планировщика и планирования четко выполняют разные роли и имеют четкие обязанности. Добавление метода к одному объекту, который пытается вычислить данные из другого, был бы ненужной тесной связью.

Краткое описание

В этом конкретном случае я предпочитаю геттер, поскольку он позволяет планировщику лучше контролировать использование предпочтений расписания, а не «принудительно их» через функтор.

3 голосов
/ 08 июня 2010

Лучше не делать ни того, ни другого. Инкапсулируйте эти списки и, если возможно, переместите саму операцию в класс. Затем просто вызовите 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 внешним для ShoppingCart.

Если это невозможно или очень непрактично, по крайней мере, предоставьте только неизменяемые представления этих списков.

0 голосов
/ 15 июня 2010

Я думаю, что было бы лучше передать объект с логикой классу контейнера и позволить ему вызывать метод.

Например, если 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 )
      }
 }

И иметь подкласс для конкретного Scheduler, каждый из которых имеет свой алгоритм.

Это все равно, что получить блок, но вместо того, чтобы раскрывать внутреннюю часть держателя (Person) и внутреннюю часть логики (Scheduler), вы передаете функцию, также инкапсулированную в "логический класс ( Scheduler)

Подумайте об этом. Если вы заболели, вы принимаете таблетку, и внутренне вы можете думать, что она действует на ваши внутренние органы (может быть, внутренне вы знаете, что какая-то таблетка должна поступать в легкие, а не в печень и т. Д.)

Иначе было бы так: дай мне все свои органы, а потом сделай что-нибудь с ними вне тела. Это не что-то хорошее.

0 голосов
/ 15 июня 2010

Ваш вопрос:

Является ли шаблон для перебора списков

Я думаю, что существует простой шаблон, называемый "итератор", нет?

Примерно так: (пример на 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;
        }
    }
}
0 голосов
/ 13 июня 2010

Я думаю, что использование геттера неприятно - оно нарушает Принцип Наименьшего Знания.Таким образом, функтор лучше в этом отношении, но я думаю, что он все еще может быть более гибким.Вы можете взять объект стратегии и передать ему список, чтобы он преобразовал его - вместо того, чтобы брать функцию и повторять список, выполняя функцию на нем.

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); }
0 голосов
/ 08 июня 2010

Мне (лично) не нравятся функции "doXList / eachXList" и list.for_each(Action).
По определению, эти методы выполняют побочные эффекты, и побочные эффекты должны быть отмечены.

Общие черты языка в соответствии с foreach(item in collection) очевидны и хорошо известны и, на мой взгляд, достаточны.

Это личное решение, и Эрик Липперт говорит об этом в этом посте . Этот пост специфичен для C #, но аргументы применимы к языкам в целом, даже если они сформулированы в терминах C # и компилятора C #.

Надеюсь, это поможет

...