-1

I am coding a visualizer of graph algorithms. Each vertex of the graph has a style (color, size etc.). As long as the algorithm has done nothing to a vertex, that vertex has a default style. The default style needs to be configurable at run-time.

I came up with the following solution based on the singleton pattern, which seems a bit awkward to me (as indicated in the comment). Is there a better one?

struct Style {
    int color;
    // size etc. are skipped for brevity
};

// Changeable default style
struct DefaultStyle {
    static DefaultStyle &instance() {
        static DefaultStyle res;
        return res;
    }
    DefaultStyle() = default;
    DefaultStyle(DefaultStyle const &) = delete;
    void operator=(DefaultStyle const &) = delete;

    Style s{5}; 
        // didn't see a benefit in making it private, 
        // since I would need to provide get() and set() anyways. 
};

int main()
{
    auto &s = DefaultStyle::instance().s; 
       // this need to access the style indirectly (.s) seems awkward

    s.color = 10;

    std::cout << DefaultStyle::instance().s.color << std::endl;
       // the change is globally visible
    return 0;
}
  • 1
    The question is not really specific to the graphs domain, so I think that the [graph] tag should be removed. – AlwaysLearning Mar 4 '16 at 11:29
  • 1
    Sound like you want to use EventListeners.. – Simon Kraemer Mar 4 '16 at 14:26
  • 1
    Did you consider using graphviz instead of making your own thing from scratch? And you need to define for which output (which graphics library - perhaps Qt or SFML or Cairo, which file format - PDF, SVG, ....) you want to get the graphics. – Basile Starynkevitch Jul 5 '16 at 5:13
  • @BasileStarynkevitch I am using Cairo, no file output, only drawing on screen. – AlwaysLearning Jul 5 '16 at 8:34
  • 1
    Your question seems to be how to manage the styles used by vertices. However, you don't show how you relate vertices to their style. Your example is meaningless. – D Drmmr May 1 '17 at 11:54
0

What's wrong here ?

First, the purpose of a singleton is to ensure that a class has only one instance and to provide a global point of access to it:

  • you indeed would benefit from a global point of access to the default style for initializing the style of new vertexes.
  • but is there any need to have only one instance of it ? E.g. couldn't you imagine your application having several windows with different diagrams, each having a different default style ?

Next, is the "default style" really a type/subclass ? Isn't it rather a default value for the properties ?

  • Imagine that you would have style classes such as Thick, Dotted, Blinking. All these classes would have some common properties like the color and brush_style.
  • Now Blinking would need additional information about the duration of a blink. In this case, it would not be sufficient to have a single default style class : you'd need as many default classes ad derived styles !

How to address this design issue

In my opinion, there are two main approaches:

  • First option: If you use some factory method to create your shapes and vertexes, just setup the factory when it is created with the default style (managed by value or by reference).
  • Second option: As you want to use an object to create new ones by copying the prototype (i.e. default style), you could also think of a prototype design pattern

The two are not incompatible: if your styles could not be trivially copied (for instance, if they have some virtual functions), or if they could have different sizes, your first option would need the second to initialize the vertex style.

0

Maybe something like this will be nicer to use. The whole thing could be done by an assignable global variable, but I think it's still better to have explicit interface for it.

struct Style {
    int color;
    // size etc. are skipped for brevity
};

// Changeable default style
struct DefaultStyle {
    static const Style &get() {
        return style();
    }
    static void set(Style s) {
        style() = s;
    }
    DefaultStyle(DefaultStyle const &) = delete;
    void operator=(DefaultStyle const &) = delete;
    DefaultStyle() = delete;
private:
    static Style& style() {
        static Style s{5};
        return s;
    }
};

int main()
{
    DefaultStyle::set({10});

    std::cout << DefaultStyle::get().color << std::endl;

    return 0;
}
-2

I missed a very easy solution: use inheritance! After all, the default style is a style!

struct Style {
    int color;
    // width etc. are skipped for brevity
};

// Changeable default style
struct DefaultStyle: Style {
    static DefaultStyle &instance() {
        static DefaultStyle res;
        return res;
    }
    DefaultStyle(DefaultStyle const &) = delete;
    void operator=(DefaultStyle const &) = delete;
private:
    DefaultStyle() = default;
};

int main()
{
    auto &d = DefaultStyle::instance();
    d.color = 10;
    std::cout << DefaultStyle::instance().color << std::endl;
    return 0;
}
  • 1
    -1 for the LSP violation: Style can be assigned to, DefaultStyle cannot. – Sjoerd Jul 4 '16 at 17:40
  • @Sjoerd 1. I am bad with abbreviations. What does LSP stand for? 2. As far as I recall, the code compiles and runs. Why do you say (at least that's what I understood you were saying) that d.color = 10 is illegal? – AlwaysLearning Jul 4 '16 at 21:23
  • 2
    LSP = Liskov Substitution Principle. Look it up, it's an important principle for inheritance. It says that in any context where a Style can be used, it should be possible to replace it with a DefaultStyle. If that is not possible, (public) inheritance is not the right tool. – Sjoerd Jul 5 '16 at 0:42
  • @Sjoerd Do you hold that delete should never be used in a derived class? – AlwaysLearning Jul 5 '16 at 4:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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