tags:

views:

193

answers:

8

What are (if any)the implied assumptions or restrictions and the differences of designing like:

A) this:

class SampleClass1
{
    IWorker workerA;
    IWorker workerB;

    void setWorkerA(IWorker w);
    void setWorkerB(IWorker w);
    WorkResult doWork();
}

B) versus this:

class SampleClass2
{
    WorkResult doWork(IWorker workerA, IWorker workerB);
}

I know it depends on the specific project but what if the above class is a part of a small framework? The first Class is able to maintain state and separate the steps more naturaly but Second class ensures "real time communication" with the external caller more naturaly since Worker are passed each time doWork() is called.

Are there any recommended usages or generic practices that guide the choice between the two above ways? Thanks.

A: 

Another Option:

IWorker class:

static WorkResult doWork(Iworker a, Iworker b);

Richie_W
A: 

If more than one method depends on IWorker a and IWorker b, I say do sample A.

If only doWork() uses both IWorker a and IWorker b, then do sample B.

Also, what is the real purpose of your SampleClass? doWork looks a bit like a utility method mroe than anything else.

Jon Limjap
A: 

A) is a bad design because it allows the object to be defective (one or both of the worker classes might not have been set).

B) can be good. Make it static though if you do not depend on the internal state of SampleClass2

Thorsten79
I think in this basic example, we can't really call A a bad design because it's only a representative of a class structure, not really the actual class. Also, doWork could throw an exception if it doesn't have an instance of both classes, which could be acceptable.
scubabbl
I understand the question as a design question. And classes like A) are typical IMHO for classes that make a lot of problems when used in other parts of the code. They may be transparent to the designer of the original class, but are typical points of failure when used by someone else.
Thorsten79
A: 

IMO 2nd approach looks better, it requires caller to use less code to perform a task. 2nd approach is less error prone, caller don't need to worry that object might be not initialized completely.

aku
A: 

How about instead defining a WorkDelegate (or alternatively an interface having a single doWork method without argument) that simply returns a WorkResult and letting individual classes decide how they implement it? This way, you don't confine yourself to premature decisions.

Konrad Rudolph
+3  A: 

SampleClass1

  • I may need to maintain state of the workers between doWork
  • I might need the capability to set Workers individually. (doWork with 1 and 2, then with 2 and 3)
  • I want to maintain the workers because it might be expected to run doWork multiple times on the same workers.
  • I'm not a utility class. An instance of me is important.

SampleClass2

  • Give me two workers and I will do work with them.
  • I don't care who they are and I don't want to maintain them.
  • It's someone else's job to maintain any pairing between workers.
  • I may be more of a utility class. Maybe I can just be static.
scubabbl
+4  A: 

In option (A) you are creating what is known as a Function Object or Functor, this is a design pattern that is well documented.

The two main advantages are:

  • The workers can be set by in one place and then the object used elsewhere
  • The object can retain state between calls

Also if you are using a dependency injection framework (Spring, Guice etc...) the functor can be automatically initialized and injected wherever required.

Function objects are extensively used in libraries e.g. the C++ Standard Template Library

Garth Gilmour
A: 

Another option, a variant of case A, is the following:

class SampleClass3
{
    SampleClass3( IWorker workerA, IWorker workerB );
    WorkResult doWork();
}

Advantages:

  • It's harder to make the object defective, since you are required to supply all the workers that are needed at construction time (in contrast to case A).

  • You can still carry state inside SampleClass3 and/or one of the workers. (This is impossible in case B.)

Disadvantages:

  • You have to have all your workers ready before you construct SampleClass3, instead of being able to provide them later. Of course, you could also provide the setters, so that they can be changed later.
Andy Balaam