I need to design a class hierarchy for my C# project. Basically, class\' functio
ID: 642830 • Letter: I
Question
I need to design a class hierarchy for my C# project. Basically, class' functionalities are similar to WinForms classes so let's take WinForms toolkit as an example. (However, I can't use WinForms or WPF.)
There are some core properties and functionalities that every class needs to provide. Dimensions, position, color, visibility (true/false), Draw method, etc.
I need design advice I have used a design with an abstract base class and interfaces that are not really types but more like behaviors. Is this a good design? If not, what would be a better design.
The code looks like this:
abstract class Control
{
public int Width { get; set; }
public int Height { get; set; }
public int BackColor { get; set; }
public int X { get; set; }
public int Y { get; set; }
public int BorderWidth { get; set; }
public int BorderColor { get; set; }
public bool Visible { get; set; }
public Rectangle ClipRectange { get; protected set; }
abstract public void Draw();
}
Some Controls can contain other Controls, some can be only contained (as children) so I'm thinking of making two interfaces for these functionalities:
interface IChild
{
IContainer Parent { get; set; }
}
internal interface IContainer
{
void AddChild<T>(T child) where T : IChild;
void RemoveChild<T>(T child) where T : IChild;
IChild GetChild(int index);
}
WinForms controls display text so this also goes into interface:
interface ITextHolder
{
string Text { get; set; }
int TextPositionX { get; set; }
int TextPositionY { get; set; }
int TextWidth { get; }
int TextHeight { get; }
void DrawText();
}
Some Controls can be docked inside their parent Control so:
enum Docking
{
None, Left, Right, Top, Bottom, Fill
}
interface IDockable
{
Docking Dock { get; set; }
}
...and now let's create some concrete classes:
class Panel : Control, IDockable, IContainer, IChild {}
class Label : Control, IDockable, IChild, ITextHolder {}
class Button : Control, IChild, ITextHolder, IDockable {}
class Window : Control, IContainer, IDockable {}
The first "problem" I can think of here is that the interfaces are basically set in stone once they get published. But let's assume I will be able to make my interfaces good enough to avoid need of making changes to them in the future.
Another issue I see in this design is that every of these classes would need to implement it's interfaces and duplication of code will quickly occur. For example in Label and Button the DrawText() method derives from the ITextHolder interface or in every class derived from IContainer management of children.
My solution for this issue is to implement this "duplicated" functionalities in dedicated adapters and forward calls to them. So both Label and Button would have a TextHolderAdapter member that would be called inside methods inherited from the ITextHolder interface.
I think this design should shield me from having to many common functionalities in the base class which could quickly get bloated with virtual methods and unnecessary "noise code". Changes of behavior would be accomplished by extending adapters and not Control-derived classes.
I think this is called the "Strategy" pattern and although there are millions of questions and answers on that topic, I would like to ask you for your opinions as to what I take into consideration for this design and what flaws you can think of in my approach.
I should add that there is an almost 100% chance that future requirements will call for new classes and new functionalities.
Explanation / Answer
[1] Add virtual "getters" & "setters" to your properties, I had to hack another control library, because I need this feature:
abstract class Control
{
// internal fields for properties
protected int _Width;
protected int _Height;
protected int _BackColor;
protected int _X;
protected int _Y;
protected bool Visible;
protected int BorderWidth;
protected int BorderColor;
// getters & setters virtual !!!
public virtual int getWidth(...) { ... }
public virtual void setWidth(...) { ... }
public virtual int getHeight(...) { ... }
public virtual void setHeight(...) { ... }
public virtual int getBackColor(...) { ... }
public virtual void setBackColor(...) { ... }
public virtual int getX(...) { ... }
public virtual void setX(...) { ... }
public virtual int getY(...) { ... }
public virtual void setY(...) { ... }
public virtual int getBorderWidth(...) { ... }
public virtual void setBorderWidth(...) { ... }
public virtual int getBorderColor(...) { ... }
public virtual void setBorderColor(...) { ... }
public virtual bool getVisible(...) { ... }
public virtual void setVisible(...) { ... }
// properties WITH virtual getters & setters
public int Width { get getWidth(); set setWidth(value); }
public int Height { get getHeight(); set setHeight(value); }
public int BackColor { get getBackColor(); set setBackColor(value); }
public int X { get getX(); set setX(value); }
public int Y { get getY(); set setY(value); }
public int BorderWidth { get getBorderWidth(); set setBorderWidth(value); }
public int BorderColor { get getBorderColor(); set setBorderColor(value); }
public bool Visible { get getVisible(); set setVisible(value); }
// other methods
public Rectangle ClipRectange { get; protected set; }
abstract public void Draw();
} // class Control
/* concrete */ class MyControl: Control
{
public override bool getVisible(...) { ... }
public override void setVisible(...) { ... }
} // class MyControl: Control
I know this suggestion is more "verbose" or complex, but, its very useful in real world.
[2] Add an "IsEnabled" property, do not confuse with "IsReadOnly":
abstract class Control
{
// internal fields for properties
protected bool _IsEnabled;
public virtual bool getIsEnabled(...) { ... }
public virtual void setIsEnabled(...) { ... }
public bool IsEnabled{ get getIsEnabled(); set setIsEnabled(value); }
} // class Control
Means you may have to show your control, but don't show any information.
[3] Add a "IsReadOnly" property, do not confuse with "IsEnabled":
abstract class Control
{
// internal fields for properties
protected bool _IsReadOnly;
public virtual bool getIsReadOnly(...) { ... }
public virtual void setIsReadOnly(...) { ... }
public bool IsReadOnly{ get getIsReadOnly(); set setIsReadOnly(value); }
} // class Control
That means the control can display information, but cannot be changed by the user.
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.