0

I have an interface and 3 function

public interface IDrawingObject
    {
        void Draw(Color c);
    }

    public class DrawTriangle : IDrawingObject
    {
        public void Draw(Color c)
        {
            //for demo purpose
            Console.WriteLine("Drawing Triangle with color " + c.Name );

        }
    }

    public class DrawCircle : IDrawingObject
    {
        public void Draw(Color c)
        {
            //for demo purpose
            Console.WriteLine("Drawing Circle with color " + c.Name);
        }
    }

    public class DrawRectangle : IDrawingObject
    {
        public void Draw(Color c)
        {
            //for demo purpose
            Console.WriteLine("Drawing Rectangle with color " + c.Name);
        }
    }

and this enum

  public enum Shapes
    {
        Circle,
        Rectangle,
        Triangle
    }

and here can be a lot more function(and enums)

I want to have static void Draw(Shapes s, Color c) that selects the right function to call based on this enum, and it looks to me that using if-else(or switch will bloat the code)

So i took another approach which is to use a IDictionary

   private static IDictionary<Shapes, Action<Color>> Mapper = new Dictionary<Shapes, Action<Color>>
{
    { Shapes.Circle, (Color c) => { IDrawingObject draw = new DrawTriangle(); draw.Draw(c);} },
    { Shapes.Rectangle, (Color c) => { IDrawingObject draw = new DrawRectangle(); draw.Draw(c); } },
    { Shapes.Triangle, (Color c) => { IDrawingObject draw = new DrawCircle(); draw.Draw(c); } }
};

and my function is

public static void Draw(Shapes s, Color c)
        {
            if (Mapper.ContainsKey(s))
            {
                Mapper[s](c);
            }
        }

but still, it looks to me that I'm still doing a lot of unscary copy and paste

Is there any better way to do it?

P.S

I have looked here, here

slugster
  • 49,403
  • 14
  • 95
  • 145
