Здравствуйте, Mikhail Polykovsky, Вы писали:
MP>Как вы считаете, какой из вариантов более аккуратный? Какой облегчает поддержку и дальшейшие изменения программы?
Это зависит от задачи, IMHO. Абстрактно дать лучший ответ сложно, но я бы, в тех условиях что даны, по умолчанию считал лучшим вот такой вариант:
Здравствуйте, Mikhail Polykovsky, Вы писали:
MP>Аргументация в том, что уменьшается связанность и упрощается изменение классов. Но мне вот что непонятно. Если мне в метод, например, передается объект "Машина", у которой надо накачать колеса, как лучше поступить?
Не передавать в метод обслуживание экземпляр машины. Поскольку метод "обслуживание" потенциально машино-зависим разумно делегировать эту функциональность машине:
class Машина {
void обслуживание() {
self.ЛевоеПереднееКолесо.накачать();
}
}
Машина машина = Машина.new();
машина.обслуживание();
В будущем, метод обслуживание может сильно распухнуть, тогда потребуется оставив базовые операции вида "НакачатьКолеса" или "ПротеретьФары" применить паттерн Strategy.
MP>Как вы считаете, какой из вариантов более аккуратный? Какой облегчает поддержку и дальшейшие изменения программы? Надо ли стремиться к выполнению закона Деметера, или это "шашечки"?
Закон Деметра позволяет избежать длинных цепочек делегации вида "машина.правоеПереднееКолесо.накачать". Такие цепочки череваты ошибками, небезопасны (кто сказал что у машины есть правое колесо?) и черезмерно усложняют код (если проверять существование колеса)
MP>>Аргументация в том, что уменьшается связанность и упрощается изменение классов. Но мне вот что непонятно. Если мне в метод, например, передается объект "Машина", у которой надо накачать колеса, как лучше поступить?
M>Не передавать в метод обслуживание экземпляр машины. Поскольку метод "обслуживание" потенциально машино-зависим разумно делегировать эту функциональность машине
Хорошо, этот пример я понял. А что делать в случае необходимости фильтра
? Тоже закладывать его в класс Машина? Или в класс Колесо?
Машина не осведомлена о наличии других мащин, равно, как и колесо не осведомлено о наличии других колес. Фильтрами должна заниматься коллекция машин или та прослойка, которая достает машины из БД, файлов и т.п. В этом случае, да, машины должны предоставлять этой коллекции/прослойке информацию о своих свойствах.
class СписокМашин {
СписокМашин фильтрПоТипуКолеса( типКолеса ) {
//Здесь мы генерим запрос или нашу перебираем коллекцию машин
//Например:
СписокМашин совпадение = СписокМашин.new()
foreach машина in self.список {
if (машина.типКолес == типКолеса) {
совпадение.push(машина)
}
}
return совпадение
}
}
списокМашины.фильтрПоТипуКолеса( Шипованный );
? Тоже закладывать его в класс Машина? Или в класс Колесо? M>Машина не осведомлена о наличии других мащин, равно, как и колесо не осведомлено о наличии других колес. Фильтрами должна заниматься коллекция машин или та прослойка, которая достает машины из БД, файлов и т.п. В этом случае, да, машины должны предоставлять этой коллекции/прослойке информацию о своих свойствах.
M> if (машина.типКолес == типКолеса) {
И вот здесь, насколько я понимаю, нарушается закон Деметера? Это самый оптимальный вариант?
3-й вариант. За исключением того, что "левоеПереднееКолесо" — не очень удачный вариант. Т.к. машины могут обладать разным количеством колес и придется писать разные "обслуживания". Т.е. возможность получить колеса в виде коллекции у тебя явно напрашивается. Что касается дальнейших изменений, то тут вариантов может быть много. Что может меняться? Понадобиться качать не только колеса, а еще и шарики, надувные лодки и брюшной пресс? Придется обслуживать не только машины, а велосипеды, тракторы или самолеты? Появяться машины с самоподкачивающимися колесами или детские велосипеды с колесами из сплошной резины? Будешь учитывать все варианты — сделаешь супергибкое решение, но просядешь по срокам так, что твое решение уже нафиг никому не будет нужно. Не учтешь какого-нить варианта, потеряешь клиентов из-за тормозов с кастомизацией или расширением функциональности. Идеальной стратегии нет и это исключительно хорошо, т.к. именно благодаря этой неопределенности и существует возможность у маленькой компании из 5 человек сделать продукт, который станет лучшим на рынке.
P.S. Кстати, был у меня коллега, который в оголтелой погоне за соблюдением закона Дементора наплодил такое количество врапперов по проекту, что их стало чуть ли не больше, чем обычных классов.
Здравствуйте, Mikhail Polykovsky, Вы писали:
MP>И вот здесь, насколько я понимаю, нарушается закон Деметера? Это самый оптимальный вариант?
Или я что-то не так понимаю, или не нарушается:
Любой метод объекта должен обращаться только к методам, принадлежащим:
— тому же классу
— любым объектам, переданным в метод в качестве аргументов
— любым создаваемым им объектам
— любым непосредственно содержащимся объектам компонентов
Этот закон можно грубо переформулировать как "никаких ссылок через две точки".
Здравствуйте, Mikhail Polykovsky, Вы писали:
MP>И вот здесь, насколько я понимаю, нарушается закон Деметера? Это самый оптимальный вариант?
Или я что-то не так понимаю, или не нарушается:
Любой метод объекта должен обращаться только к методам, принадлежащим:
— тому же классу
— любым объектам, переданным в метод в качестве аргументов
— любым создаваемым им объектам
— любым непосредственно содержащимся объектам компонентов
Этот закон можно грубо переформулировать как "никаких ссылок через две точки".
MP>>И вот здесь, насколько я понимаю, нарушается закон Деметера? Это самый оптимальный вариант? M>Или я что-то не так понимаю, или не нарушается:
M>
M>Любой метод объекта должен обращаться только к методам, принадлежащим:
M>— тому же классу
M>— любым объектам, переданным в метод в качестве аргументов
M>— любым создаваемым им объектам
M>— любым непосредственно содержащимся объектам компонентов
M>Этот закон можно грубо переформулировать как "никаких ссылок через две точки".
Любую ссылку через две точки можно развернуть в две ссылки с одной точкой, используя временную переменную. В чем же тогда смысл?
Здравствуйте, Mikhail Polykovsky, Вы писали:
MP>В чем же тогда смысл?
В том и смысл, чтобы не делать так, что обращение можно свернуть в "через две точки". Если сворачивается, значит что-то сделано неправильно.
MP>>В чем же тогда смысл? M>В том и смысл, чтобы не делать так, что обращение можно свернуть в "через две точки". Если сворачивается, значит что-то сделано неправильно.
Извини, что-то я недопонял. Ведь любую строку
object.subObject.method()
можно преобразовать в
sub = object.subObject
sub.metod()
и по твоим словам закон Деметера соблюден. А по моему мнению все равно нарушается, просто в скрытой форме.
Здравствуйте, Mikhail Polykovsky, Вы писали:
MP>Ну хорошо, пусть Фильтр. Покажи мне пожалуйста, как бы ты сделал метод отбора Машин по МаркеКолеса в классе Фильтр. Метод получает на вход массив объектов "Машина".
Это изначально неверная постановка задачи. Потому как реализация по закону Деметры — разделяет функциональность в том числе по методам. И это будет не один метод. Что-то типа следующего:
public class CarsCollectionHelper
{
.....
public List<Car> FilterByMark(List<Car> source, Mark mark)
{
List<Wheel> wheels=GetWheels(source);
List<Wheel> wheelsMarked=FilterByMark(wheels, mark);
return GetOwnerCars(wheelsMarked);
}
List<Wheel> GetWheels(List<Car> source){...}
List<Wheel> FilterByMark(List<Wheel> source){...}
List<Car> GetOwnerCars(List<Wheel> source){...}
}
MP>>Ну хорошо, пусть Фильтр. Покажи мне пожалуйста, как бы ты сделал метод отбора Машин по МаркеКолеса в классе Фильтр. Метод получает на вход массив объектов "Машина". GZ>Это изначально неверная постановка задачи. Потому как реализация по закону Деметры — разделяет функциональность в том числе по методам. И это будет не один метод. Что-то типа следующего: GZ>
Здравствуйте, Mikhail Polykovsky, Вы писали:
MP>Но мне вот что непонятно. Если мне в метод, например, передается объект "Машина", у которой надо накачать колеса, как лучше поступить?
Есть несколько вариантов, при которых не нарушается упомянутый закон. Если объект класса Машина умеет обрабатывать сообщение накачатьКолесо(), тогда можно рассмотреть такой вариант: