I'm still a relative novice with OOP, so I am still trying to understand some of the best practices for how to design "good" objects. I know this question is probably a rather subjective, which is why I am here rather than on stackoverflow.
I am designing an object that will have 20 separate string properties, none of which are required. These constraints are fixed and cannot be changed as it is an implementation of an outside data source that I have no control over.
The design problem I am running into is how to effectively construct an instance of the object and be able to compare equality by comparing each field without violating normal best practices.
A 20 argument constructor seems very wrong, especially since every field is a string, and there are cases where many of them will be null. I could use the optional arguments in C#4 to reduce the number of arguments that are actually called, but since everything is a string, I would have to use named parameters when calling the constructor, and that seems that it would be even uglier.
I have thought about trying to group the properties into smaller classes, but the most logical grouping still has an object with 10 properties, and at least half of the remaining properties have no logical grouping.
I could give each property a public setter, but that would make the object mutable, and thus complicate matters for overriding
GetHashCode()
. And I really don't need the object to be mutable. Once it is built, there should never be a reason to change it, but I don't want to rely on that because who knows what someone will try to do with it.Implementing
IEquatable<T>
is a possibility for the equality test, but my understanding is it is recommended to also overrideEquals(object obj)
andGetHashCode()
when implementingIEquatable
so allEquals
methods have the same behavior.
Is this an edge case where overriding Equals
without overriding GetHashCode
should be done? I don't intend on using this object as a key in a dictionary or as a member of a HashSet
, but I can't predict what someone else would try to do in the future.
Or should I just abandon the idea of doing an equality comparison on all of the properties with Equals and implement my own Compare()
method?
Or is there a better method for building the object that would allow it to still be immutable without having to pass all of the arguments through a constructor or a method?
EDIT: In response to Sign's comment, the purpose of the object is ultimately to be able to compare the data from the outside data source to our internal data to ensure it is accurate. Both data sets contain address information, but they are broken up differently.
The internal data, is generally stored as a typical address (Street, City, State, Postal Code, County, Country), but there are variations when you have a suite/apartment # or a floor in a building.
The external data is broken down even further into smaller pieces.
So this object is supposed to be a common link between the 2 data sources and allow the user to compare like fields for equality.
EDIT #2: Ultimately, I chose Doc Brown's answer because a modified version of his dictionary idea (with Enums as Keys instead of strings) was the simplest way to go without having a messy constructor and still provide the immutability I was hoping to have. In the long term, I am going to experiment with some of Oliver's suggestions as some of his edits did have a lot to offer.