3

How can I avoid duplicate use of doSomethingCommon() in the following block of code?

doSomething();

if (conditionA) {
    doSomethingSpecificToConditionA();
    doSomethingCommon();
}
else if (conditionB) {
    doSomethingSpecificToConditionB();
    doSomethingCommon();
}

doSomethingElse();

NB: Calculating conditionA and conditionB can be expensive.

Ehtesham Hasan
  • 149
  • 1
  • 6
  • related (possibly a duplicate): [How to do a clean refactoring of an If Else Code without leaving any free blocks?](https://softwareengineering.stackexchange.com/q/220888/31260) – gnat Jul 29 '18 at 08:01
  • 5
    Calling a method multiple times in an `if` statement is *not* code duplication. The whole point of an "extract method" refactoring is to eliminate code duplication, so multiple calls to a method don't count. – Robert Harvey Jul 29 '18 at 14:16
  • @RobertHarvey Please post as an answer and I'll accept it. – Ehtesham Hasan Jul 29 '18 at 17:04
  • 2
    The DRY principle has a lot to answer for. It's **one** line of code that's duplicated. Any alternatives are going to be uglier, less readable or more complicated than what you have here. – Simon B Jul 30 '18 at 15:48

6 Answers6

5

Calling doSomethingCommon() from 2 conditional blocks should be fine, and technically NOT code duplication, as only one of the conditional blocks will be executed at any given time.

However, if doSomethingCommon() is not already a separate method, you should refactor it into a method and again calling the same is NOT code duplication.

Ehtesham Hasan
  • 149
  • 1
  • 6
  • 3
    If your first assumption was true, then the problem would be trivial. OP would have figured it out himself. But he did not. The problem, as it is presented, implies it is not possible! – Aphton Jul 29 '18 at 07:32
  • 1
    @Aphton Hence the second part of the answer. Also, as a general rule, assumptions, especially about the cost of boolean checks, should always be avoided. – nikhil jhaveri Jul 29 '18 at 07:36
  • 1
    Well, you put it into the else-branch too. So, that's different code. – Deduplicator Jul 30 '18 at 18:44
5

Introduce a special condition handler function:

doSomething();

handleSpecialConditions();

doSomethingElse();

...

void handleSpecialConditions()
{
   if (conditionA)
      doSomethingSpecificToConditionA();
   else if (conditionB)
      doSomethingSpecificToConditionB();
   else
      return;

   doSomethingCommon();
}
Eternal21
  • 1,584
  • 9
  • 11
1

Consider using inheritance to avoid multiple conditionals in a method

public class SomethingA : Something
{
    public override void DoSomething()
    {
        base.DoSomething() //all the common things
        this.SpecialForASomethings();
    }
}
Ewan
  • 70,664
  • 5
  • 76
  • 161
  • If it came to inheritance, I'd prefer to see the template pattern. Something like [this](https://pastebin.com/aMmMJyEU). – Andy Jul 29 '18 at 10:32
  • hmmm.. that seems super bad to me. but we do have limited data to go off, why dont you write a full answer? – Ewan Jul 29 '18 at 10:47
  • 1
    The solution I proposed circumvents the problem of you accidentally forgetting to call the `base.DoSomething()` method, because it's called by the template. Because of that I like to put the necessary calls in the parent class and let the children only really define the specialisations. When it comes to the full answer, I don't feel like answering this question because the question is too broad and you'd need much more information to find the right solution. Perhaps the template pattern would be overkill in this situation, other time the if/else if is the wrong path. It depends. – Andy Jul 29 '18 at 14:23
  • 1
    This answer assumes that `doSomethingCommon()` can reasonably be abstracted into a base class where A/B can represent its subtypes. As there is no inheritance present in the example, not even a mention of objects altogether, suggesting inheritance seems like a very big leap to make. I'm not saying you're wrong, I'm just saying that your answer only applies to a small subset of possible scenarios that OP's example could be taking place in. – Flater Jul 30 '18 at 11:08
  • @Flater it's such a generic example I don't think you can make assumptions either way. Q: 'how do I avoid conditionals?', A: 'use inheritance', Q: 'how do i avoid conditionals that have a special problem which means inheritance doesn't work?', A: '98% of the time, refactor and use inheritance' – Ewan Jul 30 '18 at 11:14
  • A2: 'use a dictionary of method objects', A3: 'like inheritance but use composition' – Ewan Jul 30 '18 at 11:15
  • @Ewan I specifically mentioned you weren't wrong but that your answer wasn't universally applicable. `Q: 'how do i avoid conditionals that have a special problem which means inheritance doesn't work?', A: '98% of the time, refactor and use inheritance'` This is either really misguided or dead wrong. There is no inherent relationship between "conditionals" (which are essentially boolean evaluations) and inheritance. Your suggestion seems to be based on nothing more than the assertion that anything can be rewritten to fit OOP principles, which is an excessive blanket approach and bad practice. – Flater Jul 30 '18 at 11:18
0

If you don't want to extract handleSpecialConditions() like Eternal21 suggested (you probably should), consider memoizing:

doSomething();

bool flagA;
if (((flagA = conditionA)) || conditionB) {
    if (flagA)
        doSomethingSpecificToConditionA();
    else
        doSomethingSpecificToConditionB();
    doSomethingCommon();
}

doSomethingElse();
Deduplicator
  • 8,591
  • 5
  • 31
  • 50
  • 2
    This is less readable than the original code. – user253751 Jul 31 '18 at 04:50
  • @immibis: If `doSomethingSpecific….` is just that one call of a well-named function, sure. It could be more complicated though, defying easy naming, and being very involved with the rest of the function. The simplified example-code gives no clues either way. Sure, that's unlikely, but it's better to know the option. – Deduplicator Jul 31 '18 at 12:18
-1

You should also consider other best practices like Prefer Composition over inheritance. So my suggestion would look like this:

class CommonBehavior{
  void doSomethingCommon(){
    //...
  }
}

interface SpecialBehavoior{
  void doSomethingSpecial();
}

class SpecialBehavior1 implements SpecialBehavoior{
   @Inject
   private CommonBehavior commonBehavior;
   @Override
   public void doSomethingSpecial(){
    // what ever
        commonBehavior.doSomethingCommon();
    // what ever more
   }
}

class SpecialBehavior2 implements SpecialBehavoior{
   @Inject
   private CommonBehavior commonBehavior;
   @Override
   public void doSomethingSpecial(){
    // what ever
        commonBehavior.doSomethingCommon();
    // what ever more
   }
}

This unleashes its full power when combined with the Tell, don't ask! principle. That means that you convert your "conditions" as soon as possible to SpecialBehavior objects (usually in a Factory class that still uses ifs and/or switches) and pass them around.


my question with this is that the calling code needs to know to call the special behaviour vs the common behaviour? – Ewa

This is the "smart" thing about inheritance and tell, don't ask!: the calling code does not need to know. The objects passed have this knowledge.

The calling code "splits up". You create the SpecialBehavior objects as soon as the data enters your system and pass them around to places where methods of a common API are called and any special behavior is coded in the different implementations of that interface.

The code implies that some cases are neither special A or B, how are those dealt with? – Ewan

Doing "nothing" is also a "special behavior" so you need an implementation of the SpecialBehavior interface for that:

class DoNothing implements SpecialBehavoior{
   @Override
   public void doSomethingSpecial(){
        // do nothing
   }
}

This looks like a lot of "glue code" but thanksfully Java has an eye candy named lambda so that you can equally write it like this:

SpecialBehavoior doNothing = ()->{;};

ok, but then shouldnt it be doSomethingCommon which calls doSomethingSpecial? – Ewan

Both ways are possible and it depends on your requirements.

As far as I understood your "special behavior" is mutual exclusive. In this case it is better to have a single instance of the CommonBehavior class passed to the individual instances of SpecialBehavior implementations. Especially the "DoNothing" requirement is easy to solve this way.

If you wold do it the other way around you would have two down sides:

  1. You need a separate instance of CommonBehavior as "wrapper" for each and every SpecialBehavior instance.

  2. There is no Tell, don't ask! way to prevent the "common behavior" from being executed.

    This means you need something like

    if(!specialBehavior instance of DoNothing){
       // do the common behavior
    }
    

    and that is exactly the kind of code we initially tried to avoid.

Timothy Truckle
  • 2,336
  • 9
  • 12
  • my question with this is that the calling code needs to know to call the special behaviour vs the common behaviour? The code implies that some cases are neither special A or B, how are those dealt with? – Ewan Jul 29 '18 at 14:15
  • ok, but then shouldnt it be doSomethingCommon which calls doSomethingSpecial? – Ewan Jul 29 '18 at 16:30
  • @Ewan please see my next update – Timothy Truckle Jul 29 '18 at 17:00
  • maybe im just not following. Your saying that everything implements specialBehaviour, including a class which only runs the common behaviour? I dont see how this stops you forgetting to call commomBehaviour in one of these classes. Its simply a replacement of inheritance with composition and the interface should simply be IDoSomthing and make no reference to special behaviour? – Ewan Jul 29 '18 at 17:06
  • I though you were saying the parent class would implement a doSomething() which called a virtual DoSpecialThing, which would then be overridden in child classes? thus making it impossible to leave out the common behavioir – Ewan Jul 29 '18 at 17:07
  • @Ewan *"Its simply a replacement of inheritance with composition and the interface should simply be IDoSomthing and make no reference to special behaviour?"* I meant exactly that. I just kept the `doSomethingSpecial()` name to make it easier to follow (I thought). – Timothy Truckle Jul 29 '18 at 17:12
  • but it doesnt work with composition. you have the 'child' classes calling commonBehaviour.dosomething() same as my base.doSomething() (and commonBehaviour not implementing the interface) – Ewan Jul 29 '18 at 17:14
  • @Ewan *"parent class would implement a doSomething() which called a virtual DoSpecialThing, which would then be overridden in child classes? thus making it impossible to leave out the common behavioir"* no. The `SpecialBehavior` instance decides if it wants to use the *common behavior*. It decides if it want to apply the *common behavior* before or after or in the middle of its own special behavior or not at all. – Timothy Truckle Jul 29 '18 at 17:14
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/80841/discussion-between-timothy-truckle-and-ewan). – Timothy Truckle Jul 29 '18 at 17:15
  • 1
    This is clearly far more confusing and unreadable than the original code. – 17 of 26 Jul 30 '18 at 15:46
  • @17of26 ths is a completely different approch. I agree that this may be "overkill" for the concrete given situation, but usually this kind of problem tends to repeat all over your code. And when you have a second place where you branch by the same conditions it's worth to concider *replacing branching with inheritance* and *tell, don't ask!*. – Timothy Truckle Aug 03 '18 at 10:44
-3

Boolean checks are not expensive:

doSomething()

if (conditionA) {
    doSomethingSpecificToConditionA();
}
else if (conditionB) {
    doSomethingSpecificToConditionB();
}

if (conditionA || conditionB) {
    doSomethingCommon();
}

doSomethingElse()
Aphton
  • 98
  • 1
  • 4