styx
  • 1,852
  • 1
  • 11
  • 22
  • Do you need to create a new `IDrawingObject` each time you draw a shape? Can't you just have a `Dictionary`? – canton7 Apr 17 '19 at 11:26
  • Change `Action` to `Func` and call `Draw()` in `Draw()`? – CodeCaster Apr 17 '19 at 11:26
  • You could do it via reflection and the name of the enum / method. i.e. if the enum is Circle then look for a class called DrawCircle that implements IDrawingObject. You will need to be strict and consistent with how you name your classes and enums. – David Christopher Reynolds Apr 17 '19 at 11:30
  • @CodeCaster and canton7, but i need the color as well, should i use `Action>` ? – styx Apr 17 '19 at 11:30
  • This code is no cleaner than a switch, unless the dictionary is used by other methods too. The use of the `Shapes` enum means it's not possible to use double dispatch, overloading or pattern matching - there are no types involved here at all. You could try using `if (Mapper.TryGetValue(s,out var act){ act(c);}` – Panagiotis Kanavos Apr 17 '19 at 11:31
  • Why the `Shapes` enum? Where does it come from? – Panagiotis Kanavos Apr 17 '19 at 11:31
  • @PanagiotisKanavos it's my own enum – styx Apr 17 '19 at 11:32
  • @styx I mean why that, instead of passing objects of different types to an overloaded method? Why not use `IDrawingObject` instances instead of that enum? Is the enum value loaded from a database perhaps? – Panagiotis Kanavos Apr 17 '19 at 11:33
  • @PanagiotisKanavos that's how the legacy is implemented, I do not want to touch it ATM – styx Apr 17 '19 at 11:35
  • @styx The color is passed to the `Draw` method. So you'd do `Mapper[s].Draw(c)` – canton7 Apr 17 '19 at 11:35
  • @styx the way the code is written you get no advantage from the interfaces. The various `Draw` methods may just as well be static methods on a single or multiple static classes – Panagiotis Kanavos Apr 17 '19 at 11:37
  • @styx what you posted looks a bit like an attempt to use F#'s discriminated unions – Panagiotis Kanavos Apr 17 '19 at 11:40
  • @canton7 , can you show me how to write it?(i mean the mapper) – styx Apr 17 '19 at 11:41
  • @styx `private static Dictionary Mapper = new Dictionary() { { Shapes.Circle, new DrawCircle() }, { Shapes.Rectangle, new DrawRectangle() }, { Shapes.Triangle, new DrawTriangle() } };` – canton7 Apr 17 '19 at 11:42
  • @styx Added an answer showing a full example – canton7 Apr 17 '19 at 11:50
  • The canonical OO approach is to model the shapes as classes with a draw method. Then you just need a static method that takes an enum and returns a shape object...which knows how to draw itself..that makes adding new shapes quite easy..you add a shape and ammend the mkShape method...if you're set of shapes is closed and you want to add lots of methods...then look at the visitor pattern. Discrimated unions and visitors are closely related. – MrD at KookerellaLtd Apr 17 '19 at 11:59
  • @MrD if you can show it as a full answer that would be great – styx Apr 17 '19 at 12:05
  • well actually you are ALREADY really doing the canonical OO approach, except you've decided to return an action not an IDrawingObject.. – MrD at KookerellaLtd Apr 17 '19 at 12:06
  • If you want to do it in a more functional style then you'd use the visitor pattern....but you'd still have to map from your (unpleasant) enum to a "Shape" object. – MrD at KookerellaLtd Apr 17 '19 at 12:07
  • @MrD , can you show this part? except you've decided to return an action not an IDrawingObject – styx Apr 17 '19 at 12:09
  • or do it in f#...where you would use sum types rather than enums. – MrD at KookerellaLtd Apr 17 '19 at 12:09
  • ok...the standard OO approahc is virtually what you've got, the problem you have is the enum isnt an OO friendly thing – MrD at KookerellaLtd Apr 17 '19 at 12:10
  • @MrD sadly i cannot abandon the enum – styx Apr 17 '19 at 12:11
  • I've posted an answer...if you can't get rid of it, then just isolate its unpleasantness to a single method that maps from it into shape objects...you can make it look like it doesnt exist (with extension methods) and you just have 1 unsafe method that maps from enums to objects in a switch – MrD at KookerellaLtd Apr 17 '19 at 12:24

4 Answers4

1

while I wouldn't recommend it, you can use reflection to create an instance of the class by the name. Something like this (untested):

var draw = (IDrawingObject)Activator.CreateInstance("AssemblyName", "Draw" + shape.ToString());
draw.Draw();
Z .
  • 12,657
  • 1
  • 31
  • 56
1

This code isn't really cleaner than a switch, unless the dictionary is used by multiple methods. The way the drawing objects and interfaces are used, all Draw methods could easily be static methods on one class.

Answering the exact question, one could use Dictionary.TryGetValue :

public static void Draw(Shapes s, Color c)
{
    if (Mapper.TryGetValue(s,out var act))
    {
        act(c);
    }
 }

All the Draw methods could change to static methods since they don't use any instance members:

private static IDictionary<Shapes, Action<Color>> Mapper = new Dictionary<Shapes, Action<Color>>
{
    [Shapes.Circle]= (Color c) => DrawTriangle.Draw(c),
    [Shapes.Rectangle]= (Color c) => DrawRectangle.Draw(c),
    [Shapes.Triangle]=(Color c) => DrawCircle.Draw(c)
};

If not :

private static IDictionary<Shapes, Action<Color>> Mapper = new Dictionary<Shapes, Action<Color>>
{
    [Shapes.Circle]    = DrawTriangle.Draw,
    [Shapes.Rectangle] = DrawRectangle.Draw,
    [Shapes.Triangle]  = DrawCircle.Draw
};

Update

BTW that syntax shows, there's something weird going on. Using types instead of an enum would have prevented drawing circles when a circle is requested

We'd need to know more about where Shapes comes from, or why interfaces are used to create a better implementation, and avoid using the wrong implementation

Just for fun by the way, one could use C# 8's switch expressions together with the interfaces :

