mskeel Posted October 22, 2004 Posted October 22, 2004 I have a class the represents a line segment. The segment has several attributes including a name, an id number, a start time, and an end time. There is a requirment that in order for a line to be valid, startTime <= endTime. What is the best way to enforce this business rule in the class? 1.) Two properties, StartTime and EndTime, each of which preform a check on set to make sure the business rule (starttime <= endtime) is not viloated. If the rule is violated an exception is raised. Problem: Changing times can be unnatural. You have to modify the End Time before you modify the Begin Time. Problem: Initilization from a file is awkward. The file that stores these lines is in the order begin time, end time. To initilize you would have to store the begin time in a variable, read in the end time, set it, then set the begin time. 2.) Use a transaction function called SetTime(byval startTime, byval endTime) that will set the start and end times and then verify the business rule, rolling back and throwing an exception if there is a violation. Problem: Usage could significantly slow the program. I'm looking at 1000's of time changes. Usage for this would be worst case 3 function calls, best case 2: a) SetTime(line.StartTime, newEndTime) //the function, then the property b) SetTime(line.StartTime + X, line.EndTime + Y) //the function then both properties. From a pragmatic design perspective, what is the best way to abstract this design? Is there another way I haven't thought of or perhaps a combination of both ways? Thanks for you help... Quote
*Experts* Nerseus Posted October 22, 2004 *Experts* Posted October 22, 2004 The best way to design your object, is to first figure out how you want to use it. It sounds like you're trying to force limitations on the user based on how you want to implement it. An object is only as good as the interface it exposes to the code calling/using it. While I wouldn't worry too much about the performance, is there a need to check that the start is less than end (and vice versa) every time you set it? If you plan on making x number of changes before using the object, maybe the object supports an IsValid property or method? -ner Quote "I want to stand as close to the edge as I can without going over. Out on the edge you see all the kinds of things you can't see from the center." - Kurt Vonnegut
mskeel Posted October 24, 2004 Author Posted October 24, 2004 That is a third option that my fellow developers and I recognized after a day of thinkging and implemented, but this is still a bit of an intersting question. How do you enforce business rules of an object in the implimentation? Is it apropriate to do so? I suppose it would depend on how the business rules are related to the private members of the class, but enforcing business rules is definately something that makes an object an object right? You are trying to encapsulate an implimentation of the real world, yes? I think in this case, two independant attributes, each contributing to a rule, allowing the user to set at will and then verify (knowing that if they make it invalid, functionality will not work as expected) is definately the correct answer. Is there ever a time when this might not be the case? Quote
*Experts* Nerseus Posted October 26, 2004 *Experts* Posted October 26, 2004 Business rules aside, how do you want to use the object? What would the code on the outside (using the object) look, ideally? First, assume good data, then figure out where and how you might want to check for bad data. In your case, it sounds like you create the object, change two properties repeatedly, then use it to draw a line (or lines)? If so, you only need to check the business rule when you go to draw (maybe?). If you were going to save the data (after changing the two properties), then I'd say check the rules before you save. Sometimes it makes sense to validate a business rule sooner. But in your scenario, you say you can't easily validate the business rule on the change of a single property. Let the use of the object drive it's design, business rules, and implementation rather than using what you think a robust object should look like. -ner Quote "I want to stand as close to the edge as I can without going over. Out on the edge you see all the kinds of things you can't see from the center." - Kurt Vonnegut
*Experts* Nerseus Posted October 26, 2004 *Experts* Posted October 26, 2004 Forgot to address your "real" question - where do business rules belong. That can't really be answered with one answer, it's one of those "it depends" kinda things. For simple objects with unchanging rules, you can embed business rules right in the object. Or, if your archictecture is relatively small and doesn't require more robust rules, then embedding them in the object might work great! In other scenarios, you may want to separate out business rules and apply them at a specified time. That answer is kinda fuzzy, so let me tell you the way I've done it. A "big" transaction has many business rules; some are small (this field is required and has maxlength=30), some are very complex even requiring a call to the database to verify information. All business rules for a given transaction live in a single file. We currently use a combination of XPath and asserts to detect errors (our transactional data is all stored in DataSets). We test for errors at a couple of different times: sometimes when a user leaves a field (or tries to), sometimes when they leave a row (such as editing in a grid), and sometimes not until the "save" button is pressed. Our more recent coding efforts have been using C# code in a separate file. The C# code is downloaded to the client (all in-memory) and compiled on the fly. This code validates the business rules instead of the XPath and asserts. Both of these last two solutions requires a good framework and lots of standards to make sure everyone does things the same way. If your classes are smaller, I would embed the rules right in the object. With a good set of unit tests you should have very robust objects. Check out Test-Driven Development in Microsoft .NET if you want to go this route - it might help. -ner Quote "I want to stand as close to the edge as I can without going over. Out on the edge you see all the kinds of things you can't see from the center." - Kurt Vonnegut
mskeel Posted October 26, 2004 Author Posted October 26, 2004 Totally awesome. Thank you so much. Quote
Joe Mamma Posted October 27, 2004 Posted October 27, 2004 Is there another way I haven't thought of [?] Yes. . . This 'Smells' of non OOP - SetTime(line.StartTime, newEndTime) see GOF I think the Idea of StartTime and EndTime is the root of the problem. . . What you want is EventDate and EventLength. this makes no inherent relationship between the two properties as they can be set independently with no 'rule' needing to be applied. public class UninitializedEventException: Exception{}; public class LineTime { DateTime _eventStart; TimeSpan _eventLength; public LineTime(DateTime startTime, TimeSpan length) { _eventStart = new DateTime(startTime.Ticks); _eventLength = new TimeSpan(length.Ticks); } public LineTime(DateTime startTime) { _eventStart = new DateTime(startTime.Ticks); } public LineTime(TimeSpan length) { _eventLength = new TimeSpan(length.Ticks); } public LineTime() { } public DateTime EventStart { get { return _eventStart; } set { _eventStart = value; } } public DateTime EventStart { get { return _eventStart; } set { _eventStart = value; } } public TimeSpan EventLength { get { return _eventLength; } set { _eventLength = value; } } public DateTime EventEnd { get { try { return _eventStart.Add(_eventLength); } catch (Exception ex) { throw new UninitializedEventException("Line Event Not Initialized"); } } } } Quote Joe Mamma Amendment 4: The right of the people to be secure in their persons, houses, papers, and effects, against unreasonable searches and seizures, shall not be violated, and no warrants shall issue, but upon probable cause, supported by oath or affirmation, and particularly describing the place to be searched, and the persons or things to be seized. Amendment 9: The enumeration in the Constitution, of certain rights, shall not be construed to deny or disparage others retained by the people.
mskeel Posted October 27, 2004 Author Posted October 27, 2004 I think the Idea of StartTime and EndTime is the root of the problem. . . Very interesting. After hearing what you have to say and looking through the code I think Iwould agree. My next question then is, would a class that only stores the startTime and the length still accruately refect the real world object (a line) this class will represent? I think the answer is yes, becuase the information is still available, it just has to be derived, which is the whole point of properties... I like it. It's like a self enforcing rule because you've actually modelled the real world correctly. Putting in a negative length doesn't make any sense at all and you could even declare the argument unsigned and then it could never be negative. The gang of four book is definately on my short list of things I need to read in the near future. Thanks for your insight, I appreciate it greatly. Quote
Joe Mamma Posted October 27, 2004 Posted October 27, 2004 Perhaps you want to aggregate the class. Define the Line (parent) Define the LineEvent Class as suggested Then inherit from Line add the LineEvent class as a member and call it LineWithEvent. Quote Joe Mamma Amendment 4: The right of the people to be secure in their persons, houses, papers, and effects, against unreasonable searches and seizures, shall not be violated, and no warrants shall issue, but upon probable cause, supported by oath or affirmation, and particularly describing the place to be searched, and the persons or things to be seized. Amendment 9: The enumeration in the Constitution, of certain rights, shall not be construed to deny or disparage others retained by the people.
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.