-1

I have a base class with a method called Update:

Start Update

    Code block 1 (An If statement)

    Code block 2 (Setting a variable based on the If result)

    Code block 3 (A switch which is setting something)

End Update

The order in which the code is executed is important so they can't be shifted around.

I also have a derived class which needs a little bit more code. This code however has to be in between Code Block 1 and 2. So when I would rewrite the entire thing it would be something like this:

Start Update

    Code block 1 (An If statement)

    Code block 4 (An extra calculation based on the If result)

    Code block 2 (Setting a variable based on the If result)

    Code block 3 (A switch which is setting something)

End Update

I'm looking for ways how to reuse Code Block 1 through 3 and also put Code Block 4 in there.

The best thing I came up with is having a method called Extra in my base class and have the Update method look like this:

Start Update

    Code block 1 (An If statement)

    Call Extra

    Code block 2 (Setting a variable based on the If result)

    Code block 3 (A switch which is setting something)

End Update

In the base class the Extra method would be empty since it has no use here. The derived class would also have the Extra method but in that method the following would be called:

Start Extra

    Code block 4 (An extra calculation based on the If result)

End Extra
Krowi
  • 157
  • 4
  • 1
    I was going to propose the same way of work so this looks fine to me. – Carra Jan 30 '18 at 16:51
  • 2
    What’s the problem with your proposed solution? Looks good to me! And it’s in fact an established design pattern ([template method](https://en.wikipedia.org/wiki/Template_method_pattern)). – Konrad Rudolph Feb 02 '18 at 14:06

4 Answers4

4

Here's an alternative approach to consider.

Make Blocks 1, 2 and 3 into (protected) methods on the base class. Make the base class' Update call these three methods.

Add Block 4 as a (private) method on the derived class. Make the derived class' Update call all four methods in the required order.

You've got a bit of a contradiction between the DRY principle here and the Open/Closed principle. Really, you shouldn't be hacking about in the base class to add functionality to the derived one. But making the code clean, maintainable and readable is more important than obsessing about the various SOLID principles.

Simon B
  • 9,167
  • 4
  • 26
  • 33
  • This is a good suggestion but what if I need to add something in the base class between Blocks 2 and 3? Your approach would mean I have to insert this in the base class but also in every derived class because there I'm recalling the 3 base methods + the extra 4th one + the new base one. Right now I have 5 instances of this so I'm afraid maintaining this might become a hassle in the future. Would you still go with this approach if you have this new information (I just found out myself)? – Krowi Jan 31 '18 at 07:22
  • @Krowi Try to come up with a solution that will cause the least confusion for anyone who follows you. If they all follow essentially the same pattern, but where sometimes a Block is a no-operation, then you could make them all follow the same sequence, with empty methods where appropriate. Define functions for each block, using overrides as appropriate in derived classes. – Simon B Jan 31 '18 at 08:48
  • Hmm so if I understand this answer correctly you’re calling the [template method pattern](https://en.wikipedia.org/wiki/Template_method_pattern) bad? Because that’s what OP’s current solution amounts to. – Konrad Rudolph Feb 02 '18 at 14:07
  • 1
    @KonradRudolph Not necessarily, if that's the design that was intended. Designing a base class with pluggable behaviour looks fine to me. Hacking the base class whenever you realise there's something missing in the derived class is a problem (as it can mess up other derived classes) - the Open/Closed principle. – Simon B Feb 02 '18 at 15:46
0

I understand your question as being about style, not so much about technical possibilities.

From the comments in your code sample, I'd like to give names to your blocks (you can surely do better than I, knowing what the steps really are about):

  • FindUpdateSituation
  • SetGenericData
  • SetCaseSpecificData

Whatever you name the blocks, it will surely make sense to refactor them into individual methods.

Now the question is "Where do the additional actions that we called Block4 fit best?" And that needs some analysis, where I see (at least) three possible outcomes:

Version A:

After some thinking, you find that for your base class and all derived classes, updating still is a 3-step process consisting of the blocks named above, and that Block4 just sets some generic data, and fits into that part. Then override the SetGenericData method, prepending the new action before calling the base implementation.

Version B:

You find that from an abstract point of view, the Update method always should have been a 4-step process, and should have had a SetSpecialVariables block, which just (because of the clever implementation of your base class) happened to be empty there. Then introduce the SetSpecialVariables call into the base implementation, add an empty method there and the proper one in your derived class.

Version C:

You find that it's just coincidence that the Update methods of base class and derived class have some elements in common. Then override the Update method in the subclass, using the refactored individual-block methods if their names (not their implementation) describe what the subclass needs.

Ralf Kleberhoff
  • 5,891
  • 15
  • 19
-1

I think the approach you came up with is a valid one and it is close to the template method design pattern.

you can search for the design pattern for further info and see if it matches your requirements

Edit to make the answer clear, let's say that you have a base alogrithm for a generic solution that consists of certain steps:

begin Solution call step 1 call step 2 call step 3 call step 4 end solution

and the solution needs to be customized in certain scenarios by adding extra pieces of code in between the known steps. one way to do it is to do as Simon B said: 1- make the known steps protected in the parent class 2- create a method that contains the extra code you need in the child class 2- define a solution method in the child class and call the known steps from the parent class and insert your call you extra code in the correct place.

one risk you have here is that any class inheriting you base class will have the ability to modify your solution by defining their solution method and doing whatever they want or simply changing the order of the steps call.

what I think you can do is define the following proteceted no-op methods in the base class: preStep1 postStep1 preStep2 postStep2 preStep3 postStep3 preStep4 postStep4

and then modify the solution method to look like:

begin Solution call preStep 1 call step 1 call postStep 1 call preStep 2 call step 2 call postStep 2 call preStep 3 call step 3 call postStep 3 call preStep 4 call step 4 call postStep 4 end solution

and then in the child class, you can modify any the of preStep* or postStep* as needed to insert your code in the correct place.

also, if you want to make sure the general flow of the solution does not change (step1 -->step2-->step3-->step4) then you can make the solution method in the parent class final.

this mathces the open-closed principle, since in the future if you want to define another child class with a different code to be inserted -let's say before step 4- then you just need to extend the parent class and override the the preStep4 method. thus, you added a new code in the middle of the base solution but the only change is the declaration of the new child class.

and I believe alot of existing frameworks use this design pattern and call these extra methods lifeCycle hooks that developer can use to execute extra code at specific points of time in the lifecycle.

-2

Setting your base class method with virtual will allow you to override then and reimplement them at derived class.