开发者

Can generics improve this design?

开发者 https://www.devze.com 2023-03-25 12:08 出处:网络
The PartyRoleConstraints class in the model below (on the right) is the subject of this question. The idea is that when a client tries to associate a Party with a RoleType, the RoleType sees if it

The PartyRoleConstraints class in the model below (on the right) is the subject of this question.

Can generics improve this design?

The idea is that when a client tries to associate a Party with a RoleType, the RoleType sees if it has any Constraints that should prevent a given Party from being associated. A Party is a supertype for both Person and Organization.

Here is the totally generic interface I am after:

public interface IRoleConstraint<in T>
{
    Func<T, bool> IsSatisfied { get; }
    string UnsatisfiedDescription { get; }
    bool CanAddRole(T instance);
}

A common constraint would be by Type. So if I have role type of "husband" then I want to make sure the Party instance is a Person. Here is some implementation and a test case proving I can do this:

public class RoleConstraint<T> : IRoleConstraint<T>
{
    public RoleConstraint(Func<T, Boolean> isSatisfied, string unsatisfiedDescription) {
        if (isSatisfied == null) throw new ArgumentNullException("isSatisfied");
        if (unsatisfiedDescription == null) throw new ArgumentNullException("unsatisfiedDescription");

        IsSatisfied = isSatisfied;
        UnsatisfiedDescription = unsatisfiedDescription;
    }

    public Func<T,开发者_StackOverflow bool> IsSatisfied { get; protected set; }

    public string UnsatisfiedDescription { get; protected set; }

    public bool CanAddRole(T instance) { return IsSatisfied.Invoke(instance); }
}

public class PartyRoleConstraint : RoleConstraint<Party>
{
    public PartyRoleConstraint(Func<Party, bool> isSatisfied, string unsatisfiedDescription) : base(isSatisfied, unsatisfiedDescription) { }
}

public class PartyRoleConstrainedToType<TRequired> : PartyRoleConstraint where TRequired : Party
{
    private static readonly string _unsatisfiedDescription
        = string.Format("This role requires a Party instance to be a {0}", typeof(TRequired).Name);

    private static readonly Func<Party, bool> _isSatisfied = instance => instance.GetType().Equals(typeof(TRequired));

    public PartyRoleConstrainedToType() : base(_isSatisfied, _unsatisfiedDescription) { }
}

    [Test]
    public void Constraints_IfTypeConstraint_and_InstanceDoesNotMatch_False()
    {
        var sony = new Organization("Sony Corporation");
        var constraint = new PartyRoleConstrainedToType<Person>();
        _husbandRoleType.AddConstraint(constraint);
        Assert.That(_husbandRoleType.CanAddRole(sony), Is.False);
    }

The problem I am hitting is if I want to set up a rule based on an attribute of a subtype of Party. For example, I want the gender of the husband to be Male. I can do this with a cast, as:

    [Test]
    public void Constraints_IfConstraintConditionIsNotMet_False()
    {
        _husbandRoleType.AddConstraint(new PartyRoleConstrainedToType<Person>());
        Assert.That(_husbandRoleType.CanAddRole(_arthur), Is.True);

        //**** here is the cast **** //
        var mustBeMale = new PartyRoleConstraint(p => ((Person)p).Gender == Gender.Male, "the husband must be male.");  

        _husbandRoleType.AddConstraint(mustBeMale);
        Assert.That(_husbandRoleType.CanAddRole(_arthur), Is.False);
        _arthur.Gender = Gender.Male;
        Assert.That(_husbandRoleType.CanAddRole(_arthur), Is.True);
    }

The question (finally!) is: can I use generics to avoid that cast, and if so, how?


Yes, you can get rid of the cast, but you will have to specify "Person" somewhere. It's hard to propose a "best place" to do so, without knowing more about your requirements and constraints. One option would be something like:

var mustBeMale = PartyRoleConstraint.For<Person>( p => p.Gender == ...);

Another option would be to tweek PartyRoleConstrainedToType to allow further restrictions. And example might look like this:

var combinedConstraint = new PartyRoleConstrainedToType<Person>().MustSatisfy(p => p.Gender == ...);

As said before: You will have to specify Person somewhere, but there a different options to create a nice syntax. What nice means, depends on your requirements and use cases.

0

精彩评论

暂无评论...
验证码 换一张
取 消