1
\$\begingroup\$

Working from a C# and EntityFramework background. Jimmy Bogard makes the point of modelling your domain model correctly, such that an Email Address is not a simple string but should be of type EmailAddress.

Also known as Primitive Obsession.

I've come to this part of a piece of software where a Application form is to be filled out.

For the user a Yes/No question where they can answer Yes or No is a very easy thing, you can even create really go user experiences around yes/no.

So I have quickly thought of two ways to handle Yes/No.

A complex object:

public class YesNoAnswer
{
    public YesNoAnswer(string state) 
    {
        this.SetState(state);
    }

    public bool Yes { get; private set; }

    public void SetState(string state)
    {
        var uppedState = state.ToUpper();

        if (uppedState == "YES") 
        {
            this.Yes = true;
            return;
        }
        if (uppedState == "NO") 
        {
            this.Yes = false;
            return;
        }

        throw new ArgumentOutOfRangeException(nameof(state), "Yes or No strings not found in argument");
    }
}

Or a simple extension method:

public static class StringExtensions
{
    public static bool GetYesNoState(this string state) 
    {
        var uppedState = state.ToUpper();
    
        if (uppedState == "YES") 
        {
            return true;
        }
        if (uppedState == "NO") 
        {
            return false;
        }
    
        throw new ArgumentOutOfRangeException(nameof(state), "Yes or No strings not found in argument");
    }
}

Obviously the validation should be done earlier in the pipeline (or at least it is in our application).

Which is better? They both achieve the same thing, the first is much clearer, neater. The second is well, slightly obfuscated - not a great deal but takes more than a second to think about what it is doing.

Usage of both

var application = new ApplicationForm { IsOkayToWork = new YesNoAnswer(state) };

or

var application = new ApplicationForm { IsOkayToWork = state.GetYesNoState() };
\$\endgroup\$
9
  • 1
    \$\begingroup\$ Why is this mutable? \$\endgroup\$ Commented Feb 22, 2016 at 15:20
  • \$\begingroup\$ This code doesn't even compile. YesNoAnswer has no property/field called No and string doesn't have either Yes or No. \$\endgroup\$ Commented Feb 22, 2016 at 15:26
  • \$\begingroup\$ it's more of a concept than actual working code, I only wrote that in the edit box. Why shouldn't the class be able to be changed? Are you saying just keep assigning a new value object to IsOkayToWork everytime you want assign a new value? \$\endgroup\$ Commented Feb 22, 2016 at 16:05
  • \$\begingroup\$ updated to compile now. \$\endgroup\$ Commented Feb 22, 2016 at 16:07
  • \$\begingroup\$ 1) NO should return false 2) I don't think you're allowed to edit code like that. \$\endgroup\$ Commented Feb 22, 2016 at 16:17

1 Answer 1

4
\$\begingroup\$

The idea of creating a Value Object instead of using primitives is to have a highly descriptive domain. Using the second option (the extension method) misses the point. You definitely want to be using the first.

e.g.

public class QuestionAnswer
{
    public int QuestionId { get; set; }

    public YesNoAnswer YesNoValue { get; set; }
}

Is more descriptive and specific than:

public class QuestionAnswer
{
    public int QuestionId { get; set; }

    public bool YesNoValue { get; set; }
}

or

public class QuestionAnswer
{
    public int QuestionId { get; set; }

    public string YesNoValue { get; set; }
}

One thing I would add is that you probably want a conversion to bool:

public class YesNoAnswer
{
    // omitted

    public static implicit operator bool(YesNoAnswer answer)
    {
        return (answer?.Yes).GetValueOrDefault();
    }

}

Then you can use it naturally in code:

var someAnswer = new QuestionAnswer { QuestionId = 1, YesNoValue = new YesNoAnswer("YES") }:

if (someAnswer.YesNoValue)
{

}

You should also override Equals and GetHashCode as Value Objects should have value equality not reference equality.

\$\endgroup\$
2
  • \$\begingroup\$ Yeah that is what I was leaning towards. What property type did you have holding the yes/no value in YesNoAnswer? Was it a string of the response? I like the bool conversion, that is a nice touch \$\endgroup\$ Commented Feb 22, 2016 at 13:12
  • \$\begingroup\$ @CallumLinington You can have whichever you like. You don't even really need a property if you only consider it as a bool in consuming code. \$\endgroup\$
    – RobH
    Commented Feb 22, 2016 at 13:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.