Какой дизайн класса лучше и почему?
public class User
{
public String UserName;
public String Password;
public String FirstName;
public String LastName;
}
public class Employee : User
{
public String EmployeeId;
public String EmployeeCode;
public String DepartmentId;
}
public class Member : User
{
public String MemberId;
public String JoinDate;
public String ExpiryDate;
}
ИЛИ
public class User
{
public String UserId;
public String UserName;
public String Password;
public String FirstName;
public String LastName;
}
public class Employee
{
public User UserInfo;
public String EmployeeId;
public String EmployeeCode;
public String DepartmentId;
}
public class Member
{
public User UserInfo;
public String MemberId;
public String JoinDate;
public String ExpiryDate;
}
Ответ 1
На вопрос просто ответят, узнав, что наследование моделирует отношение "IS-A", а членство моделирует отношение "HAS-A".
- Сотрудник IS Пользователь
- Сотрудник HAS A userinfo
Какой из них правильный? Это ваш ответ.
Ответ 2
Мне не нравится ни один. Что происходит, когда кто-то является членом и сотрудником?
Ответ 3
Задайте себе следующее:
- Вы хотите смоделировать Employee IS User? Если это так, выберите наследование.
- Вы хотите, чтобы модель Employee имела информацию о пользователе? Если да, используйте композицию.
- Существуют ли виртуальные функции между Пользователем (info) и Работником? Если это так, используйте наследование.
- Может ли сотрудник иметь несколько экземпляров User (info)? Если да, используйте композицию.
- Имеет ли смысл назначать объект Employee объекту User (info)? Если это так, используйте наследование.
В целом старайтесь моделировать реальность, которую ваша программа имитирует, с ограничениями сложности кода и требуемой эффективности.
Ответ 4
Я не думаю, что композиция всегда лучше, чем наследование (как правило, обычно). Если Employee и Member действительно являются Пользователями, и они являются взаимоисключающими, то первый дизайн лучше. Рассмотрим сценарий, в котором вам нужно получить доступ к UserName сотрудника. Используя вторую конструкцию, вы должны:
myEmployee.UserInfo.UserName
что плохо (закон Деметры), поэтому вы бы реорганизовали:
myEmployee.UserName
который требует небольшого метода для Employee делегировать объект User. Все это избегается первой конструкцией.
Ответ 5
Хороший вопрос, хотя во избежание отвлекаться по поводу правильного и неправильного я бы подумал о том, чтобы просить плюсы и минусы каждого подхода - я думаю, что вы имели в виду, что лучше или хуже, и почему. Во всяком случае....
Первый подход aka Наследование
Плюсы:
- Разрешает полиморфное поведение.
- Изначально простой и удобный.
Минусы:
- Может стать сложным или неуклюжим с течением времени, если добавлено больше поведения и отношений.
Второй подход aka Состав
Плюсы:
- Хорошо подходит для не-oop-сценариев, таких как реляционные таблицы, структурированное программирование и т.д.
- Является простым (если не обязательно удобным) постепенным расширением отношений и поведения.
Минусы:
- Нет полиморфизма, поэтому менее удобно использовать связанную информацию и поведение.
Списки, подобные этим + упомянутые вопросы Jon Limjap, помогут вам принять решения и начать работу - тогда вы можете найти ответы на правильные ответы;-)
Ответ 6
Вы также можете думать о Employee как о роли пользователя (человека). Роль пользователя может измениться во времени (пользователь может стать безработным), или Пользователь может иметь несколько ролей одновременно.
Наследование намного лучше, если существует реальное "отношение, например Apple - Fruit. Но будьте очень осторожны: Circle - Ellipse не является реальным" является "отношением, потому что cirlce имеет меньше" свободы ", чем эллипс (круг является состоянием эллипса) - см.: Проблема эллипса круга.
Ответ 7
Реальные вопросы:
- Каковы бизнес-правила и пользовательские истории за пользователем?
- Каковы бизнес-правила и пользовательские истории за сотрудником?
- Каковы бизнес-правила и пользовательские истории за членом?
Это могут быть три совершенно несвязанных объекта или нет, и это определит, будет ли работать ваш первый или второй дизайн, или если другой совершенно другой дизайн в порядке.
Ответ 8
Ни один из них не является хорошим. Слишком много изменчивого состояния. Вы не сможете создать экземпляр класса, который находится в недопустимом или частично инициализированном состоянии.
Тем не менее, второй лучше, потому что он способствует составу над наследованием.
Ответ 9
Утверждение вашего требования/спецификации может помочь прийти к "лучшему дизайну".
На данный момент ваш вопрос слишком "интерпретируется для читателя".
Ответ 10
Вот сценарий, о котором вы должны подумать:
Композиция (второй пример) предпочтительнее, если один и тот же Пользователь может быть как Сотрудником, так и Участником. Зачем? Потому что для двух экземпляров (Employee и Member), которые представляют одного и того же пользователя, если данные пользователя изменяются, вам не нужно обновлять их в двух местах. Только экземпляр пользователя содержит всю информацию о пользователе, и только он должен быть обновлен. Поскольку классы Employee и Member содержат один и тот же экземпляр пользователя, они автоматически содержат обновленную информацию.
Ответ 11
Еще три варианта:
-
Если класс User
содержит дополнительную информацию как для сотрудников, так и для членов, причем неиспользованные поля пусты (ID
конкретного User
будет указывать, был ли пользователь сотрудником, членом, или что-то еще).
-
Есть класс User
, который содержит ссылку на ISupplementalInfo
, где ISupplementalInfo
наследуется ISupplementalEmployeeInfo
, ISupplementalMemberInfo
и т.д. Код, применимый ко всем пользователям, может работать с User
объекты класса и код, который имел ссылку User
, могли получить доступ к дополнительной информации пользователя, но этот подход не позволит изменить User
, если в будущем потребуются различные комбинации дополнительной информации.
-
Как указано выше, но класс User
содержит некоторую коллекцию ISupplementalInfo
. Преимущество такого подхода состояло в том, чтобы облегчить добавление свойств пользователю во время выполнения (например, поскольку a Member
получил наем). При использовании предыдущего подхода нужно было бы определить разные классы для разных комбинаций свойств; превращение "члена" в "участника + клиента" потребует от другого кода превращения "служащего" в "сотрудника + клиента". Недостатком последнего подхода является то, что он будет затруднять защиту от избыточных или несогласованных атрибутов (использование чего-то типа Dictionary<Type, ISupplementalInfo>
для хранения дополнительной информации могло бы работать, но казалось бы немного "громоздким" ).
Я бы склонялся к второму подходу, поскольку он позволял будущему расширению лучше, чем прямое наследование. Работа с набором объектов, а не с одним объектом может быть немного обременительной, но этот подход может быть лучше, чем другие, для обработки изменяющихся требований.