Is there a design pattern to solve this problem?

I’ve been struggling with a design so I figured I’d ask here and see if anyone’s able to help 🙂

High level overview

I’m designing an app to gamify exercise by creating mini competitions (ex. Who can lose the most weight in 3 months or bike the most miles in a month).

Users can create competitions (calling them races for now) and they need to be able to specify constraints – participants have to be in a certain age range, over a certain weight, maximum number of participants in a race, etc.

My Objects

RaceParticipant – for brevity, the only pertinent property is int Age.

Race:

public class Race
{
    public virtual int RaceId { get; set; }
    public virtual string RaceName { get; set; }
    public virtual int RaceTypeId { get; set; }
    public virtual TrainingType RaceType { get; set; }
    public virtual IEnumerable<RaceParticipant> RaceParticipants { get; set; }
    public virtual IEnumerable<RaceConstraint> RaceConstraints { get; set; }
}

RaceConstraint abstract class:

public abstract class RaceConstraint
{
    public string DisplayText { get; set; }
    public string ValidationText { get; set; }
    public virtual bool PassesConstraint(Race race, RaceParticipant participant); 
}

RaceConstraint implementation MaxNumberOfParticipants:

public class MaxNumberOfParticipants : RaceConstraint
{
    public int MaximumNumberOfParticipants { get; set; }

    public MaxNumberOfParticipants()
    {
        DisplayText = "Maximum number of racers";
        ValidationText = "This race has reached the maximum number of racers.";
    }

    public override bool PassesConstraint(Race race, RaceParticipant participant) 
    {
        bool result = false;
        if (race.RaceParticipants.Count() <= MaximumNumberOfParticipants)
        {
            result = true;
        }
        return result;
    }
}

RaceConstraint implementation AgeRange:

public class AgeRange : RaceConstraint
{
    public int MinimumAge { get; set; }
    public int MaximumAge { get; set; }

    public AgeRange()
    {
        DisplayText = "Age range of racers";
        ValidationText = string.Format("Racers must be between the ages of {0} and {1}.", MinimumAge, MaximumAge);
    }

    public override bool PassesConstraint(Race race, RaceParticipant participant)
    {
        bool result = false;
        if (MinimumAge <= participant.Age && participant.Age <= MaximumAge)
        {
            result = true;
        }
        return result;
    }
}

The question

My question is where to store MinimumAge, MaximumAge, and MaximumNumberOfParticipants. I’m using Entity Framework code-first so I know I don’t need to worry about the schema directly, but I do need to think about which properties to add to EF and which object they’ll be connected to.

Currently EF would make a separate table for each implementation of RaceConstraint which seems like overkill and I definitely don’t want DisplayText and ValidationText stored in the db for each instance.

I thought about moving MinimumAge, MaximumAge, and MaximumNumberOfParticipants to properties of Race because each race will have values for these, but that feels like Race and RaceConstraint would be too coupled. I’d like to be able to add a RaceConstraint in the future as requirements change without having to add a property to Race and therefor add a column to the Race db table.

I think I’m going to have to make a container object for RaceConstraint that holds a collection of all possible RaceConstraints. Would it make sense to put these properties in that object and then remove IEnumerable RaceConstraints from Race and instead add a property for the RaceConstraintContainer?

Or maybe I’m way over thinking this and there’s a design pattern I can follow. I’m sure I’m not the first person to come across this scenario!

Thanks in advance.

UPDATE

After mulling this over for a few days and trying out each of the answers, I think @maple_shaft really nailed the problem of EF getting in the way of OO design. I think I found a compromise somewhere in the middle. I added properties like MinimumAge, MaximumAge, and MaximumNumberOfParticipants to the Race object because from a database perspective, these are all very related and each race will have entries for each of these.

I changed the abstract class RaceConstraint to an interface that just requires bool PassesConstraint(). I made better use of DisplayText by making it a data annotation of the properties in race (ex. [Display(Name = “Maximum number of racers”)]) since I really only wanted it for labels in the UI. I moved the text from ValidationText to an exception message if a PassesConstraint() fails.

Finally, I made a RaceManager class to hold a List of RaceConstraints and handle adding users by checking each RaceConstraint. This class will also handle removing users, determining winners, etc.

I think this will work well from an MVC standpoint too because I’m not passing models to the view that have methods. The Race class will really just be there to hold EF data and will make for a clean model to use for adding or updating races from a view.

Thanks for the help everyone!

1

You are running into a limitation of Entity Framework that encourages what each type of object in your domain model to map directly to a single schema construct. Creating sub types of RaceConstraint sounds like a good idea from an OO perspective but then the Anemic Domain Model is a more encouraged pattern using this framework anyway.

RaceConstraint should be the entity type and all the different possible constraint properties should exist in this one entity. Display and Validation text are not well represented here, and may be more appropriate as references to an enum or a resource file as each individual constraint property in your RaceConstraint object may have a unique message.

To maintain the OO design however, you can translate your domain model to a view model that is not Anemic, however some would consider it bad practice to have business logic like PassesConstraint in your View Model or your Domain Model as well. These are the two schools of thought on this.

5

It looks like you need to separate the Constraint metadata (DisplayText, ValidationText) from the values for each instance (MinimumAge, MaximumAge, etc.)

If the Display/Validation text isn’t going to change unless you’re making code updates anyway, I’d just declare them const and load them from a resource file.

MinimumAge, MaximumAge, age MaximumNumberOfParticipants should stay on the object where they have the most meaning and the least duplication. It looks like the RaceConstraint subclasses are a good place.

FWIW, you are already using something very similar to the Specification pattern with the PassesConstraint method.

I would implement a RangeConstraint with an indicator of the type if you’re concerned about the database layout, this will give you one table for range constraints and an identifier (1=Age, 2=Weight, 3=IQ, etc), then the min and max is all you need otherwise. THen the PassesConstraint bit should just use a selector from a dictionary somewhere Dictionary<RangeConstraintType, Func<RaceParticipant, int>> rangeSelectors where you give it the constraint type, it returns a function that given a participant will give back the value to be validated against the range. Something like:

var _rangeSelectors = new Dictionary<RangeConstraintType, Func<RaceParticipant, int>>() {
    { RangeConstraintType.Age, (participant) => participant.Age },
    { RangeConstraintType.Weight, (participant) => participant.Weight },
    ...
};

Then in your PassesConstraint it just..

public override bool PassesConstraint(Race race, RaceParticipant participant)
{
    var participantsMetric = _rangeSelectors[_rangeConstraintType](participant);
    return participantsMetric < maxValue && patricipantsMetric > minValue;
}

2

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