I have a lot of font object declarations over 500+. I want to give them default scale (and other styles later) via derived class, I dont want to change juce::Font implementation. But i saw Font is final class, I cant derive it to make a Customized Font class.
The Font
class doesn’t have any virtual methods, it’s not really designed to be derived from. Inheritance is for polymorphism and I don’t think we need that here. Generally speaking I would strongly encourage composition over inheritance wherever possible.
Take the following example
#include <iostream>
#include <memory>
struct FontNoVirtual
{
FontNoVirtual() { std::cout << __FUNCTION__ << '\n'; }
~FontNoVirtual() { std::cout << __FUNCTION__ << '\n'; }
};
struct FontNoVirtualDerived : public FontNoVirtual
{
FontNoVirtualDerived() { std::cout << __FUNCTION__ << '\n'; }
~FontNoVirtualDerived() { std::cout << __FUNCTION__ << '\n'; }
};
struct FontVirtual
{
FontVirtual() { std::cout << __FUNCTION__ << '\n'; }
virtual ~FontVirtual() { std::cout << __FUNCTION__ << '\n'; }
};
struct FontVirtualDerived : public FontVirtual
{
FontVirtualDerived() { std::cout << __FUNCTION__ << '\n'; }
~FontVirtualDerived() override { std::cout << __FUNCTION__ << '\n'; }
};
int main()
{
{
std::unique_ptr<FontNoVirtual> font;
font = std::make_unique<FontNoVirtualDerived>();
}
std::cout << '\n';
{
std::unique_ptr<FontVirtual> font;
font = std::make_unique<FontVirtualDerived>();
}
return 0;
}
The output of this is
FontNoVirtual
FontNoVirtualDerived
~FontNoVirtual
FontVirtual
FontVirtualDerived
~FontVirtualDerived
~FontVirtual
Note that when the destructor is not marked virtual the derived destructor is not called when a pointer to the derived class is deleted!
By us marking Font final we’re able to prevent code from compiling that might make this mistake.
In this instance I think what I’ve normally seen is some free functions that return the Font with the style desired.
For example something like (untested)
static const Font& getMyMainApplicationFont()
{
static Font font { "Helvetica" }.withStyle (Font::bold | Font::underlined)
.withHeight (25);
return font;
}
You could have multiple of these functions, or you could add a parameter or two to the function to suit your needs. For example (again untested)…
enum class FontStyle
{
heading1,
heading2,
paragraph
}
template <FontStyle style>
static const Font& getApplicationFont();
static const Font& getApplicationFont<FontStyle::heading1>()
{
static Font font { "Helvetica" }.withStyle (Font::bold | Font::underlined)
.withHeight (50);
return font;
}
static const Font& getApplicationFont<FontStyle::heading2>()
{
static Font font { "Helvetica" }.withStyle (Font::bold)
.withHeight (30);
return font;
}
static const Font& getApplicationFont<FontStyle::paragraph>()
{
static Font font { "Helvetica" }.withHeight (18);
return font;
}
Hopefully that helps?
Thanks for the detailed reply,
You are right, composition is better, but I have 2 reason to derive it,
First one is, I just want to regex replace of class names of declarations instead of adding
.withStyle (Font::bold)
Second and my main reason is I have a layered project that has a GUI layer derives most of JUCE components to abstract JUCE api. So I can replace the JUCE api with other in the future easly. I thought having a general font customization class in my GUI layer could be better. But as you said and I just noticed juce::Font doesnt have any virtual. I’ll try your methods, thanks.