2013-05-19 2 views
30

Скажите, не спрашивайте принцип here часто приклеивается ко мне, когда я использую геттеры или сеттеры, и люди говорят мне не использовать их. Сайт четко объясняет, что я должен делать, и что я не должен делать, но на самом деле это не объясняет, ПОЧЕМУ я должен сказать, а не спрашивать. Я использую геттеры и сеттеры гораздо эффективнее, и я могу сделать больше с ними.Использование getter/setter против «tell, not ask»?


Представьте класс Warrior с атрибутами health и armor:

class Warrior { 
    unsigned int m_health; 
    unsigned int m_armor; 
}; 

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

void Attacker::attack(Warrior *target) 
{ 
    target->setHealth(target->getHealth() - m_damage); 
    target->setArmor(target->getArmor() - 20); 
    // wait 5 seconds 
    target->setArmor(target->getArmor() + 20); 
} 

И с сказать, не проси принцип это будет выглядеть следующим образом (поправьте меня, если я ошибаюсь):

void Attacker::attack(Warrior *target) 
{ 
    target->hurt(m_damage); 
    target->reduceArmor(20); 
    // wait 5 seconds 
    target->increaseArmor(20); 
} 

Теперь второй, очевидно, выглядит лучше, но я не могу найти реальных преимуществ этого. Вам все еще нужно такое же количество методов (increase/decrease vs set/get), и вы теряете выгоду от того, чтобы спросить, когда вам нужно спросить. Например, как бы вы установили здоровье воинов до 100? Как вы узнаете, следует ли использовать heal или hurt, и сколько здоровья вам нужно для лечения или лечения?

Кроме того, я вижу, что сеттеры и геттеры используются некоторыми из лучших программистов в мире. Большинство API, использовать его, и он используется в станд Lib все время:

for (i = 0; i < vector.size(); i++) { 
    my_func(i); 
} 
// vs. 
vector.execForElements(my_func); 

И если я должен решить, стоит ли верить людям здесь ссылки мне одну статью о рассказывающие, не спрашивая, или считают, что 90% крупных компаний (apple, microsoft, android, большинство игр и т. д. и т. д.), которые успешно сделали много денег и работают над программами, мне очень трудно понять, почему расскажите, t ask быть хорошим принципом.

Почему я должен использовать его (должен ли я?), Когда все становится проще с помощью геттеров и сеттеров?

+0

Вместо того, чтобы использовать одиночную броню или одиночную броню, вы можете использовать список hp_reduction и armor_reduction, чтобы, когда несколько противников попали в одну и ту же жертву, они не повреждают здоровье и броню из-за того, что несколько потоков получают доступ к одной и той же переменной для здоровья и брони , Ожидание 5 секунд в потоке не так ли? Как вы можете быть уверены, что он ждет ровно 5 секунд? Вам также могут понадобиться планировщики задач. –

+7

@huseyintugrulbuyukisik Спасибо, но я не пытаюсь написать здесь рабочую игру ._. это был просто простой пример –

+0

Тогда вы можете пойти с простейшими заданиями присваивания. Warrior.HP- = 5; –

ответ

32

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

Вы получили это неправильно. Дело в том, чтобы заменить getVariable и setVariable на значимую операцию: например, inflictDamage. Замена getVariable на increaseVariable дает вам несколько более неясных имен для геттера и сеттера.

Где это имеет значение. Например, вам не нужно предоставлять сеттеру/получателю, чтобы отслеживать броню и здоровье по-другому, один inflictDamage может обрабатываться классом, пытаясь заблокировать (и повредить экран в процессе), а затем нанести урон на если экран недостаточен или ваш алгоритм требует этого. В то же время вы можете добавить более сложную логику в одном месте.

Добавить магический щит, который временно увеличивает урон, наносимый вашим оружием, на короткое время, например, при нанесении урона. Если у вас есть getter/seters, все злоумышленники должны видеть, есть ли у вас такой предмет, затем примените ту же логику в нескольких местах, чтобы, надеюсь, получить тот же результат. В скажите нападающим, которые все еще нуждаются в том, чтобы просто выяснить, сколько урона они нанесут, и рассказать об этом вашему персонажу. Затем персонаж может выяснить, как распространяется урон по предметам и влияет ли он на персонажа любым другим способом.

Усугубляет игру и добавляет огневое оружие, тогда вы можете получить inflictFireDamage (или передать урон от огня в качестве другого аргумента функции inflictDamage). Warrior может выяснить, зависит ли она от заклинания огнестойкости и игнорировать урон от огня, а не использовать все другие объекты в программе, чтобы выяснить, как их действие повлияет на других.

+3

Это был единственный дополнительный ответ, который действительно объяснил преимущества * tell, не спрашивайте *, а не просто сказал: «Дух, это дает вам лучшую инкапсуляцию». Вы полностью заслуживаете принятия, спасибо за ответ! :) –

2

Ну, если это так, зачем беспокоиться о геттерах и сеттерах? У вас могут быть только открытые поля.

void Attacker::attack(Warrior *target) 
{ 
    target->health -= m_damage; 
    target->armor -= 20; 
    // wait 5 seconds 
    target->armor += 20; 
} 

Причина проста здесь. Инкапсуляция. Если у вас есть сеттеры и геттеры, это не лучше, чем в общественном поле. Здесь вы не создаете структуру. Вы создаете правильный член вашей программы с определенной семантикой.

Цитируя статью:

Самая большая опасность в том, что, запрашивая данные от объекта, вы только получать данные. Вы не получаете объект - не в большом смысле. Даже если вещь, полученная вами от запроса, является объектом структурно (например, String), это уже не объект семантически. Он больше не связан с объектом своего владельца. Просто потому, что вы получили строку, содержимое которой было «RED», вы не можете задать строку , что это означает. Это фамилия владельца? Цвет автомобиля? текущее состояние тахометра? Объект знает эти вещи, данных нет.

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

target->setHealth(target->getArmor() - m_damage); 

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

Кроме того, вы ошиблись в std lib здесь. Getters и seters используются только в std::complex, и это связано с отсутствием функциональности языка (у C++ тогда не было ссылок). На самом деле все наоборот. Стандартная библиотека C++ поощряет использование алгоритмов, чтобы сказать, что делать с контейнерами.

std::for_each(begin(v), end(v), my_func); 
std::copy(begin(v), end(v), begin(u)); 
+0

Я бы предпочел изменить «здоровье», чтобы сохранить его> = 0, если мне не придется об этом беспокоиться лично. – chris

+0

И вообще забыть инкапсуляцию. Имеет смысл ... нет. Как вы тогда поддерживаете инварианты? – syam

+0

@syam Это не имеет значения. Это был просто ретирный вопрос. (как бы вы это сделали? Вы могли бы изменить тип «здоровье» на тот, который похож на int, но делает что-то другое. Плохая идея) – milleniumbug

1

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

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

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

+0

Неизменяющие значения hitpoints также могут быть сохранены в функции атаки. –

+0

Вы можете сделать большую часть этого в сеттере тоже, не так ли? –

0

С моей точки зрения, оба кода делают то же самое. Разница заключается в выразительности каждого из них. Первый (сеттеры anad getters) может быть более выразительным, чем второй (сказать, не спрашивайте).

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

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

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

Смежные вопросы