views:

405

answers:

5

I have a class : Schedule.

public class Schedule {

private int locationNum;
private int cost;
private String costReason; 
private Date weekOfChange;
private Date dayOfChange;
private String changeReason; 

// and all those getters and setters

public Schedule(int locationNum, int cost, String costReason, Date weekOfChange, Date dayOfChange, String changeReason) throws ApplicationException {
//change is all or nothing - all attributes are present or none
if((weekOfChange!=null && dayOfChange!=null && changeReason!=null) || (weekOfChange==null  && dayOfChange == null && changeReason == null))  {
this.weekOfChange = weekOfChange;
this.dayOfChange = dayOfChange;
this.changeReason = changeReason;
}
else { throw new ApplicationException();}
//similary another if block to ensure that if cost is specified 
//then there exists the corresponding reason code for it.
}
}

So far, I am liking my Schedule class. However, I am not done with the checks, I would have to do some other checks:

  • is the locationNum a valid store number in the database.
  • is changeReason text one of those 6 different changeReason codes in the database.
  • etc etc....

Typically, I would not write these in Schedule class, coz obviously, I cannot invoke DAOs from this class. So, I would have a business layer and some kind of validator class, accepting an object of type Schedule and performing a bunch of database validations in sequence and collecting errors for display / whatever.

Now, here are my questions:

  1. If you were to treat Schedule as a POJO and argue that an object is not responsible for validating itself - I would have to move all of the code in the constructor to the business layer's validator class. But, if I were to do this, isn't Schedule anemic? Is this what they call violation of Single Responsibility principle?
  2. Lets presume I moved the code I have in my constructor to a business layer class so that all kinds of validations are now in my busines layer. Lets assume that someone changes dayOfChange to NULL in my datastore and now I am loading objects from my database. Now, with this kind of an object, my application can break, wouldn't it? coz I would have written code assuming that validation rules are met. I guess my question is getting confusing, but the point I am trying to make is that in this context I would rather have these checks done in constructor to ensure integrity of the data hidden by Schedule class.
  3. How is it done usually? What is the best practice?

Appreciate your involvement in this discussion.

+1  A: 

Why is locationNum an int when in fact you refer to an instance of another class, say Location? If you'd use proper object references you could validate them in Schedule and configure the ORM (if any) to enforce referential integrity.

@lutz I am compelled to use a Torque like homegrown framework of my client. I am not allowed to use ORMs. Now, in this scenario if i were to develop a webapplication, are you suggesting that I would load up the entire graph of objects from the database? Schedule has a Location reference, Location has a ref to something else, etc etc etc. I guess, that would just kill the application on performance front.
Jay
No, you would not load all the data. Instead, the Location reference would contain a lazy-loading stub object. Sounds complex? Yeah, that's why you leave it to the ORM. If you're not allowed to use an ORM, then you'll have to live without such nice features.
Michael Borgwardt
I understand the advantages of lazy-loading that comes with ORMs like hibernate. Wouldn't you agree that ORMs are dangerous tools in the hands of programmers' who don't know them thoroughly? An ORM that is incorrectly used could result in the slowest performance for your application. Hey, I am not one of those guys who thinks ORMs are evil - I have nothing against them.Your DB guy is probably not going to like ORMs - that is what huge companies with small IT teams will tell you and that is what I got with my current client :)
Jay
An ORM is dangerous when it makes you believe you don't have to think about what happens at the SQL level at all. But a programmer who thinks that and can't be taught otherwise would probably do horribly inefficient things with SQL either.
Michael Borgwardt
+4  A: 

If you were to treat Schedule as a POJO and argue that an object is not responsible for validating itself - I would have to move all of the code in the constructor to the business layer's validator class. But, if I were to do this, isn't Schedule anemic? Is this what they call violation of Single Responsibility principle?

POJO only means that you don't have to derive from a particular class or use a byte code enhancer to get desired functionality like OR mapping. It does NOT mean that the objects should be anemic with no functionality.

Now, with this kind of an object, my application can break, wouldn't it? coz I would have written code assuming that validation rules are met. I guess my question is getting confusing, but the point I am trying to make is that in this context I would rather have these checks done in constructor to ensure integrity of the data hidden by Schedule class.

Who says you cannot call rules defined in a separate class from the constructor of your Schedule class? If it needs access to non-public fields, you could make them package-private and have the rules class in the same package - or pass them as parameters to the rule.

How is it done usually? What is the best practice?

Whether or not you move validation rules to separate classes should depend on your application's needs, not on misunderstood buzzwords.

Good reasons to have separate rule classes would be:

  • The rules are so many and so complex that putting them all into the Schedule class would make it too big.
  • You do not always want to apply all the rules.
  • The rules can be reused to operate on different classes that are not necessarily in the same inheritance hierarchy (i.e. they work through an interface).
