|
Lately I have begun to question how far an application should go to allow users to address possible errors? This may be answering my own question but it dawned on me while writing that it comes down to, at what point does the error checking and correction begin to affect application build costs and performance.
What do others think?
|
|
|
|
|
Troy.compdr wrote: how far an application should go to allow users to address possible errors?
Depends on what the application does.
Troy.compdr wrote: at what point does the error checking and correction begin to affect
application build costs and performance.
I have never seen a user (human) application where validation impacted performance. At least not where it wasn't traced to some basic misuse of something (perhaps overuse and incorrect use of regexes.)
Presumably by "build costs" you mean development cost. Since most places don't rigorously track costs I doubt there is an objective answer to that. However I have seen complex error scenarios be deferred because they would take too much development time. Not sure that the extreme cases matter anyways because they often reflect possible rather than likely user errors.
|
|
|
|
|
In my experience input validation and logging reduce development time and cost, and maintenance time and cost. Error correction, retries, best guesses, and the like, are tricky ones; when provided, make sure you have an easy way to disable them, otherwise your basic (or unit) testing may become very cumbersome.
|
|
|
|
|
See my thoughts regarding the issue here[^]
"You get that on the big jobs."
|
|
|
|
|
|
What is your way of dealing with a composition in a class?
Will you force the user to pass the required values to the contained objects?
Or you'll let the user set the contained object anytime later and check for it's validity everywhere?
For example,
class Rocket
{
Booster b1;
Rocket(Boostertype bt, BoosterPower bp)
{
b1 = new Booster(bt,bp);
}
Launch()
{
b1.fire();
}
}
Or it could be done this way
class Rocket
{
Booster b1;
Rocket()
{
}
initBooster(Boostertype bt, BoosterPower bp)
{
b1 = new Booster(bt,bp);
}
Launch()
{
if(null!=b1)
{
if(b1.bp >100)
{
}
}
}
}
My preferred way is to enforce Rocket's constructor with params required for booster as well.
Unless we pass a sensible value for the booster, rocket creation will fail.
But my mate here seem to differ with my opinion. He doesn't want Rocket's creation to be disturbed by booster constructors parameters.
Instead, when a Launch() is called , it should throw an exception saying Booster is not initialized/configured.
Which one would you prefer?
Starting to think people post kid pics in their profiles because that was the last time they were cute - Jeremy.
|
|
|
|
|
It depends on how you want to interact with the composite member.
I will often use an interface for the composite member that forces certain behavior on the parent.
To continue with your example. Can a rocket exist without the booster? Yes, then it does not belong in the constructor; no, then it must be initialised.
I would have:
interface BoosterInterface {
Booster getBooster();
Boostertype getBoosterType();
Boostertype getBoosterPower();
void initBooster(Boostertype bt, BoosterPower bp);
void setBooster(Booster bl);
void setBoosterType(Boostertype bt);
void setBoosterPower(BoosterPower bp);
}
class Rocket
impliments BoosterInterface {
}
Having the interface means that you can of course force behaviour to the containing class.
My two pennies worth.
Panic, Chaos, Destruction. My work here is done.
Drink. Get drunk. Fall over - P O'H
OK, I will win to day or my name isn't Ethel Crudacre! - DD Ethel Crudacre
I cannot live by bread alone. Bacon and ketchup are needed as well. - Trollslayer
Have a bit more patience with newbies. Of course some of them act dumb - they're often *students*, for heaven's sake - Terry Pratchett
|
|
|
|
|
Makes sense.
Starting to think people post kid pics in their profiles because that was the last time they were cute - Jeremy.
|
|
|
|
|
It's an interesting one to say the least. One of the principals of OO development is that you shouldn't return an object out of a constructor that requires further configuration to be useful*. In other words, you shouldn't need to set additional properties before the class is useful. In this particular case, the fact that you can get to the Launch code without setting it, and the first point that you notice that there's a failure is at this point would indicate that the Booster should have been applied earlier on.
*Now, looking at your sample, it would seem that what you are looking at here is an object that would typically be composed using Dependency Injection, so it would be reasonable to assume that the DI code would actually compose your object for you. This would mean that you would be exposing a dependency that your code was reliant on - and this is where it gets slightly murkier. If you favour the wide constructor approach to composition, then every time you need to add an extra dependency to your class, you have to add it to your constructor, and add a backing field for that type. You may prefer the dependency property (I'm not using this in the WPF way here) approach, which means that you would expose a property for it - and this could be an auto property so you wouldn't need to add the field yourself as the compiler takes care of this for you behind the scenes.
|
|
|
|
|
Using the analogy of the rocket, you can still perform a lot of actions - giro tests, etc - without a booster. The booster is an added item, not necessarily an intrinsic part. If the rocket must have a booster, then I agree it must be a part of the constructor to ensure its presence.
Panic, Chaos, Destruction. My work here is done.
Drink. Get drunk. Fall over - P O'H
OK, I will win to day or my name isn't Ethel Crudacre! - DD Ethel Crudacre
I cannot live by bread alone. Bacon and ketchup are needed as well. - Trollslayer
Have a bit more patience with newbies. Of course some of them act dumb - they're often *students*, for heaven's sake - Terry Pratchett
|
|
|
|
|
Nagy Vilmos wrote: If the rocket must have a booster, then I agree it must be a part of the
constructor to ensure its presence.
And that's the rub. What constitues a valid instance of the class? In the admittedly contrived example given, the only operation that could be performed was a Launch, so the booster is an intrinsic part of this particular example.
Now, if this class were one I was designing, I would take a slightly different approach if this was a full lifecycle of a rocket. This particular rocket implementation is tightly coupled to a single booster. What happens if I want a rocket with more than one booster? Or I want to use one that's based on the ACME Fusion Drive 3000 elastic band? To that end, I'd consider that a design like this might be better:
public class Rocket
{
public Rocket()
{
}
public void PreLaunchTests()
{
OnPreLaunchTests();
}
public void Launch()
{
}
protected virtual void OnPreLaunchTests()
{
}
protected virtual void OnLaunch()
{
}
}
public class SingleBoosterRocket : Rocket
{
public List<IBooster> Boosters { get; private set; }
public SingleBoosterRocket()
{
Boosters = new List<IBooster>();
}
public void AddBooster(IBooster booster)
{
Boosters.Add(booster);
}
protected override void OnPreLaunchTests()
{
}
protected override void OnLaunch()
{
if (Boosters.Count == 0)
throw new NotEnoughBoostersException();
}
}
public class DualBoosterRocket : SingleBoosterRocket
{
public DualBoosterRocket() : base()
{
}
protected override void OnPreLaunchTests()
{
}
protected override void OnLaunch()
{
if (Boosters.Count < 2)
throw new NotEnoughBoostersException();
}
} Again, this is a completely fabricated design, but this has the advantage of being more flexible. Ultimately, the design should be based on the real requirements, which is why I have steered well clear of saying that one approach is better than another in all cases. BTW - in case you're wondering, the actual composition in this sample has been moved to the containing object, rather than the actual rocket implementation, as that piece of code would be the one that decided what the composition entailed. The rules for the rocket have been encapsulated in a simple classes that have a single responsibility.
|
|
|
|
|
Thanks for the example Pete
Starting to think people post kid pics in their profiles because that was the last time they were cute - Jeremy.
|
|
|
|
|
|
As Pete says, a constructor should always return a valid object, that is, you should be able to use it in normal situations. So the answer to the question depends on whether a Rocket can be useful without a Booster. In your simplistic example, where it has one method and that method throws an exception if it isn't set, it clearly can't, and therefore the Booster should be assigned in the constructor (and, if applicable, a null check applied there).
|
|
|
|
|
Neither. Rocket should be able to call a factory method "Boosters.GetBooster()", or some other suitable mechanism to acquire what it needs, when it needs it.
Marc
|
|
|
|
|
Funnily enough, that was the gist of the answer that I've just added above.
|
|
|
|
|
Pete O'Hanlon wrote: Funnily enough, that was the gist of the answer that I've just added above.
I read it, but your response was too wordy for me to figure out what you were saying! But none-the-less, two great minds, a single thought between them, eh?
Dependency Injection, hmmm... it's really weird to me how we have these (what I consider to be) massive frameworks to support DI when all it really needs is a simple instantiation engine.
Anyways, I'm getting distracted by the lure of an unoccupied soapbox.
Marc
|
|
|
|
|
We internally have a DI engine that is only 60 lines or so of code, and it's good enough for our purposes when all we need is basic DI.
|
|
|
|
|
Pete O'Hanlon wrote: We internally have a DI engine that is only 60 lines or so of code
Sweet!
Marc
|
|
|
|
|
I think we can think about two scenarios:
1. First, On the ground, we "build" the rocket with boosters and make a launch.
2. We don't build or assemble a rocket, but when a launch is called, it acquires a booster and executes a launch.
The second one is loosely tied.
The Boosters.GetBooster() can be applied for both the scenarios?
Starting to think people post kid pics in their profiles because that was the last time they were cute - Jeremy.
|
|
|
|
|
VuNic wrote: The Boosters.GetBooster() can be applied for both the scenarios?
I think so. In the first scenario, the constructor can call GetBooster to get a concrete instance, though I think it would be much more preferable for Rocket to have a Build() method.
In the second scenario, and this may be over the top, consider that we know that a rocket, among other things, has some form of propulsion mechanism, otherwise it wouldn't be a rocket. Now, a rocket might have a main engine, boosters, stages, ion engines, so forth, a whole slew of combinations of things. So if rocket is to have a "has a" relationship to booster, the concept of booster needs to be abstracted to "propulsion systems", ideally a collection of this abstraction type. The GetBooster factory might return a collection specific to the rocket's requirements. You can define specific collections as a concrete type (there's most likely a limited, well defined set of configurations). Each type (the configuration of propulsion systems) probably has specific launch behaviors. Therefore, the type itself contains all the logic for how to proceed with a launch, going from main engine firing, booster firing, stages, and final orbit maneuverings with ion engines. So, why should the rocket itself know how to do this? The rocket is just a container for a particular configuration. Maybe the rocket performs some coordination with other systems, but the execution of the launch should be delegated to the configuration type.
Which is where functional programming can be used, not only for "computing" the launch, but also for ensuring that configurations, using discriminated unions, are permitted.
Marc
|
|
|
|
|
Hmm as I understand, the design decision completely relies on the exact requirement. More thinking is required to realize what is the system about. The more we are precise with the actual system, the better and sensible design we could make. Thanks for the inputs Marc. All learning for me
Starting to think people post kid pics in their profiles because that was the last time they were cute - Jeremy.
|
|
|
|
|
This is probably a really basic database design question, but for some reason, I'm struggling with it.
The real world problem I'm trying to solve is a little too escoteric to describe, so I'll try to use a generic example that presents the same problem.
Say a store sells two kinds of cogs. Each type of cog is represented in the database with its own table (they have sufficiently different characteristics to warent different table schemas). A customer can buy both kinds of cogs, and we want to keep track of their purchases. So we have a sales table that's associates the customer with the cogs he/she buys. One foreign key points to the customer, and another foreign points to the cog.
What's got me stumped is that the foreign key that points to the cog could be associated with either type of cogs. How do we know which? Do we create an additional field that tells us the type of cog the customer purchased? That rings alarms for me in that it's surely unnormalized.
So instead do we have seperate sales table for each type of cog? But wouldn't this lead to an explosion of tables as we add more cogs types to our inventory?
This must seem like a beginner's question; I'm an old C++ audio programmer who's become a 'web developer', so occasionally I get stumped on DB basics. Any help is appreciated.
|
|
|
|
|
You need to find a way of describing n widgets from a single table so that the problem of new widgets does not require more tables which means a nightmare in maintenance and changing application pages that add, edit and display the data. Even if you can break it down to tables of classes of widgets you might be able to have a widget type table that keys into a widget class table. Even then you may find that you need to add new widget classes to cope but if you can make the table columns generic enough this may not be an issue. Just a quick thought.
"If you think it's expensive to hire a professional to do the job, wait until you hire an amateur." Red Adair.
nils illegitimus carborundum
me, me, me
|
|
|
|
|
Thanks for your reply.
The solution I've settled on uses part of your answer. I have a 'base' table for all items. Then I have tables where needed for 'derived' sub types. These derived tables have a 'zero to one' relationship to the base table. The derived tables are a way of extending the properties of items in the base table.
So the items table could look like this:
+----+--------+-------+
| id | name | price |
+----+--------+-------+
| 1 | wheel | $1.23 |
+----+--------+-------+
| 2 | wire | $1.50 |
+----+--------+-------+
Then if there are more than one kind of cogs, I can subclass them in 'derived' tables (like derived classes in object oriented programming).
wheels table
+----+--------+--------+
| id | cog_id | radius |
+----+--------+--------+
| 1 | 1 | 50" |
+----+--------+--------+
wires table
+----+--------+--------+
| id | cog_id | length |
+----+--------+--------+
| 1 | 2 | 12" |
+----+--------+--------+
I can then do joins to get back only wheels, and another join to get just wires.
I'm not sure if this is a common approach. I know the joins will cost more processing, but at least the schema makes sense to me.
|
|
|
|
|