Object.DoSomething() против DoSomethingWith(Object)
Это может быть просто вопросом предпочтений, однако мне интересно знать, каков наилучший способ использования любого из этих подходов.
например
var person = new Person();
person.Run();
в отличие от
var person = new Person();
Excercise.Run(person);
Приведенный выше пример может быть не самым лучшим, но мой общий смысл заключается в том, когда вы должны решить возложить на объект ответственность, а не на другой класс?
12 ответов
Не делай вещи для своих предметов. Они здесь, чтобы сделать что-то для тебя.
Это звучит довольно упрощенно, но это полезный принцип, которому нужно следовать. Это означает (как вы определили) вызов методов для объекта, и он будет использовать все имеющиеся у него знания для получения результата. Это усиливает инкапсуляцию и разделение / сдерживание ответственности
Индикатором того, что этого не происходит, является код, подобный следующему:
priceBond(bond.getPrincipal(), bond.getMaturity(), bond.getCoupons(), interestRate)
где объект облигации передает всю свою информацию какой-либо третьей стороне. Код, подобный приведенному выше, в конечном итоге будет дублироваться. Вместо этого напишите
bond.priceBond(interestRate)
и хранить всю информацию, связанную в одном объекте.
Если ваши объекты страдают от огромного числа геттеров, то это может быть индикатором того, что ваши объекты не выполняют то, что им положено.
Вообще говоря, эта конструкция OOPish:
person.Run(distance);
это просто синтаксический сахар для:
Person.Run(person, distance);
где person
становится неявным this
ссылка. Есть некоторые тонкости с виртуальными функциями и тому подобное, но вы поняли идею.
Что касается вашего вопроса, вы в основном имеете модель с богатой областью по сравнению с анемичной моделью, и это является предметом множества споров.
Обычно класс, в этом случае Person
имеет поведение;
И в этом случае поведение Run
и, таким образом, человек должен иметь метод Run
var p = new Person();
p.Run();
Первый несет гораздо больше концептуальной ясности. Человек бежит, беговое упражнение не потребляет человека.
Сравнение в идеале не является правильным по нескольким причинам: 1. В идеале каждый объект должен отвечать за свои собственные действия, например, в случае человека, человек будет отвечать за human.walk(), human.eat(), human.sleep() и т. д. 2. Параметр, который передается в действие, является потребляемым ресурсом для этого действия. Было бы неразумно говорить Life.walk(человек), поскольку прогулка - это не деятельность Жизни, а человек - не расходуемый ресурс. Здесь человек является объектом. Тем не менее, было бы разумно сказать human.eat (еда); где еда является потребляемым ресурсом. 3. Пример, который вы привели, кажется, показывает, что во втором случае Run - это статический метод, и для функционирования объекта вы редко хотите реализовать его как конструкцию статического метода.
В идеале шаблоны проектирования, если их правильно реализовать, помогут вам в том, каким образом функция будет вызываться в экземпляре, но в основном то, что будет передано методу, - это ресурс req. делать эту деятельность, а не объект действия.
Я надеюсь, что это прояснит ваши сомнения. Более подробно о шаблонах проектирования вы можете прочитать в некоторых книгах Мартина Фаулера. http://www.martinfowler.com/books.html
Если ответственность за Run лежит на человеке, я бы предложил person.Run(), если хотя Run и может обрабатывать другие типы объектов, и его можно как-то повторно использовать вне объекта person, тогда он может стоять сам по себе и вызывать его как Excercise.Run(person);
Для меня я бы пошел с person.Run();
Я согласен с первым ответом, что, если акт бега лучше всего "понимается" объектом-человеком, то именно здесь он должен находиться как для функциональности, так и для ясности.
Второй случай больше подходит для интерпретаций вне объекта и лучше всего выполняется через интерфейсы. Таким образом, вместо использования объекта person методы Excersize должны использовать интерфейс, скажем IExcersizable, который, например, перемещает конечности. Метод Excesize.run(IExersizable) может перемещать одну ногу, а затем другую в быстрой последовательности. Excesize.walk(IExersizable) может так же, но медленнее.
Затем человек может реализовать интерфейс, чтобы разобраться со спецификой движения конечностей.
Я вижу две ситуации, когда предпочтительным подходом является вызов статического метода для объекта:
- Если существует реальная вероятность того, что переданный параметр может не поддерживать указанное действие, и такое возникновение должно быть обработано изящно. Например, часто полезно иметь функцию, которая будет располагать объект, если он ненулевой и iDisposable, но безвредно ничего не делать иначе;
- Если метод делает что-то, что на самом деле не является широко применимым к его параметрам [например, тестирование, если у человека есть наручные часы, сделанные каким-то конкретным производителем; такая функциональность может использоваться достаточно часто, чтобы заслужить свой собственный метод, но, скорее всего, не будет принадлежать ни классу Person, ни классу WatchManufacturer].
Две вещи:
Это не дело вкуса, но имеет реальные технические последствия. Различия между функцией-членом и свободной функцией уже обсуждались достаточно подробно, я рекомендую взглянуть на книгу Effective C++ и / или статью Скотта Мейерса по адресу http://www.ddj.com/cpp/184401197 Короче говоря, если вы сделаете функцию функцией-членом, у вас будет чертовски веская причина для этого.
Аргумент читабельности следует принимать с осторожностью. Это очень сильно зависит от имени функции-члена. Помните правило SPO - объектный предикат: нет сомнений, что
person.run()
выглядит лучше чемrun(person);
(по крайней мере, для говорящего по-английски), но что, если у вас есть другие глаголы, кроме "беги". Какой-нибудь глагол, который берет объект, например, "позвонить", если вы хотите позвонить? сравнитьcall( person );
противperson.call();
, Бывший выглядит намного приятнее.
Я согласен с Брайаном. Просто для справки, я хотел указать на эту статью "Скажи, не спрашивай" и "Закон Деметры". Оба применимы здесь.
Есть небольшие отличия:
- person.Run () получает единственный параметр: это
- если Excercise.Run(person) является статическим методом, он также получает один параметр
- если Excercise является экземпляром, он получает два параметра: this и person
Очевидно, что третий подход необходим только в том случае, если вам нужно передать оба параметра. Я бы сказал, что первый подход лучше ООП, и я бы выбрал второй только в очень особых обстоятельствах (например, если человек был запечатан).
Так как Person
будет заниматься бегом, лучше иметь run
метод там.