Michael Borgwardt
@Michael "Who says you cannot call rules defined in a separate class from the constructor of your Schedule class? If it needs access to non-public fields, you could make them package-private and have the rules class in the same package - or pass them as parameters to the rule." - There are two problems here: 1) Schedule will always depend on the seperate class. 2) If you write it in the same class, your Schedule class will have lot of DAO stuff.
Jay
Neither of these are a fundamental problem. 1) can be adressed by a dependency injection framework, so that Schedule only knows it has a bunch of Rule objects it should call, but does not know the concrete classes implementing them.
Michael Borgwardt
Thanks to Rod Jhonson,people are talking dependency injection since the advent of Spring. Let's rewind a bit and assume that I dont have a dependency injection framework.
Jay
You don't even need a framework to do dependency injection. And not all DI frameworks are as heavyweight and all-encompassing as Spring. e.g. Guice: code.google.com/p/google-guice/
Michael Borgwardt
@Michael agreed - I wouldn't need a framework to DI. Now, let's stick to the basic question here. Where do rules go?
Jay
As I said: it depends. I'd put them into the domain class unless there is an app-specific reason not to.
Michael Borgwardt
+1 for clarifying the meaning of POJO.
MEMark
A: 

to answer your questions :

1) I don't think your class will be anemic, it will rather be a DTO for the moment, I see no problem with this.

2) If your validation is in the business layer, it doesn't mean you'll be getting an invalid object, because the validation will still be there, normally your validation process will somehow complain about an InvalidDataException or something and won't allow you to use the "corrupted" object. If the null date is not an acceptable value ( null could not normally get into the database ), then the business/dao layer should catch this error and prevent the object to get used by your app.

Billy
+2  A: 

It looks like there is two distinct behaviours for the Schedule constructor. There, therefore, should be two constructors. Possibly even two implementation classes.

locationNum and changeReason. These are currently weakly typed. They should probably have their own class. Following your question, the valid values depend upon the context. At the moment Schedule doesn't have any context. You could keep Schedule independent of context, in which case there are no database constraints on locationNum and changeReason. Going the other way, Schedule, locationNum and changeReason could be dependent on context, and the constructor should merely check that they are all in the same context. A package-private constructor (acting as a poor man's friend) may omit the checks.

A POJO implies a (well written) object and therefore behaviour. The question seems to be using it to mean "struct". Avoid structs.

Business layer seems to be misleading. It appears to be a business entity, it should have business behaviour. However, it may not contain business process (service).

Your application will always assume certain things about the database, including the (public) schema. If you remove constraints, then you may well break your application. This is the same as if you similarly changed code that is depended upon.

The common practice is to have anemic objects with procedural code. For most cases, this is not best practice.

Tom Hawtin - tackline
@Tom "The common practice is to have anemic objects with procedural code. For most cases, this is not best practice." - I see this happen at work often. With clients not caring about what they get in terms of quality and people often resorting to anemic objects with procedural code - It upsets me a lot. As someone who wants to write a few good lines of code, I find that offshoring produces anything but good coders. It's a sad state of things.
Jay
+5  A: 

If you really have "all those getters and setters" then you need to validate your class better than in the constructor. If the invariants of your class are such that all weekOfChange, dayOfChange and changeReason must be null or not all must be not null your setters are quickly going to put your class in an invalid state. Do you mean to have setters or do you mean your class to be immutable?

Before worrying about were validation should go maybe do an analysis of your class. Look at all its valid states and invariants for given states. Then you will understand whether your class should be mutable or immutable. Where you have mutually dependant covariants (like weekOfChange, dayOfChannge and changeReason it makes sense to package them into there own class and use composition in the Schedule class. This will put the "rules" about these things fields in a single place and simplify the Schedule.

The same with other collaborating fields (like cost and cost reason). Then Schedule is composed of to self validating classes. If both these are immutable and Schedule is immutable also you have your self a much easier domain.

So, to answer your question: it is better that a class defines its states and invariants and exposes only the minimum to its collaborators where practical. The responsibility for the internal state of Schedule should rest with Schedule and with just a little more design it can do with relative ease.

So you have

    class Schedule {
         private Change change;
         private Cost cost;
         private Location location;

        public Schedule(Location location, Cost cost, Change change) {
           this.change = change;
           this.cost = cost;
           this.location = location;
        }
}

And colloborators like

public class Change {
    private Date weekOfChange; //shoudln't these two fields be one, a date is a date after all??
    private Date dayOfChange; //shoudln't these two fields be one??
    private String changeReason; 

    public Change(Date weekOfChange Date dayOfChange, String changeReason) {
      this.weekOfChange = weekOfChange;
      ...etc.
    } 
}

Though I would strongly advise you protect your classes invariants with defensive copying of any mutable values that are passed in by client code.

russelldb
+1 for mentioning defensive copying.
Paul Morie