Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle composed classes in C#

What I am asking is partly related to design pattern.

Lets say I have an IDrawing interface. Two other basic classes named TextDrawing and ShapeDrawing implement this, and I have a View class that knows how to draw these!

But I have more complex drawing classes that also implement IDrawing interface but are composed of several IDrawing classes themselves!

How can I draw these in my View class? Obviously teaching the View class to draw each new IDrawing is not a good idea! But what other choices I have? Maybe the design is not correct? How can I tell the View's Draw method to know the primitive parts of the complex classes and draw them?

   public interface IDrawing
   {
   }

   public class TextDrawing : IDrawing
   {
   }

   public class ShapeDrawing : IDrawing
   {      
   }

   public class SignDrawing : IDrawing
   {
      public TextDrawing Text { get; set; }
      public ShapeDrawing Border { get; set; }
   }

   public class MoreComplexDrawing : IDrawing
   {
      public TextDrawing Text { get; set; }
      public ShapeDrawing Border1 { get; set; }
      public ShapeDrawing Border2 { get; set; }
   }

   public class View
   {
      public void Draw(IDrawing drawing)
      {
           // The View only knows how to draw TextDrawing and ShapeDrawing.
           // These as the primitive building blocks of all drawings.
           // How can it draw the more complex ones!
           if (drawing is TextDrawing)
           {
              // draw it
           }
           else if (drawing is ShapeDrawing)
           {
              // draw it
           }
           else
           {
              // extract the drawings primitive parts (TextDrawing and ShapeDrawing) and draw them!
           }
      }
   }

Update:

I received suggestions to implement the Draw() method in my drawing classes. The Draw method in View relies on an external library to draw (in my case, it is SkiaSharp library). If I implement the Draw in these classes they will not be general anymore! For example I will not be able to use them in other projects where I have a different strategy to draw the things.

like image 973
Vahid Avatar asked Aug 23 '17 15:08

Vahid


People also ask

How do I write a class in C?

C isn't an OOP language, as your rightly point out, so there's no built-in way to write a true class. You're best bet is to look at structs, and function pointers, these will let you build an approximation of a class. However, as C is procedural you might want to consider writing more C-like code (i.e. without trying to use classes).

What are the classes and objects in C++?

C++ Classes and Objects. Class: The building block of C++ that leads to Object Oriented programming is a Class. It is a user defined data type, which holds its own data members and member functions, which can be accessed and used by creating an instance of that class. A class is like a blueprint for an object.

What are compositions in C++?

Compositions are one of the easiest relationship types to implement in C++. They are typically created as structs or classes with normal data members. Because these data members exist directly as part of the struct/class, their lifetimes are bound to that of the class instance itself.

How are base classes represented in C++?

A base class must be represented as a member variable with the same name and type as the base class itself. A subclass may override the base class instance method pointers to provide polymorphism.


3 Answers

I would add a Draw() method to the interface and implement that in each of your classes.

The benefit of this is that your View doesn't care what the actual type is.

public interface IDrawing
{
    void Draw();
}

public class TextDrawing : IDrawing
{
    public void Draw()
    {
        // Draw a TextDrawing
    }
}

public class ShapeDrawing : IDrawing
{      
    public void Draw()
    {
        // Draw a ShapeDrawing
    }
}

public class SignDrawing : IDrawing
{
    public TextDrawing Text { get; set; }
    public ShapeDrawing Border { get; set; }

    public void Draw()
    {
        // Draw a SignDrawing
    }
}

public class MoreComplexDrawing : IDrawing
{
    public TextDrawing Text { get; set; }
    public ShapeDrawing Border1 { get; set; }
    public ShapeDrawing Border2 { get; set; }

    public void Draw()
    {
        // Draw a MoreComplexDrawing
    }
}

public class View
{
    public void Draw(IDrawing drawing)
    {
        //Draw the drawing
        drawing.Draw();
    }
}

UPDATE - Abstract away the dependency on SkiaSharp

You'll need to create a wrapper for SkiaSharp or whichever external dependency that will actually do the drawing. This should exist in the same assembly as your IDrawing interface and derived classes.

public interface IDrawingContext
{
    // Lots of drawing methods that will facilitate the drawing of your `IDrawing`s
    void DrawText(...);
    void DrawShape(...);
    void DrawBorder(...);
}

As well as the SkiaSharp specific implementation

public class SkiaSharpDrawingContext IDrawingContext
{
    // Lots of drawing methods that will facilitate the drawing of your IDrawings
    public void DrawText(...) { /* Drawing code here */ }
    public void DrawShape(...) { /* Drawing code here */ }
    public void DrawBorder(...) { /* Drawing code here */ }
}

Update the IDrawing interface to

public interface IDrawing
{
    void Draw(IDrawingContext drawingContext);
}

Updating your classes too to reflect this change. Your classes will call methods on the IDrawingContext implementation to do the drawing.

Create a dependency specific implementation within your application(s) and update your View class to use the new SkiaSharpDrawingContext

public class View
{
    public void Draw(IDrawing drawing)
    {
        // This should ideally be injected using an IOC framework
        var drawingContext = new SkiaSharpDrawingContext(...);

        //Draw the drawing
        drawing.Draw(drawingContext);
    }
}
like image 87
phuzi Avatar answered Nov 15 '22 02:11

phuzi


You have the choice to force the classes to implement a method called Draw().

public interface IDrawing
{
    void Draw(); 
}

now you can simply call this method and give the responsibility of the inner implenmentation to the classes on their own.

public void Draw(IDrawing drawing)
{

     drawing.Draw();

For Example TextDrawing would be forced to implement this method

public class TextDrawing : IDrawing
{
    public void Draw()
    {
        // draw Text-like
    }
}

In your View class the call to drawing.Draw(); will result in the correct implementation.

How can I tell the View's Draw method to know the primitive parts of the complex classes and draw them?

Now in the complex classes you would simply use the methods that are already implemented in the simple drawing classes

public class SignDrawing : IDrawing
{
    public TextDrawing Text { get; set; }
    public ShapeDrawing Border { get; set; }

    public void Draw()
    {
        Text.Draw();

        Border.Draw();

        Text.Draw();
    }
}
like image 31
Mong Zhu Avatar answered Nov 15 '22 03:11

Mong Zhu


you can polymorphically draw without checking the type.

 public class View
   {
      public void Draw(IDrawing drawing)
      {
         drawing.Draw();
      }
   }
Encapsulate each drawing strategy (Strategy Pattern) in different class and you could inject each strategy via DI.
like image 41
aspxsushil Avatar answered Nov 15 '22 04:11

aspxsushil