Whenever you can't simply achieve a SingleStepConstructor
, or may need to invalidate objects before their ultimate destruction (or reclaimation by the GarbageCollector
) a good solution could be the isValid() method.
The isValid() method has no arguments and that returns a boolean indicating if the receiver has a) been fully instantiated and is a valid instance; b) has not had its DestroyMethod
How can I know if it is fully instantiated? You can tell the object that it is fully instantiated and ready for operation. For that you can use the BuilderPattern
. When the object is not fully instantiated it should not be ready for operation (all operations should return exceptions or the like). Another alternative would be to build a similar object that can't be operated but acts as a prototype (see PrototypePattern
), with the following twist: One object is just for creation, the other is just for operation.. This sounds like EssenceObject -- it's a ParameterBlock that's also a Factory.
Methods on the object can check some internal properties to determine if it is valid. In Java one could throw IllegalStateException
(not checked) from any method that depends on the object being correctly instantiated. The BuilderPattern
is a better way to ensure this though.
What should the client do when isValid() returns false?
The answer is simple. Make the operation fail. This is the same that would happen if the constructor failed (an exception is thrown). Also this is common in older C++ code, instead of an exception being thrown you would check if the result was zero and make the whole operation fail. In Java you can use IllegalStateException and then mask the exception appropriately.
- Wait for it to become true? In that case, you might as well just execute the constructor in the same thread.
- What if the constructor doesn't have default arguments?
- Then send it the arguments it requires.
- What if you don't know the arguments? Many clients of a Foo will have no idea how to create one...
- Fail immediately? In that case I'd rather throw an exception than call isValid before each access.
It isn't clear what you're advocating or why you're advocating it. Are you suggesting wrapping all calls to a class's methods with isValid() guards? Or using isValid inside a class's methods to throw an IllegalStateException
Ok, let us suppose you wrote this complex system that handles requests for rating phone calls. Some users have plans, others use calling cards and others use both. It depends on the day, on the time of the day and on several other variables how you should rate those calls. Now you are asked to build the account (read it from the database) because a customer wishes to talk. Can he talk? Well you need the account and possibly there is a complicated way to get it, you probably use the BuilderPattern or something like that. Is the construction fails you can always throw an Exception like ConstructionException? or if you think that ExceptionsAreEvil?, you may use the isValid() method. Both are correct, but I find that using the isValid() method is easier. Refactoring the code to throw an exception is easy. Refactoring the code to use isValid() is not easy at all, so I prefer to use isValid() and then if I need the exception I simply write if (! isValid() ) throw ConstructionException?( "Object not valid because of " + getNotValidCause() );
I don't understand the scenario. Why would isValid ever be false in this case? Why would the constructor fail? And why would you do something hard because something else is easy? -- EricHodges
If you think nothing could go wrong, then:
- You don't throw any exception.
- You don't catch any exception.
- You simply use a SingleStepConstructor and you avoid the isValid() method.
But if something can go wrong in the constructor, for example you initialize the data by reading information from a file or database, then you can use:
- SingleStepConstructor that throws an exception, or
- MultiStepConstruction? and finally call an isValid() method, or
- TwoParallelHierarchies?: One for constructing and another for using it. Once the first is constructed, it can't be used directly, because it must be transformed in the second which can't be modified, but it can be used. This may or may not use the isValid() method internally, or
What advantage does isValid() provide over throwing an exception in a SingleStepConstructor
? -- EricHodges
None. I would go with the exception in a SingleStepConstructor, but some programmers really don't understand exceptions very well. FOr example they are reluctant to mask the exceptions appropiately so that they make sense, or they simply do not handle exceptions appropiately in catch. See ExceptionsAreEvil?. In those cases I recommend the IsValid method.
What should the client do when isValid returns false?
Clients don't (directly) call isValid, at least in my code.
I usually make isValid() a private method.
It is only called by other methods of the class, something like
ASSERT( this.isValid() );
at the start of the method.
Does anyone use isValid() for anything *other* than this sort of ProgrammerTest
- I never use isValid() method for asserts, because asserts are typically not executed on production code and that's exactly the interesting case. In debug mode, asserts are usually useless, because you are debugging something that already has broken the ClassInvariant, so asserts pop windows several times for every loop. This is not a GoodThing.
- I use isValid() method for UnitTests. One object can contain several objects, therefore it makes sense that when the big object is valid, all its internal objects are valid. This seems so simple, but you will be surprised how many times you get this wrong. The isValid() method is a very powerful tool in this case. -- GuillermoSchwarz
Why not use a class factory method or a null object instead?
A class factory method is a simple way to combine a constructor and an initialize statement into a single user call. This is appropriate when the the initialization is too complex for the constructor or the language does not allow parameters in the constructor.
If the problem is time driven, try using a null object instead. One can avoid a lot of conditional guards in either the class or user code by simply having two versions of the object, an uninitialized null object and an initialized object.
See also: TestWhetherInConstructionPhase