1

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
{
    Bar,
    Baz
}

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 and tags because it's in the context of a DDD/CQRS architecture, but I'm not sure if that matters for this problem.

  • Is the list of available actions unchanging and finite? – MetaFight Aug 16 '16 at 12:38
  • @MetaFight It grows or shrinks depending on a subset of the methods on the object. If I add a method Bat(), it's possible that a FooAction.Bat or CanBat() is needed. But we can assume that the list will change rarely. – Stijn Aug 16 '16 at 12:43
  • It sounds like you may have a modelling issue then. Would it make sense for you to only implement these methods (and their CanExecute logic) in types derived from Foo? For example, only a BatFoo has the Bat method. – MetaFight Aug 16 '16 at 13:07
  • @MetaFight It's not an 'always available' or 'always unavailable' scenario, it depends on the state of the system. What I'm trying to expose to the client is something along the lines of "no matter which parameters you send to Bar(), at this point in time the call will fail because the state of the system tells us that ...". The method call may very well succeed at a later point, when the state of the system has changed. – Stijn Aug 16 '16 at 13:21
  • Hrm, some more concrete examples might help elicit some answers. – MetaFight Aug 16 '16 at 13:28
2

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
               .Query<Prescription>()
               .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))
    handler.Handle(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.

  • I appreciate the answer, it's similar to the code already in place today, so at the very least it validates the design. But what's still missing is a way to provide the client with an "early exit" mechanism. To translate the last paragraph in the question into a CQRS context: I don't want the client to even begin setting up an Administer command object if I can determine that validator.Validate(command) will return false regardless of the content of the Administer command. – Stijn Aug 16 '16 at 15:00
  • If the validation doesn't depend on the client input (Foo and Bar values) then you can use the same validator class to show/hide UI buttons on UI load. – Adrian Iftode Aug 16 '16 at 15:14
  • You pass the system state in the validator class. – Adrian Iftode Aug 16 '16 at 15:15
  • I added the interaction between the viewmodel and the command validator – Adrian Iftode Aug 16 '16 at 21:49
0

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;
    }
}

then:

        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)

'HTH,

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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