How do I design a drawing program with the separation of concerns?

2

Suppose you need to design a drawing program that relies on a geometric kernel library.

Inside the geometric kernel library, you have the definition of Line, Circle, Ellipse classes with all the geometrical data (position, radius) and functions (area, perimeter, tangentAt).

The actual program should extend these geometric kernel classes to add drawing functions but you don’t want to have DrawableLine or DrawableCircle classes on the UI side: entities classes must be unique.

The final goal is to have a single collection of entities in the geometric kernel, that can also be used in the drawing application to do the actual drawing.

I am using C# so I cannot use polymorphism.

  • The first idea I had is to add a null field to the geometric entity base class (something like object Renderer) that would be populated and used in the drawing application to do the actual drawing (at the cost to cast to proper types it all the times).

What other options do I have?

8

1

Approach #1 (in C# / .Net): Extension Methods

Those are exactly made for this – extending entities of a base library inside a depending library, where the original entities cannot be changed where they “live”.

Of course, this only works when all the drawing operations can be implemented in terms of public attributes and public methods of the geometric classes. But for this kind of use case, this is typically the case or can easily be arranged.

Approach #2: Generic drawing functions inside the geometric entities

Such functions would get some abstract graphics context or canvas object and let the entity draw itself on that canvas. The canvas interface must be part of the kernel, but the concrete canvas implementation would be outside of the kernel, hence the kernel does not become dependend on the specific UI components.

This approach makes most sense when the graphics context interface can provide some basic drawing operations which can be used for all of the geometric objects, maybe a “DrawLineSegment” or “DrawPolylines”, which is universally used for drawing a line, circle or ellipse.

Of course, nothing hinders you to combine both approaches: generic drawing functions working on an abstract canvas can be implemented as extension methods outside the kernel. This keeps the “draw-to-canvas” logic fully away from the kernel, it can be implemented, for example, in some intermediate layer or directly in the application layer.

2

3

It seems very challenging to design one object to address two different concerns (abstract geometry, and concrete drawing) and at the same time achieve separation of concerns.

One technique sc technique is composition: you may inject some drawing capabilities into a geometric object or some geometric capabilities in a drawable. On both cas you would polute one concern with the other, unless the injected object knows about the shape of the other. Another sc technique is inheritance. But this will inevitably lead to drawableCircle and circle.

Two designs manage well separation of concerns:

  • the drawableShape is composed with a Shape. This allows by the way to have several drawableCircle instances for the same geometricCircle instance, and thus works well under MVC and similar architecture.
  • using a bridge pattern that lets an abstract shape be composed with a drawable api implementation, leaving both evolve independently. This allows to create shapes that can draw themselves independently of the graphical API that implements the drawing. In your case it seems however to be oberengineering the problem.

0

At risk of confusing everyone, let’s forget about OOP for a second and talk about the English language. There are two types of objects in English: direct objects and indirect objects. If I give you a book, the book is the direct object and you are the indirect object. If I draw a circle on a wall then the circle is the direct object and the wall is the indirect object.

Seems to me the kernel library is providing the direct objects while the application itself contains the indirect object, which might generically be called a “canvas.” So you can have an interface like this:

class Canvas
{
    protected readonly Control _control;

    public Canvas(Control controlToReceiveDrawings)
    {
        _control = controlToReceiveDrawings;
    }

    public void Draw(Circle circle)
    {
        using var gr = _control.CreateGraphics();
        gr.DrawEllipse
        (
            pen: new Pen ( circle.Color ),
            x: circle.X,
            t: circle.Y,
            width: circle.Radius * 2,
            height: circle.Radius * 2
        );
    }

    public void Draw(Line line)
    {
        //etc....
    }
}

The canvas class is the container for anything that supports rendering (e.g. a reference to a WinForms control, as in this example, although it could also be some other sort of renderer or interface reference) and is its sole responsibility it to act as a bridge between your primitives and the rendering device.

This way you avoid having to extend or contain your primitive classes while still providing a cohesive library.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật