Вопрос объектно-ориентированной парадигмы - PullRequest
8 голосов
/ 15 июля 2010

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

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

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 должны быть уведомлены и т. Д., И я просто пытаюсь выяснить, есть ли какие-либо принципы, которые я могу применить здесь.

Ответы [ 10 ]

3 голосов
/ 15 июля 2010

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

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

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

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

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

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

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

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

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

2 голосов
/ 15 июля 2010

Возможно, вы захотите немного прочитать о шаблонах Observer и Publish / Subscribe.То, что вы описываете, в значительной степени является классическим приложением для шаблона Observer.Паттерн pub / sub в основном та же идея, абстрагированная для масштабирования.

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

1 голос
/ 15 июля 2010

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

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

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

1 голос
/ 15 июля 2010

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

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

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

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

1 голос
/ 15 июля 2010

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

Отличная книга, в которой есть то, что вам нужно, это Применение UML и паттернов КрейгаЛарман.

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

1 голос
/ 15 июля 2010

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

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

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

какая здесь самая лучшая практика ...

Это зависит от вашего варианта использования , как пользователь будет использовать систему ?. Будет ли лаборатория "взорвана" Person? или в случае использования системы нужно взорвать Person Labs?

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

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

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

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

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

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

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

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

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, который просто взрывает лабораторию человека, вызывая метод boom! Лаборатории.

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

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 голосов
/ 15 июля 2010

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

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

Затем сохраните где-нибудь хранилище LabExplosion и сделайте что-то вроде:

// To find out if Gordon Freeman ever blew up Black Mesa
explosions.find("Gordon Freeman", "Black Mesa").length > 0;
// returns T/F
0 голосов
/ 15 июля 2010

Мне нравится создавать такие вещи:

let blackMesa = labs["BlackMesa"]
if (blackMesa.isDestroyed) 
{
    let destroyer = blackMesa.destroyer
}
0 голосов
/ 15 июля 2010

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

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

...