Объектно-ориентированный вопрос о парадигме

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

Позвольте мне дать небольшой пример ни на каком конкретном языке...

class Person {
    private int personnel_id
    private String first_name;
    private String last_name;
    private int personnel_level;
    //Lab labs[4]; <- Lab(s) the Person works in
}

class Lab {
    private int lab_id;
    private String lab_name;
    //Person[99] personnel; <- Person(s) working in the Lab
}

Позволяет игнорируют ctors/setters/getters/dtors на данный момент и просто инстанцируют некоторого материала...

Person people = new Person[1500];
Lab labs = new Lab[10];

Мой вопрос.. какова лучшая практика здесь...

people["Gordon Freeman"].blewUp((Lab)"Black Mesa");
-> returns T/F

или...

labs["BlackMesa"].blownUpBy((Person)"Gordon Freeman");
-> returns T/F

или возможно это даже не имеет значения :S

Реальным примером я продолжаю работать, является более сложная тонна. Каждый раз, когда Person делает что-то, всех в Lab потребности, которые будут уведомлены, и т.д., и я просто пытаюсь выяснить, существуют ли какие-либо принципы, которые я могу применить здесь.

8
задан David Titarenco 15 July 2010 в 07:27
поделиться

10 ответов

Мой ответ - это комбинация нескольких существующих ответов.

Основная проблема здесь в том, что здесь есть скрытая концепция. Метод на самом деле говорит не об объекте lab или объекте person, а об отношениях между ними. (Как предложили @dacris и @vs.)

Один из способов справиться с такими ситуациями - использовать язык с двойным диспетчеризацией (Спасибо, @Ken.)

Другой способ - иметь автогенерируемый код (Спасибо @vs.), в этом случае методы будут доступны в любом направлении.

Но часто такие решения непрактичны - изменение целых языков из-за этого кажется излишеством.

Автогенерируемое решение дает нам понимание, однако. Обе техники должны быть законными. Поэтому вы можете реализовать обе техники вручную.

Однако, если вы не хотите повторяться, этот подход дает понять, что КАЖДЫЙ способ законен. Так что не стоит сильно париться.

Если вы разрабатываете систему, в которой объект Person имеет и другие применения, кроме взрыва вещей, то будет лучше, если связь будет идти от Lab к Person (т.е. поместить методы в объект Lab), чтобы объект Person можно было использовать в других местах без необходимости вносить изменения в объект Lab или методы, связанные со взрывом.

... и наоборот. Если человек только и делает, что взрывает вещи, то логика должна быть там, чтобы сохранить лабораторию чистой и нетронутой (что важно для лабораторий!)

.
3
ответ дан 5 December 2019 в 18:56
поделиться

Возможно, вы захотите немного прочитать о шаблонах «Наблюдатель» и «Публикация / подписка». То, что вы описываете, в значительной степени является классическим приложением для паттерна Observer. Шаблон pub / sub - это, по сути, та же идея, но немного более абстрагированная для облегчения масштабирования.

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

2
ответ дан 5 December 2019 в 18:56
поделиться

oO дам вам другой взгляд на это: на самом деле вас интересуют не персоны или лаборатории, а отношение между ними. Если вы посмотрите на это с точки зрения UML или базы данных, вы увидите, что это отношение является очень новым понятием в вашей (ментальной) модели. См. также комментарий @dacris выше, где он вводит новый класс.

Если бы вы использовали ORM (Object-Relational Mapping), как это делается при разработке UML-моделей, эти два метода blowsUp() и blownUpBy() были бы автоматически сгенерированы в коде, с соответствующими проверками во время выполнения для обеспечения их согласованности.

В книге Лармана действительно должно быть что-то на эту тему.

1
ответ дан 5 December 2019 в 18:56
поделиться

Я не совсем уверен, что означает ваш пример, но

Отличная книга, в которой есть то, что вы хотите, - это Применение UML и шаблонов Крейга Лармана.

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

1
ответ дан 5 December 2019 в 18:56
поделиться

Мне нравится разрабатывать такие вещи:

let blackMesa = labs["BlackMesa"]
if (blackMesa.isDestroyed) 
{
    let destroyer = blackMesa.destroyer
}
0
ответ дан 5 December 2019 в 18:56
поделиться

Вы правы. Я думаю, что это одна из основных проблем большинства сегодняшних объектно-ориентированных систем: часто кажется, что методы естественным образом «принадлежат» объекту, но часто это не так.

Системы с множественной диспетчеризацией аккуратно избегают этой проблемы. Например, в Дилане вы можете сказать что-то вроде:

define method blows-up(p :: <person>, l :: <lab>) => explodes :: <boolean>;
  // ...returns #f or #t...
end method;

(Я связался со страницей MultiMethods c2.com, потому что я думаю, что это наименее плохая работа по описанию этого. В Википедии есть страница для Multiple_Dispatch, но ее пример довольно ужасно.)

1
ответ дан 5 December 2019 в 18:56
поделиться

Думайте так, как будто вы говорите по-английски. Общее правило таково: глаголы (и методы) должны иметь "активный залог", насколько это возможно - то есть, объект должен что-то делать, а не чтобы с ним что-то делали.

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

