Таким образом, мне нужен сеттер или метод, который доступен только одному (или нескольким другим) классам. Насколько я знаю, это распространено на некоторых других языках в качестве классов друзей. Я попытался упростить приведенный ниже пример. Это не настоящий случай, и это немного сложнее. Но я должен убедиться, что два свойства в двух разных классах синхронизируются. Т.е. я использую Entity Framework, и эти свойства синхронизируются с базой данных.
public class UserGroup
{
List<GroupMember> _Members;
int _AcceptedMemberCount; // this is being synced to the database on save for performance reasons.
public void Join(User user)
{
var member = GetOrCreateMember( user );
member.State = MemberState.Accepted;
UpdateAcceptedMemberCount();
}
public void Ban(User user)
{
var member = GetOrCreateMember( user );
member.JoinState = MemberState.Banned;
UpdateAcceptedMemberCount();
}
private GroupMember GetOrCreateMember(User user)
{
// Creates a GroupMember from User,
// and add it to _Members if it doesn't exist.
}
private void UpdateAcceptedMemberCount()
{
_AcceptedMemberCount = _Members.Where(m => m.State == MemberState.Accepted).Count();
}
}
public class GroupMember
{
public MemberState State { get; internal set; }
}
Альтернативы, которые я знаю
Использование внутренних
Недостатком является то, что свойство все еще доступно для всех других классов в той же сборке. Т.е. у меня есть сборка с моими бизнес-моделями и сущностями. Создание отдельной сборки будет усложнять код, особенно когда вы используете Entity Framework, и нужно начать разделение объектов на несколько сборок.
Добавление SetState (UserGroup userGroup) в GroupMember
... что делает дополнительную ручную проверку, если UserGroup это заработало. Как и проверка того, был ли Пользователь добавлен как GroupMember в _Members. Чувствует себя действительно уродливым, и больше кода для поддержки, который может выйти из синхронизации и сломаться в будущем.
Использование вложенных классов (см. qaru.site/questions/34397/...)
Запишите его в сводке метода и проигнорируйте его
И просто надейтесь, что все прочтут это и никогда не начнут ругаться с состоянием GroupMember вручную. Недостатки этого, наверное, очевидны :)
Вопрос
Я не могу себе представить, что это действительно странный пример, и многие приложения должны иметь одинаковые проблемы. Какая здесь самая лучшая практика? Как это обычно решается?
Заранее спасибо,
Если для других классов может быть изменено значение GroupMember.State
, вы можете использовать события, чтобы монитор UserGroup
всякий раз, когда состояние менялось: когда State
изменяется, оно вызывает событие, и класс UserGroup
слушает его.
Если только пользователю UserGroup
должно быть разрешено устанавливать состояние или если по какой-либо группе должно пройти какое-либо изменение состояния, то одна вещь, которую вы могли бы сделать, - это перемещать фактическое хранилище свойства State из GroupMember
и в UserGroup
:
public class UserGroup
{
List<MemberState> _MemberStates;
...
}
Для этого потребуется, чтобы каждый GroupMember
UserGroup
принадлежащую ему UserGroup
, поэтому это будет работать, только если каждый член группы является частью только одной группы пользователей. Если это так, то GroupMember
будет выглядеть примерно так:
public class GroupMember
{
private UserGroup _Parent;
public GroupMember(UserGroup parent)
{
_Parent = parent;
}
public MemberState State { get {return _Parent.GetState(this);} }
// Where GetState is a function that returns the state from _MemberState
...
}
Конечно, это создает сильную связь между ними (вместе они работают для обеспечения двух сторон одного и того же публичного интерфейса) и работает только в том случае, если каждый GroupMember
всегда имеет ровно одну UserGroup
.
Теперь, если каждый член группы может быть частью нескольких групп, то выше не будет работать, но угадывание из вашего примера здесь не так (потому что friend
не решит это), поэтому я не буду вдаваться в возможные решения.
Подумав об этом, это действительно сводится к вашим точным требованиям и вариантам дизайна. Невозможно дать ответ на один размер, но, может быть, мне удалось вдохновить вас найти хороший выбор для вашего проекта.