I need to define an entity (CompositeContactList
) that contains and manages two collections of identifiers
(IncludedLists
and ExcludedLists
) that are ids of some other entities (ContactList
).
The business rules are the following:
1. The collections must contain only unique identifiers.
2. The same identifier cannot belong to both IncludedLists
and ExcludedLists
.
3. Identifiers can be added or removed (performing the checks above).
Note: Adding an identifier to the ExcludedLists
is not opposite to removing it from the IncludedLists
.
For instance, there is a list A that contains included lists B and C and excluded list D.
That means that A = (B + C) - D
.
An attempt to add list C to the ExcludedLists
leads to an error while C is in the IncludedLists
.
If the list C is removed from the IncludedLists
, the list A will be the following: A = B - D
.
I introduced two IEnumerable<Guid>
properties to get the collections and four methods to manage them.
There are few areas I'd like to hear some feedback about. Which variant is better?
Property naming
IncludedLists
andExcludedLists
IncludedListIds
andExcludedListIds
Method naming:
- IncludeList(id)
- RemoveFromIncludes(id)
- ExcludeList(id)
- RemoveFromExcludes(id)
- AddIncludedList(id)
- RemoveIncludedList(id)
- AddExcludedList(id)
- RemoveExcludedList(id)
- AddIncludedListId(id)
- RemoveIncludedListId(id)
- AddExcludedListId(id)
- RemoveExcludedListId(id)
Validation
Rule 1. When I add the same identifier twice, does it may sense to throw an exception saying that the
id
cannot be added? Is there any harm in 'doing nothing' here?Rule 2. Trying to add a list id to the included lists throws an error if it's already belongs to the excluded. Does it make sense to introduce some helping methods like
CanAddToIncludedLists
orTryAddToIncludedLists
?Does it make sense to throw an
ArgumentException
if an emptyGuid
is provided?
Usability
Does it make sense in declaring 'Add' methods as
void
and 'Remove' methods returningbool
? (similar to .NET collections).Does it make sense in adding other collection-specific methods like
ClearIncludedLists
orClearAll
?
Code
public class MyCompositeEntity
{
private readonly ISet<Guid> _excludedLists = new HashSet<Guid>();
private readonly ISet<Guid> _includedLists = new HashSet<Guid>();
public MyCompositeEntity(Guid id)
{
if (id == Guid.Empty)
throw new ArgumentException("The identifier cannot be empty.", nameof(id));
Id = id;
}
public Guid Id { get; }
public IEnumerable<Guid> IncludedLists => _includedLists;
public IEnumerable<Guid> ExcludedLists => _excludedLists;
public void IncludeList(Guid id)
{
if (id == Guid.Empty)
throw new ArgumentException("The identifier cannot be empty.", nameof(id));
if (ExcludedLists.Contains(id))
throw new ArgumentException(
"The identifier cannot be included because it is already excluded. " +
"Please remove it from the excluded lists first.",
nameof(id));
_includedLists.Add(id);
}
public void ExcludeList(Guid id)
{
if (id == Guid.Empty)
throw new ArgumentException("The identifier cannot be empty.", nameof(id));
if (IncludedLists.Contains(id))
throw new ArgumentException(
"The identifier cannot be excluded because it is already included. " +
"Please remove it from the included lists first.",
nameof(id));
_excludedLists.Add(id);
}
public bool RemoveFromIncludes(Guid id)
{
if (id == Guid.Empty)
throw new ArgumentException("The identifier cannot be empty.", nameof(id));
return _includedLists.Remove(id);
}
public bool RemoveFromExcludes(Guid id)
{
if (id == Guid.Empty)
throw new ArgumentException("The identifier cannot be empty.", nameof(id));
return _excludedLists.Remove(id);
}
}