Currently, I’m working on a system that enables users to add Tag’s to available TagTypes on specific pages. For instance, we would have a TagType called “Installer” and the user wants his name associated with it (his name then would be the Tag).
A requirement of this system is that administrators can add constraints to TagTypes. Once a Tag is created on an existing TagType, the containing constraint checks the Tag’s value.
Our wish is to make it as easy as possible to implement new types of constraints in the future. For instance, we would have a RegularExpression-constraint, a List-constraint (the value of a Tag should be one of the values in a list), an int-range constraint, etcetera. What would be the best way of facilitating this need? Not only does the constraint need checking once a Tag has been created (or edited), also, the back-end for administrators creating new constraints needs to be adjustable in an easy manner.
We got to this point by implementing something bad:
class TagTypeConstraint{}
class TTConstraintList : TagTypeConstraint{}
class TTConstraintIntRange : TagTypeConstraint{}
class TTConstraintRegex : TagTypeConstraint{}
This already has the problem, besides the awful maintainability, that abstract methods in TagTypeConstraint are becoming useless as the inheriting classes want other information passed to it (one wants a string, the other one wants two ints, the last one wants a string again). Maybe a future constraint will want a list of integers. I’m thinking there needs to be a good level of abstraction for our back-end to be easily extendable but how to achieve that when methods of inheriting classes will be asking for different parameters?
Edit:
I’ve made some progressions on how to tackle.
My TagTypeConstraint class is now abstract, having abstract methods like + SetConstraint(object[] values)
and + IsValid(object[] values)
.
These abstract methods get implemented by the inheriting classes and in those classes, the object[] variables are cast to appropriate types. If the casts fail, I will throw a ConstraintException.
Also, I’ve added a static function to TagTypeConstraint that acts like a (simple) factory, removing the need to call the implementations of TagTypeConstraint in layers like the GUI.
Any comments on this would be appreciated.
Current (updated) structure:
Tag
- string _content;
- TagType _tagType;
TagType
- string _name;
- TagTypeDataType _dataType;
TagTypeDataType
- string _name;
- Constraint _constraint;
abstract Constraint
- static Constraint CreateConstraint(ConstraintType type)
- abstract void SetConstraint(object[] values)
- abstract bool IsValid(object[] values)
- abstract ConstraintType GetConstraintType()
StringListConstraint
- List _constraintValues;
- ConstraintType _constraintType;
- override void SetConstraint(object[] values)
- override bool IsValid(object[] values)
- override ConstraintType GetConstraintType()
6