1
ответ дан 5 December 2019 в 18:56
поделиться

Я думаю, что это связано скорее с реальным миром и вашими правилами кодирования, чем с общей хорошей практикой. Для вашего английского языка я все еще предпочитаю call people.notify(lab). Однако, если вы хотите, чтобы ваша лаборатория имела некоторые данные о том, кто ей звонил, какой человек, вы можете сделать lab.isNotifiedBy(people).

Хорошая практика здесь заключается в том, что это имеет смысл для вас и вашего коллеги, когда вы смотрите на код, они понимают, что он делает, если они хотят найти метод, они знают, с чего им следует начать, а не просто продолжать поиск или спрашивать вас

.
0
ответ дан 5 December 2019 в 18:56
поделиться

какой здесь лучший метод ...

Как пользователь собирается использовать систему, зависит от вашего варианта использования ?. Будет ли это лаборатория, которую «взорвет» Человек ? или вариант использования системы состоит в том, чтобы несколько Person взорвали Labs ?

или, может быть, это даже не имеет значения: S

В конечном итоге результат тот же, но здесь важна семантика кода. Если звучит глупо, что люди взрывают Лаборатории, не делайте этого.

Итак, золотое правило, как упоминает BobTurbo, состоит в том, чтобы найти «эксперта по информации» (см .: GRASP ) в системе и передать управление этому объекту.

Обычно вы определяете историю пользователя или рассказ о том, как будет использоваться система, если, например, описание выглядит следующим образом:

Когда человек что-то делает, все в лаборатории должны быть уведомлены.

Тогда для меня это означает, что Человек работает в Лаборатории , когда она будет создана, этот человек может получить лабораторию, в которой он работает, и зарегистрироваться для получения уведомления о том, что происходит в этой лаборатории.

Поскольку в лаборатории есть список лиц, которых необходимо уведомить, имеет смысл быть лабораторией, которая выполняет уведомление (в данном случае пользователь передает управление лаборатории)

Тогда, вероятно, человек может можно определить как:

labs.Person {
     - name: String
     - lab : Lab 

     + Person( withLab: Lab , andName: String ) {
           self.lab = withLab
           self.name = andName
           self.lab.subscribe( self ) // want to know what happens
      }


     + blowUpLab() {
           lab.boom!(blownBy:self)
       }
       // receive a lab even notification 
       // performed by "someone" 
     + labEvent( lab:Lab, by: Person  ) {
          // if is my lab and it wasn't me?
          if( self.labg .== lab .&& self .!= by ) {
             // ok it was someone else.... 
          }
       }
  }

Итак, человек что-то делает в лаборатории, в данном случае публичный метод blowUpLab , который просто взрывает лабораторию человека, вызывая бум лаборатории! метод.

В свою очередь Лаборатория выполняет действия метода и уведомляет всех своих подписчиков в конце:

labs.Lab {
    - labName:String
    - subscribers: Person[0..*]

    + subscribe( to: Person ) {
          subscribers.add( to ) 
      }

    + boom!( blowedBy: Person ) {
         // do blow up the lab 
         .... 
         // and then notify:
        subscriber.forEach( person: Person ) {
             person.labEvent( self, blowedBy )
         }
     }
 }

Это шаблон наблюдателя.

Наконец, ваше основное приложение создаст людей и лаборатории и выполнит вариант использования:

 labs.MainApp {
     _ main() {
          blackMesaLab = Lab("BlackMesa")
          gordon = Person( withLab: blackMesaLab, andName: "Gordon Freeman")
          scott  = Person( withLab: blackMesaLab, andName: "Scott Tiger")
          james  = Person( withLab: Lab("SO Labs"), andName:" James Hetfield");

          persons = Array( gordon, scott, james )

          .... 

         while( true ) {
              // every now and then, have someone blowing up it's lab 
              if ( randomNumber() .== 42 ) {
                  person.at( randomPosition ).blowUpLab()
             } 
         }
       }
   } 

Это главное приложение создаст трех человек с некоторой лабораторией, только двое из них связаны (Скотт и Гордон)

Случайно один из них получит сообщение blowUpLab и выполнит метод. Лаборатория, в свою очередь, уведомит всех подписчиков этой лаборатории.

Итак, когда Джеймс Хэтфилд взорвет его лабораторию, никто не будет уведомлен :)

Дело в том, Опишите свой вариант использования и укажите там эксперта по информации; передать управление этому объекту и позволить этому объекту полагаться на управление другим объектом, но только в соответствии с вашим вариантом использования

Я надеюсь, что это имеет смысл.

0
ответ дан 5 December 2019 в 18:56
поделиться

В этом случае я хотел бы представить новый объект - LabExplosion

class LabExplosion
{
    private Person personResponsible;
    private Lab labAffected;
}

Затем сохраните где-нибудь репозиторий LabExplosions и выполните что-то вроде:

// To find out if Gordon Freeman ever blew up Black Mesa
explosions.find("Gordon Freeman", "Black Mesa").length > 0;
// returns T/F
0
ответ дан 5 December 2019 в 18:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: