C. Dodds
C. Dodds

Reputation: 345

Making a class that will contain a subclass that can be different classes depending on the need

I'm unsure how to go about implementing this particular idea of I have a class lets call it EnhancedUserInput that will have some variables that all of the input types will have and a particular subclass depending on the need during operation so some extra variables and a list so for example sub classes of it would be MultipleChoice which would have MinSelection, MaxSelection and a list of a type called option with their own variables ect and then another possible sub class called ExplicitAgreement which would have the variables inputLabel1, inputLabel2 and a list of type BinaryInput which would have their own variables.

So far from what I understand the best way going about this would be to have some type of generic variable? I'll show some code to try and help get what it is I need across but was just wondering is there an easy way of doing this that I am unaware of?

public class EnhancedCustomerInput
{
    public string Title { get; set;}

    public bool ResponseOptional { get; set;}

    public string CancelLabel { get; set;}

    public string SubmitLabel { get; set}

    // this is where I am unsure of how to go about it
    public object inputType
    {
        MultipleChoice
        ExplicitAgreement
    }
}

public class MultipleChoice
{
    public List<MultipleChoiceOption> Options { get; set; }

    public int MinSelected { get; set; }

    public int MaxSelected { get; set; }

}

public class ExplicitAgreement
{
    public List<BinaryInputOption> Buttons { get; set; }

    public string InputLabel1 { get; set; }

    public string InputLabel2 { get; set; }
}

what would be the best path for this solution I can think of some possible ways but they would be a bit figity and was wondering if there are any simple ways?

Upvotes: 0

Views: 1212

Answers (2)

StriplingWarrior
StriplingWarrior

Reputation: 156469

Steve Harris's inheritance suggestion is good. Your original option to use Composition can work just fine, too:

public class EnhancedCustomerInput
{
    public string Title { get; set;}

    public bool ResponseOptional { get; set;}

    public string CancelLabel { get; set;}

    public string SubmitLabel { get; set; }

    public object InputData { get; set; }
}

The only problem is that consumers of your code need to know that InputData can be one of several different types, and you presumably need logic to switch on their type. You can add comments to the property to give people a hint, or you can use a library like LanguageExt, which provides an Either type:

public class EnhancedCustomerInput
{
    public string Title { get; set;}

    public bool ResponseOptional { get; set;}

    public string CancelLabel { get; set;}

    public string SubmitLabel { get; set; }

    public Either<MultipleChoice, ExplicitAgreement> InputData { get; set; }
}

This makes it much more obvious which types InputData can be, but would get very unwieldy if you have more than two possibilities.

You could also declare an interface that InputData must implement, which would make it easier for developers to find all the types that are intended to be used there. But an empty interface is considered to be a code smell because it indicates you're using interfaces for something they weren't really intended for.

Another option I've found to work well is to define an enum type to help identify which different types of input data you can have:

public class EnhancedCustomerInput
{
    public string Title { get; set;}

    public bool ResponseOptional { get; set;}

    public string CancelLabel { get; set;}

    public string SubmitLabel { get; set; }

    public InputType InputType { get; set; }

    public object InputData { get; set; }
}

public enum InputType { MultipleChoice, ExplicitAgreement }

This gives your business logic a specific set of possible types that you can switch your logic on, and works particularly well when the class is going to be serialized and deserialized, because then you can tell the deserializer which specific type of object to deserialize InputData to.

There are lots of options, each with their advantages and disadvantages.

Upvotes: 2

Steve Harris
Steve Harris

Reputation: 5109

Seems to me that you may have this the wrong way around. Maybe what you want is to just use class inheritance?

public class EnhancedCustomerInput
{
    public string Title { get; set;}
    public bool ResponseOptional { get; set;}
    public string CancelLabel { get; set;}
    public string SubmitLabel { get; set}
}

public class MultipleChoice : EnhancedCustomerInput
{
    public List<MultipleChoiceOption> Options { get; set; }
    public int MinSelected { get; set; }
    public int MaxSelected { get; set; }
}

public class ExplicitAgreement : EnhancedCustomerInput
{
    public List<BinaryInputOption> Buttons { get; set; }
    public string InputLabel1 { get; set; }
    public string InputLabel2 { get; set; }
}

Upvotes: 4

Related Questions