var drawer= shapes switch 
            {
                Shapes.Circle   =>new DrawingTriangle(),
                Shapes.Rectangle=>new DrawingRectangle(),
                Shapes.Triangle =>new DrawingCircle(),
                _ => ???
            };
drawer.Draw(c);
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
1

Take a look at your Draw methods: do they actually change anything on any of the DrawTriangle, DrawRectangle, etc, classes?

If not, then you don't need to create a new instance every time you want to draw something. Instead, you can just store a single instance of DrawTriangle inside your Mapper dictionary:

private static Dictionary<Shapes, IDrawingObject> Mapper = new Dictionary<Shapes, IDrawingObject>()
{
    { Shapes.Circle, new DrawCircle() },
    { Shapes.Rectangle, new DrawRectangle() },
    { Shapes.Triangle, new DrawTriangle() },
};

Then you fetch the appropriate IDrawingObject for a given Shapes, and call its Draw method:

public static void Draw(Shapes s, Color c)
{
    if (Mapper.TryGetValue(s, out IDrawingObject drawingObject))
    {
        drawingObject.Draw(c);
    }
}

If for some reason you do need to create a new DrawTriangle very time you want to draw a triangle, you can instead put Func<IDrawingObject> delegates in your dictionary, but still call IDrawingObject.Draw in your static Draw method:

private static Dictionary<Shapes, Func<IDrawingObject>> Mapper = new Dictionary<Shapes, IDrawingObject>()
{
    { Shapes.Circle, () => new DrawCircle() },
    { Shapes.Rectangle, () => new DrawRectangle() },
    { Shapes.Triangle, () => new DrawTriangle() },
};

Then:

public static void Draw(Shapes s, Color c)
{
    if (Mapper.TryGetValue(s, out Func<IDrawingObject> drawingObjectFactory))
    {
        IDrawingObject drawingObject = drawingObjectFactory();
        drawingObject.Draw(c);
    }
}
canton7
  • 37,633
  • 3
  • 64
  • 77
0

This is virtually what you have!

you can make your nasty enum look like a "proper" object by using extension methods. (though its a mirage....exposed by having the exception in the MkShape method)

so the big objection to this is its not typesafe, if you add new enums and forget to update MkShape, your code will crash...a fucntional language like F#, Scala etc, would warn you that this was bad.

your dictionary does nothing except swap a bit of complexitiy for a tiny performance benefit of the switch statement...i.e. don't bother unless you've got hundreds of enum values, it may even cost you performance (for small n)

public enum Shapes
{
    Circle,
    Rectangle,
    Triangle
}

public interface IShape
{
    void Draw(Color c);
}

public static class Shape
{
    public static void ExampleClientCode()
    {
        var s = Shapes.Circle;
        // your enum looks like a "proper" object
        s.Draw(Color.AliceBlue);
    }

    public static IShape MkShape(this Shapes s)
    {
        switch (s)
        {
            case Shapes.Circle:
                return new Circle();
            case Shapes.Rectangle:
                return new Rectangle();
            case Shapes.Triangle:
                return new Triangle();
            default:
                throw new Exception("nasty enum means I can't have typesafe switch statement");
        }
    }

    public static void Draw(this Shapes s,Color c)
    {
        s.MkShape().Draw(c);
    }
}

public class Triangle : IShape
{
    public void Draw(Color c)
    {
        //for demo purpose
        Console.WriteLine("Drawing Triangle with color " + c.Name);

    }
}

public class Circle : IShape
{
    public void Draw(Color c)
    {
        //for demo purpose
        Console.WriteLine("Drawing Circle with color " + c.Name);
    }
}

public class Rectangle : IShape
{
    public void Draw(Color c)
    {
        //for demo purpose
        Console.WriteLine("Drawing Rectangle with color " + c.Name);
    }
}

P.S.

I suspect that it may be useful to store the colour in the shape, but if you are tied to the enum, then you're tied to just knowing the shape and nothing else

MrD at KookerellaLtd
  • 2,412
  • 1
  • 15
  • 17