Exposing capabilities with a collection of enum values or with boolean functions

High-level explanation

I have an object with some methods:

public class Foo
    public void Bar() { }
    public void Baz() { }

These methods cannot be executed unconditionally, there is some validation to be done. I’d also like to expose these conditions to a client (user) in some way.

I could do this with a collection of enum values:

public enum FooAction

public class Foo
    public void Bar() { if (GetAvailableActions().Contains(FooAction.Bar)) }
    public void Baz() { if (GetAvailableActions().Contains(FooAction.Baz)) }

    public IEnumerable<FooAction> GetAvailableActions() { }

I could also do this with boolean functions:

public class Foo
    public void Bar() { if (CanBar()) }
    public void Baz() { if (CanBaz()) }

    public bool CanBar() { }
    public bool CanBaz() { }

I’ve tried to come up with a reason to favour one over the other, but I can only think of a possible performance benefit depending on how much input data these methods would have in common. And likely said performance benefit would be negligible.

Are there any real-world problems that could occur with one solution and not with the other? Or does the whole thing boil down to personal preference?

Concrete example

public class Patient
    public IEnumerable<Prescription> Prescriptions { get; set; }

public class Prescription
    public IEnumerable<Administration> Administrations { get; set; }

public class Administration
    public void Administer(string foo, int bar) { }

    public void Prepare(string baz, bool bat) { }
  • Prepare(...) may only be called if Prepare(...) has not yet been called before
  • Administer(...) may not be called if the Prescription has an unadministered Administration scheduled at an earlier point in time.
  • Administer(...) may not be called if the Administration depends on the weight of the Patient, and the weight of the Patient is unknown.

These rules can be very simple and very complex. In the client, a user can click an ‘Administer’ button, fill in a form and click a ‘Confirm’ button. I don’t want to let the user click the ‘Administer’ button to open the form, if these pre-conditions indicate that the action will fail regardless of data entered into the form.

I’ve added the domain-driven-design and cqrs tags because it’s in the context of a DDD/CQRS architecture, but I’m not sure if that matters for this problem.


Since you’re doing it with CQRS in mind, I think your classes should look more like

Command class: encapsulates the command parameters

 class Administer
       public Administer(string foo, int bar)
       public string Foo {get; private set;}
       public int Bar {get; private set;}

Command Handler: pass the command to the specialized types, don’t do any validation here

class HandleAdminister
   private readonly Administration _administration;
   public   HandleAdminister(Administration administration)
      _administration = administration;
   public void Handle(Administer command)
       administration.Administer(command.Foo, command.Bar);

Command Validator: validation is a separate concern, we create a type to handle validation only

class ValidateAdminister
  private readonly IReadEntities _readEntities;
  public ValidateAdminister(IReadEntities readEntities)
     _readEntities = readEntities;
  public bool Validate(Administer command)
     ... heavy or light validation here
     var unadministered = _readEntities
               .Any(c=>c.Unadministered && c.Foo == command.Foo);
     return !unadministered;

Client code to execute a command:

var administration = ...
var command = new Administer("foo", 19);
var handler = new HandleAdminister(administration);
var validator = new ValidateAdminister(...);

if (validator.Validate(command))

A possible relation between a ViewModel and validators

class PatientViewModel
   public bool CanAdminister {get; set;}
   public bool CanPrepare {get; set;}
   public Patient Patient {get; set;}

var model = new PatientViewModel();
model.Patient = ...;
model.CanAdminister = administerCommandValidator.Validate(administerCommand);
model.CanPrepare = prepareCommnadValidator.Validate(prepareCommand);

The point is you can use the validator to validate the command before executing it and also to build a model for the UI.


To be honest, I don’t think I’d recommend what follows as a good solution for the actual design problem you have at hands (judge for yourself), but if only for the sake of staying close to your initial idea, there’s also this other pattern based on option flags that you can express quite easily in C#:

public enum Actions
    // (Powers of 2 bitmask)
    Bar = 1,
    Baz = 2,
    Gee = 4,
    Bip = 8

public class Foo
    private bool CanDo(Actions actions) { return (FeasibleActions & actions) != 0; }

    private string Self { get { return GetType().Name; } }

    protected virtual void DoBar() { Console.WriteLine("{0}: Bar!", Self); }

    protected virtual void DoBaz() { Console.WriteLine("{0}: Baz!", Self); }

    protected virtual void DoGee() { Console.WriteLine("{0}: Gee!", Self); }

    protected virtual void DoBip() { Console.WriteLine("{0}: Bip!", Self); }

    public Actions FeasibleActions { get; protected set; }

    public Foo()
        // Initial feasible actions for Foo
        FeasibleActions = Actions.Bar | Actions.Baz;

    public void Bar() { if (CanDo(Actions.Bar)) { DoBar(); } /* else { throw some exception }... */ }

    public void Baz() { if (CanDo(Actions.Baz)) { DoBaz(); } /* else { throw some exception }... */ }

    public void Gee() { if (CanDo(Actions.Gee)) { DoGee(); } /* else { throw some exception }... */ }

    public void Bip() { if (CanDo(Actions.Bip)) { DoBip(); } /* else { throw some exception }... */ }

public class Acme : Foo
    protected override void DoBaz() { base.DoBaz(); FeasibleActions |= Actions.Bip; }

    public Acme() //(implicit : base())
        // Initial feasible actions for Acme: same as Foo's, plus Gee
        FeasibleActions |= Actions.Gee;


        var foo = new Foo();
        var acme = new Acme();

        foo.Bar(); // "Bar!"
        foo.Bip(); // (does nothing or throws an exception)
        foo.Baz(); // "Baz!"
        foo.Gee(); // (does nothing or throws an exception)
        foo.Bip(); // (does nothing or still throws an exception)

        acme.Bar(); // "Bar!"
        acme.Bip(); // (does nothing or throws an exception)
        acme.Baz(); // "Baz!"
        acme.Gee(); // "Gee!"
        acme.Bip(); // "Bip!" (thanks to prior call to Baz)


Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *