0

I often find myself in this situation where I have a base class that does a lot of graphics. There are, for example, three strings that are positioned top, middle, bottom of an element. Like a scale or something. Now for every type of scale I make a new class that inherits the base class and simple sets the three strings and everything is fine. But often there is ONE single case, where I need four strings.

Sure, I could just override the method and recalculate for four, BUT if the calculations are based on a lot of offsets etc I would always have to take a look at how the base class does it in the first place and then copy-paste the entire calculation and do it for one more string. This means that every time the base calculation changes, I would need to copy-paste again etc.

On the other hand I could write the base class so super flexible that every tiny step can be overridden and subclasses can do everything they want with ease. But this requires way more work on the base class for ONE special case out of 20.

So either it's more workload in the beginning or the risk of missing a copy paste.

Often I don't even know what will come and you can't write a baseclass that is all-flexible and no one has the time to do so. So overriding and copy-pasting seems the best way to do so, but it still seems way too risky.

How should I approach this? I am looking for solutions that are practical, not a schoolbook approach that is too time consuming in a real work life.

(I mainly work with Java)

Edit: To get a bit away from my example, which is really just an example. Let's say i have a class and 20 subclasses and they work. Now along comes a new subclass that needs ONE thing completely different and just can't use the base classes method. If i override i have to copy paste the entire method and adapt it. If i change anything about the method, then i will have to change 20 classes that are already working fine.

"Do it from the start" works with one method, but what if 10 or more methods could be the cause for trouble. I can't write the base class in a way that for every method a variable-method-class can be used. I could, but that's what i meant above with "extreme workload at the beginning"

6
  • possible duplicate of Rule of thumb for cost vs. savings for code re-use
    – gnat
    Mar 19, 2015 at 13:25
  • 4
    Now for every type of scale I make a new class that inherits the base class and simple sets the three strings and everything is fine. Why do you need a new class just to change what three strings are used? Either way, the answer is use less inheritance.
    – Doval
    Mar 19, 2015 at 13:28
  • every sub class would of course provide data calculated in a special kind of way and give it to the base class that then draws it (in a nutshell)
    – NikkyD
    Mar 19, 2015 at 13:30
  • 1
    Hard to say much of use without seeing an example, but this does sound like it may be an inheritance vs composition thing Mar 19, 2015 at 13:32
  • 2
    Shouldn't the base class be able to correctly draw a scale regardless of how many labeled points you set, provided you set at least two? I think the problem arises from short-sightedness to begin with. Mar 19, 2015 at 13:51

5 Answers 5

4

Another option might be to extract the algorithms in a class that implements an interface or abstract class. Then the calculation can then be very specific to the layout needed. For 19 of the cases pass In a reference to algorithm 1, for the other pass in a reference to algorithm 2. This allows your base class to remain lighter and still allow for the flexibility of having specifics.

Another plus is that if another layout comes along you can add another algorithm 3 class for that case. All while leaving the original base class without all the logic to determine which algorithm to use.

Also if there are methods that are shared you can have algorithm 2 extend algorithm 1 and just modify the methods that are needed.

I suppose theres also the option then of changing the algorithm class thats referenced at run time and swapping out how the layout gets generated, might be helpful.

Where do you decide what algorithm to pass in? Probably when the scale classes are instantiated.

3
  • 1
    However my question is more about "in general" so what if the next issue comes. I would need to make the base class an empty holder for all kinds of other classes to be decorated with
    – NikkyD
    Mar 19, 2015 at 14:43
  • I think the approach above works if a large amount of functionality is reused across all the classes and only specific parts need very custom logic that could be shared.
    – nate
    Mar 20, 2015 at 14:05
  • The approach works if a large amount of functionality is reused and only specific parts need very custom logic that could be shared. If theres lots of differences it makes more sense to pass in a configuration which contains non default parameters. If class 22 needs a 4 item scale but also display green instead of blue, display logic can be added. That makes for a larger more complicated set of classes. As additional cases arise there will be refactoring for additional factors that it wasn't setup for. Unless it gets over engineered for all cases upfront, but that doesn't seem right.
    – nate
    Mar 20, 2015 at 14:18
1

Your first example sounds like it could be solved by designing things always with "n elements" in mind instead of having individual derived classes for one, two and three strings. Having just one class where the list of strings is a parameter which can be of arbitrary length n will probably reduce some duplicate code in your already existing cases, and eliminiate the need for using inheritance at all.

Your second example is more abstract, so it is hard to give you any good advice without seeing the real case. When there are already working 20 subclasses, the fact you cannot easily add one more is often a code or design smell. Maybe the scope of your classes is too big, maybe there is too much inheritance involved, maybe there is something wrong with "case 21". Most of this problems can be solved by thinking twice about it (and sometimes the best option is to live with a pragmatic "duct tape" solution for case 21).

0

Edit: To get a bit away from my example, which is really just an example. Let's say i have a class and 20 subclasses and they work. Now along comes a new subclass that needs ONE thing completely different and just can't use the base classes method. If i override i have to copy paste the entire method and adapt it. If i change anything about the method, then i will have to change 20 classes that are already working fine.

The simplest solution may be to rename the existing method and adapt it so that it handles the new special case correctly.

Then create a new method with the old name, which acts as a wrapper to the renamed function, and provides exactly the same interface as before. That way all the existing code in other classes can carry on as before.

As an example, if your existing method takes three strings as parameters, but you need to handle four. Re-name that method, and add an optional fourth string. Create a new method with the old name that accepts three strings, as before, and which calls the re-named method with an extra blank string added on.

2
  • so the base class should provide a method for every special case that appears ?
    – NikkyD
    Mar 19, 2015 at 17:09
  • Up to a point. Eventually, you may have to admit it was designed wrong in the first place, and just refactor it properly. Adding an extra layer to the inheritance hierarchy may help, so that you can handle different kinds of thing without producing a monstrous base class that does everything. Also see Michael Shaw's answer, and consider whether the whole lot would be neater using composition, rather than inheritance.
    – Simon B
    Mar 20, 2015 at 8:46
0

I know you don't want a "textbook" solution, but this is an ideal problem for the Strategy pattern.

http://en.wikipedia.org/wiki/Strategy_pattern

Implement behaviors for your different algorithms and use them in the classes that require them. This way if something new comes up, you won't have to change what you have, only add a new behavior.

If you don't want to make a big change in code, you can come up with a mixed solution:

  • Extract the code of your current overridable method into a different class (this will be a behavior), and used it there (this will leave your current method as a sort of wrapper).
  • Only override your method to call a different behavior.

This way you are making your code more readable, easier to maintain, your behaviors are reusable and are less likely to introduce bugs due to some code being uses somewhere else: you know where your behaviors are used.

0

The textbook answer to your question is to "use composition over inheritance".

Instead of copy-pasting like crazy or trying to overdesign the ultimate base class, make pieces that solve parts of the problem and put them together.

Using your example, a part that might make sense would be a function that takes a string length, font, etc. and calculates the size of an onscreen rectangle that would fit it. Another part might be a function that takes a list of rectangles to be stacked on top of each other, and figures out a visually pleasing way of arranging them. By putting the parts together, you can now calculate a layout for your strings easily, and there's no difficulty going from 3 strings to 4 strings (especially if you just make a list of the strings in the first place and don't worry about how many of them there are).

Oh, but now we have to support icons, too. No problem, just make a function that takes an icon and returns its rectangular size, then use the rectangle normally. Adding new stuff doesn't involve changing working code, you just add a new piece or use the existing pieces differently.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.