Hi Dave
Hmm. Not sure I follow you. methods/events model behaviour, and have
'verb' names... like Initialize, Paint, LoadObject, etc.. but
properties, at least in my experience are frequently nouns.
This is true. It is common to use nouns for properties (plural if they
represent a collection) and verbs for methods. I was not arguing with that.
And I frequently create composite objects and expose the properties.
That is what I was arguing with. Take a moment and dig through the
Framework. Look specifically for an area that you use frequently where
there is unlikely to be a lot of reliance on Win32 to drive the naming (iow,
avoid System.Drawing and stuff like that). Open up 20 classes and look at
their methods. Count the total number of methods and compare with the
number of methods that return an object instead of a base type like String
or Int. Now, look at the names of the types compared to the names of the
properties. You will see that, in practice, it is less common than you may
think to return a type (data objects do this more than others, but my
observation still holds... it is less than you may think). You will also
notice that it is downright rare for the name of the property to be the same
as a public type.
Design works best when classes model behavior and the data goes along for
the ride. If you decide what a class is for, and you can answer the
question: "What does this class encapsulate?" then you are moving away from
rote advice into the realm of real design.
In a system of interaction objects, where each has a behavior, it is very
uncommon for an object to expose a property that is simply another object.
That would occur if the "parent" object is a mediator or proxy, but the rest
of the time, it is rare at best. That is because behavior isn't normally
heirarchical. It also means that if object A is composed of objects B and
C, and objects B and C are directly exposed, that object A effectively does
"B activities" and "C activities" as well as "A activities" which doesn't
really make logical sense.
It is perfectly appropriate to create and object and assign it into another
object. I have nothing against composition. However, you also want to
maintain encapsulation. Therefore, for a property with an object type, you
should mark a "yellow flag" next to any property that has both a getter and
a setter. Either the object consumes another object (and uses it
internally), or an object creates another object (factory methods). Having
both should be rare. If you cannot do it with your current design, then I
would suggest that this should be an indicator that your current design may
have other, more structural, flaws.
Fair enough, in the case of Monitors at least. It was just an example
but even working within it....
What about Motherboards or Cases. PCs only ever have one of each. Its
not a 'PrimaryMotherboard", there isn't going to be a list of them. I'm
not sure what 'context' you would add that wouldn't feel contrived.
And I'm still not sure why you would have individual properties for things
like this. Objects are not just data containers. I cannot think of a
problem, even surrounding this arbitrary data example, that I would model in
that way.
Let me imagine for a moment that I'm creating the system configurator page
on the Dell site. On that page, you are provided with system composition
options based on the model of computer you want. Pick an Optiplex and get
options: which motherboard, which one or two monitors, etc.
So, I have these components. I have constraints on them to decide what to
display and what to allow the customer to select from. So, let's look at
the problem. The user wants to see only the options available. So we need
to display sections on the web page that allow them to select their options.
We also need to collect enough information to display a bill-of-materials
for our new computer, so that the Dell manufacturing system can build and
ship it.
Two needs: model and view. However, are we talking about computers? No.
We are talking about choices. The user has choices. Each choice has
constraints about how to display it (we have to give the user help in
picking a choice, don't we?). The result of making a choice: we return a
"part". Aha! We've found a place where we can return an object!... but why
would we? It's an object that handles how to display information about a
part. It doesn't represent the part itself. So when querying the list of
selected parts from the object, when it comes time to display or even
persist the selections, why would we return a "displayable part" when we can
hand back a simple part number? That's a string.
Class Part
{
string _PartNumber;
string _Description;
public Part(InitPartNumber, InitDescription)
{
_PartNumber = InitPartNumber;
_Description = InitDescription;
}
public PartNumber { get { return _PartNumber;}}
public Description { get { return _Description; }}
}
Class ConfigChoice
{
private string Style;
private string Title;
private string HelpText;
private string Description;
private string<List> _PartList;
public void DisplaySelect()
{
// emit the HTML for the drop-down list where the user selects the
part number when they find the part description they want.
}
}
Class BaseSystemConfigurator
{
private List<ConfigChoice> _ConfigChoices;
public BaseSystemConfigurator(string ModelVersion)
{
// look up initial default configuration from a database
// and store the displayable form of the configuration in a
collection
_ConfigChoices = TranslationLayer.GetConfig(ModelVersion);
}
public void DrawChoices()
{
foreach (ConfigChoice ch in _ConfigChoices)
{
ch.DisplaySelect();
}
}
}
So, if you look at this air-code, you won't see any place where a class
returns a type that it didn't create, or exposed, as a property, a class.
The objects model the behavior of the system, not is data. Data comes along
for the ride. You will also notice that there is NO notion of a CPU or a
Monitor. Because the behavior of the system doesn't CARE that it is a CPU
or a Monitor.
This is with me trying my level best to come up with an example where I
would actually need to know that an item is a Monitor, but I can't figure
one.
I think in general, I don't bump into it when using "generic" classes,
like Point, or Stack, HashTable, or Button...or my own classes with that
sort of broad utility... I mean my form class will have a
Button GoButton,
Button NextButton,
Button ExitButton, etc...
It happens when I have to create a specific class for a very specific
peice of an application.
e.g. If I want a custom "ExitButton", I inherit a new class from
"Button" called "ExitButton"... and then I end up with
I assume it is because the ExitButton is intended to encapsulate the display
properties surrounding a button that the user should be able to look at and
say "Oh... that button will exit!" I also assume that you intend to put
this exit button on more than one form to make it reusable. There is both
something unique about that button that is different from other buttons, and
something reusable about this button that you want to encapsulate. It is
easy to justify creating the object.
In this case, however, there are not new methods or properties... just a new
constructor to justify the existence of this object. So, why is your
variable declared as type ExitButton? The class that uses the button
neither knows nor cares that it is an ExitButton. As far as that class is
concerned, it is a Button like any other. The only one that should know
that it is an exit button is the factory that creates it. Also, the name of
the button variable would be specific to its use.
Therefore, in the visible class, you could just as easily declare:
public MainForm : System.Windows.Forms.Form
{
// <note>You could use a factory instead, but I didn't for
clarity</note>
Button MainExitButton = new ExitButton();
// ...
}
Why did I call it MainExitButton? Because it is the exit button on the Main
form. This differentiates between the exit button that exists on ChildForm
and WarningDialog.
Even if the button isn't reusable... if it is specific to this form, then it
is still a MainExitButton, because it is a object of type ExitButton.
--
--- Nick Malik [Microsoft]
MCSD, CFPS, Certified Scrummaster
http://blogs.msdn.com/nickmalik
Disclaimer: Opinions expressed in this forum are my own, and not
representative of my employer.
I do not answer questions on behalf of my employer. I'm just a
programmer helping programmers.
--