When developing classes, the question often arises how much
of the internal workings should be exposed. For instance, when developing
classes, how much of the class should be exposed? One of the cases we'll talk
about is repository-based classes, where the repository works like a list of
domain objects.
One of the common ways to store domain objects is in a
collection of some sort, where the objects can be added, removed, or inserted.
A list also has the capabilities to perform find/contain operations, searching
for a specific item. However, should the list be exposed directly? If the
list is exposed as a public read-only property, any object can add items to the
list. Most collection classes also don't expose events for when an object is
added or removed, meaning that the source object doesn't know when these events
may occur, which is one of the reasons I created a collection that raises
events upon adding/removing items to/from the list in my Nucleo framework.
However, is that a bad thing? Maybe not. If your class
doesn't need to keep track of state, new objects being added or existing
objects being removed isn't that big of a deal. However, there are times when
it is a very big deal, especially when these objects represent data in a data
store, and there should be some correlation between the two.
In addition, what about creating the object? Should the
object be directly instantiable? Should it be created by a factory or another
object? In situations where you are tracking state of an object, I would
highly recommend making the constructor internal, so that your code can control
when the object is instantiated. In situations where the class may be a helper
object or utility, it doesn't really make a difference to me. However, when
you read about this subject on the web, there are a variety of topics. My
viewpoint is I make the constructor internal, especially if I feel I need an
extra level of control over it, unless I don't care about what the object does.
For instance, imagine a Comment class that represents a
comment generated from a web form. The factory that would work with this
object would look like the following:
Listing 11
public static class CommentFactory
{
public static Comment CreateComment(string name, string email, string subject,
string message, string source)
{
CommentsDataGateway gateway = new CommentsDataGateway();
Guid id = gateway.AddComment(name, email, subject, message, source);
return new Comment(id, name, email, subject, message, source);
}
private static Comment CreateCommentObject(DataRow row)
{
return new Comment((Guid)row["CommentID"], (string)row["Name"], (string)
row["Email"], (string)row["Subject"], (string)row["Message"], !row.IsNull
("Source") ? (string)row["Source"]: string.Empty);
}
public static void DeleteCommentsPast(DateTime pastDate)
{
CommentsDataGateway gateway = new CommentsDataGateway();
gateway.DeleteOldComments(pastDate);
}
public static CommentCollection GetComments(TimeSpan period)
{
CommentsDataGateway gateway = new CommentsDataGateway();
DataTable commentsTable = gateway.GetComments(period);
CommentCollection commentsList = new CommentCollection();
foreach (DataRow commentRow in commentsTable.Rows)
commentsList.Add(CreateCommentObject(commentRow));
return commentsList;
}
}
In the above factory, the factory instantiates the Comment
object, as the comment's constructor is internal to the project. It's solely
responsible for creating the object, and meets a principle discussed in the
next section.