3

I apologize in advance if a similar question has been asked before, it's quite complicated to describe correctly what I am looking for, and I will use an example to explain.

We will work with a base class named Shape with the following child classes : Triangle, Square, Pentagon and Hexagon. The 4 last classes represent a shape with 3, 4, 5 and 6 sides respectively. This information "belongs" to the class itself and not to the instance of those classes.

We will also suppose that each class has a static method returning a given color. Every instance of a given class will share the same color.

What I want to do is to call the static function getColor() of my shapes in the ascending order of their number of sides. Meaning, I want to call :

  1. Triangle.getColor()
  2. Square.getColor()
  3. Pentagon.getColor()
  4. Hexagon.getColor()

Unfortunately, I have the following problems (shared by many programming languages)

  • I can't use Interfaces because the information does not belong to the instances but the classes

  • I can't define the getSideCount() static function inside my Shape class because I would not be able to "override" it in my child classes to get the correct number of sides

child classes

I am not asking for a complete code, only for design advices to manage this problem. Maybe I am totally wrong and I should not go this way. Don't hesitate to criticize and suggest a new way to do this.


If you want a more "concrete" example :

I have a string myString.

I have multiple classes A, B, C defining the toString method.

  • a.toString() returns a string of 1 character
  • b.toString() returns a string of 2 characters
  • c.toString() returns a string of 3 characters

myString is the concatenation of a.toString(), b.toString() and c.toString() : ABBCCC.

Tomorrow, I may want that myString is the concatenation of c.toString(), a.toString() and b.toString() : CCCABB. Thus, I defined a static method in the classes A, B and C returning the position of the representation of the instance in myString. What I want to do is to extract in the correct order the representation of my instances.

The "long" way to do this would be :

index ← 0
if( A.position == 1 )
    aStr ← extract( myString, index, 1 )
    index ← index + 1
elif ( B.position == 1 )
    bStr ← extract( myString, index, 2 )
    index ← index + 2
elif ( C.position == 1 )
    cStr ← extract( myString, index, 3 )
    index ← index + 3
endif

if( A.position == 2 )
    aStr  ← extract( myString, index, 1 )
    index ← index + 1
elif ( B.position == 2 )
    bStr ← extract( myString, index, 2 )
    index ← index + 2
elif ( C.position == 2 )
    cStr ← extract( myString, index, 3 )
    index ← index + 3
endif

if( A.position == 3 )
    aStr  ← extract( myString, index, 1 )
    index ← index + 1
elif ( B.position == 3 )
    bStr ← extract( myString, index, 2 )
    index ← index + 2
elif ( C.position == 3 )
    cStr ← extract( myString, index, 3 )
    index ← index + 3
endif

Thank you in advance for your time and your help.

Hellium
  • 7,206
  • 2
  • 19
  • 49
  • It might be better to post the actual problem you're trying to solve, because I don't believe there's a generic "best way" to do what you want to do - it would depend largely on the problem. You may be trying to use OOP in a way it wasn't designed for. – Bernhard Barker Mar 20 '17 at 18:19
  • The second part of the question (more "concrete" example) is almost what I have in code. I just simplified the names of my classes. – Hellium Mar 20 '17 at 20:18
  • 2
    Now that you've updated the question, it seems unlikely that these methods should really be static methods. You should strive to make each class have a single responsibility, i.e. a class should not care about "the position where its string representation will end up in some external class". Just move this information in a different class, which will parse the input string according to certain rules. – vgru Mar 20 '17 at 22:36
  • I think I fell into the ["XY problem"](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). I think I will close the question and maybe open an other one with a better description of my acual problem. Thank you all for you help, especially @Groo – Hellium Mar 21 '17 at 08:48

2 Answers2

2

There are several ways you can approach this.

  1. Probably the simplest one would be pattern matching, if your language supports it (Scala, F#, Haskell). With support for (exchaustive) pattern matching, you would differentiate on the object type:

    // syntax will differ from language to language,
    // but you should get the idea
    
    def getColor(s: Shape): Color = s match {
      case Triangle => Color.Blue
      case Square   => Color.Yellow
      ...
    }
    

    This effectively moves the getColor method outside of the class, but the cool thing that you won't get with a plain "switch-case" is that compiler will warn you if you don't cover all the cases, so you effectively get the same compile-time guarantee as with abstract methods.

  2. In general, what you need is a way to map from a type to a value, so you might as well simply use a map. In C#, you would use a Dictionary<Key, Value>:

    static readonly Dictionary<Type, Color> _shapeToColor;
    
    // use this to register a color for each type somewhere at the beginning
    public static Register<T>(Color color) where T : Shape
    {
        _shapeToColor[typeof(T)] = color;
    }
    
    // generic version for compile-time read
    public static Color GetColor<T>() where T : Shape => _shapeToColor[typeof(T)];
    
    // or a parameterized version for run-time read
    // (but might fail if incorrect type passed)
    public static Color GetColor(Type t) => _shapeToColor[t];
    
    // or generally
    public static Color GetColor(object x) => _shapeToColor[x.GetType()];
    

    This is allows you greater flexibility, but you can easily forget to register a color for a newly added type. Also, you would probably register a ShapeInfo class for each color, and put there more than just a Color, so that you don't have several dictionaries.

  3. Another option (again a C# example, sorry if you're using Java) is to actually combine instance methods with the previous idea (static dictionary) and some reflection. The idea would then be something like:

    interface IShape
    {
        Color GetColor();
    }
    
    class Triangle : IShape
    {
        public Color GetColor() => Color.Blue;
    }
    
    class Square : IShape
    {
        public Color GetColor() => Color.Red;
    }
    
    static void InitializeValues()
    {
        // use reflection to iterate through all types
        var asm = Assembly.GetAssembly(typeof(IShape));
        foreach (var t in asm.GetTypes())
        {
            // find all Shapes 
            if (t.IsInterface ||
                t.IsAbstract ||
                !typeof(IConfigTokenizer).IsAssignableFrom(t))
                continue;
    
            // instantiate a temporary shape
            var inst = (IShape)Activator.CreateInstance(t);
    
            // but we are only interested in creating
            // a mapping to the result of GetColor
            _shapeToColor[t] = inst.GetColor();
        }
    }
    

But now that you've updated the question with that toString stuff, I am no longer sure what your actual goal is. :)

vgru
  • 49,838
  • 16
  • 120
  • 201
0

I might misunderstand, but I would have an abstract method in the shape class, which is overridden in each of the child classes. Then, with an array of your objects, you can sort them based on the values returned from their sideNumber() methods. Then just iterate through the sorted array calling get color on each one.

Justin Sanders
  • 313
  • 2
  • 12