Code Complete 2ed, композиция и делегирование
-
19-09-2019 - |
Вопрос
После пары недель чтения на этом форуме я подумал, что пришло время написать свой первый пост.
Сейчас я перечитываю Code Complete.Кажется, прошло 15 лет с последнего раза, а я обнаружил, что до сих пор не умею писать код ;-)
В любом случае, на странице 138 в Code Complete вы найдете этот ужасный пример кодирования.(Я удалил часть кода)
class Emplyee {
public:
FullName GetName() const;
Address GetAddress() const;
PhoneNumber GetWorkPhone() const;
...
bool IsZipCodeValid( Address address);
...
private:
...
}
Что Стив считает плохим, так это то, что функции слабо связаны между собой.Или он пишет: «Нет логической связи между сотрудниками и процедурами проверки почтовых индексов, номеров телефонов или классификаций должностей»
Хорошо, я полностью с ним согласен.Возможно, что-то вроде примера ниже лучше.
class ZipCode
{
public:
bool IsValid() const;
...
}
class Address {
public:
ZipCode GetZipCode() const;
...
}
class Employee {
public:
Address GetAddress() const;
...
}
При проверке правильности почтового индекса вам нужно будет сделать что-то вроде этого.
employee.GetAddress().GetZipCode().IsValid();
А это нехорошо с точки зрения Закон Деметры.
Поэтому, если вы хотите удалить две из трех точек, вам нужно использовать делегирование и пару функций-оболочек, подобных этой.
class ZipCode
{
public:
bool IsValid();
}
class Address {
public:
ZipCode GetZipCode() const;
bool IsZipCodeValid() {return GetZipCode()->IsValid());
}
class Employee {
public:
FullName GetName() const;
Address GetAddress() const;
bool IsZipCodeValid() {return GetAddress()->IsZipCodeValid());
PhoneNumber GetWorkPhone() const;
}
employee.IsZipCodeValid();
Но опять же, у вас есть процедуры, не имеющие никакой логической связи.
Лично я считаю, что все три примера в этом посте плохие.Это какой-то другой способ, о котором я не подумал?
Решение
Это зарплата сейчас противОплатить позже.
Вы можете заранее написать функции делегирования и оболочки (заплатить сейчас), а затем тратить меньше усилий на изменение внутренностей сотрудника.IsZipCodeValid() позже.Или вы можете туннелировать к IsZipCodeValid, написав
employee.GetAddress().GetZipCode().IsValid();везде, где вам это нужно в коде, но заплатите позже, если вы решите изменить дизайн своего класса таким образом, чтобы нарушить этот код.
Вы можете выбрать свой яд.;)
Другие советы
Вам не хватает логической связи:
class ZipCode
{
public:
bool IsValid();
}
class Address {
public:
ZipCode GetZipCode() const;
bool IsAddressValid();
bool IsValid() {return GetZipCode()->IsValid() && IsAddressValid());
}
class Employee {
public:
FullName GetName() const;
Address GetAddress() const;
bool IsEmployeeValid();
bool IsValid() {return GetAddress()->IseValid() && IsEmployeeValid());
PhoneNumber GetWorkPhone() const;
}
employee.IsValid();
Поскольку между классом «Сотрудник» и проверкой почтового индекса нет логической связи, вы можете поместить проверку почтового индекса в класс «Адрес», которому она более логично принадлежит.Затем вы можете попросить класс Address проверить для вас почтовый индекс.
class Address
{
public:
static IsZipValid(ZipCode zip) { return zip.isValid(); }
};
Тогда ты делаешь
Address::IsZipValid(employee.GetAddress().GetZipCode());
Я думаю, что это удовлетворительно с учетом ваших ограничений логических ассоциаций и Закона Деметры.