7

I recently start to work on an application that drive different measurement device.

Before the user start a measure, she sets the parameters of it.

Actually, considering all measurements type there are 50+ parameters.

The difficulty here is that every settings depends on others for:

  • Being available
  • List of available values
  • so on..

Moreover, some measurement settings depend on previous measure results and settings.

To make it short : we have a lot of stuff that are interconnected.

The actual pattern is to validate everything as soon as a values is changed. It cost a lot (in time) and we are going to add a lot of more parameters : it will break.

We try to implement a pattern where we use ObservableValues and where all parameters register on all values it depend on.

It became hard when the parameters depend on an other reference measure. If the reference change, we have to stop listening on the previous reference and start to listen on the new reference.

Etc...

An other issue is that when we work on our pattern and we had more capabilities (like serialization), or when we had some helper class (like factory), we build big files with 50+ parameters or functions.

Is there any other good pattern or library to do it ?

  • When you said 50+ parameters, did you imply by various properties or fields in a particular class. – Karan Jun 11 '15 at 7:53
  • Actually the driver receive a class where there are like 5/6 properties with properties inside, etc.. but the model we use to store measure history is a list of MeasureParameters where all parameters are stored flat. So yes there are 50+ properties in it... and they are all struct/nullable struct. – Orace Jun 11 '15 at 8:00
  • I believe all these 50+ properties would have been mapped to 50+ columns in the DB table. – Karan Jun 11 '15 at 8:35
  • I think you can try the C# ExpandoObject to add properties dynamically at runtime. stackoverflow.com/questions/15819720/… – Karan Jun 11 '15 at 8:42
  • Thanks Karan for your responses, we do not have DB in our application it's just a hardware driving software where we build/save/open a list of measure parameters then use it on the hardware. Indeed it's a C# software. The ExpandoObject safety is not strong enough. We would like to have a strongly typed code. – Orace Jun 11 '15 at 8:53
1
+100

I would suggest:

  • divide and conquer the problem
  • use components and keep each component focused on its main purpose
  • decouple you system by using an event aggregator (EA)
  • use interfaces where an EA doesn’t make sense

I would try to split the big problem in smaller problems and then try to solve them by its own. The smaller problems can be solved easier by specialized components, if they are simple enough. Each component should keep its focus on its main purpose.

  • To keep a loose coupling around components I would use an event aggregator (EA) (main purpose: notify listeners). (e.g.: Reactive Extensions, Caliburn Micro, or the Prism EventAggregator, if in .NET)
  • I would use simple parameter classes to keep values (main purpose: handle values).
  • I would group parameters in a some kind of tree class (main purpose: provide parameters).
  • The tree has to be build or updated. For this I would use one tree-builder component (main purpose: build/update tree).
  • The tree-builder needs to be notified when to react -> EA notification
  • Some consumer of the tree needs to be notified after the tree has changed -> EA notification
  • ...

If the components are not coupled tightly, then the system can be scaled or changed when necessary. I.e.: If the building process is far too complex, then it is possible to use multiple tree-builder classes instead one. To achive this, an additional component - a tree-builder-factory - would be added. It would react on tree build/update events by providing the adequate tree-builder.

3

Create a Parameter class (assuming C#): class Parameter<T> { T Value; string Name; }

Then build a graph with nodes containing Parameter values and dependencies as their references in the same graph.

And next do all the operations on the graph as a whole, not directly on parameters: graph.SetValue(parameter, value) or graph.SetValue(parameterName, value)

  • 1
    But how could we access the properties of the class like a strongly typed object. – Karan Jun 15 '15 at 10:04
  • If I understand you correctly you're asking about the SetValue(), GetValue(), etc. methods: they can be made strongly typed using templates: SetValue<T>(string parameterName, T val) – Roland Jun 15 '15 at 11:10
  • Getting back to this question 2 years later. We now are looking for a solution that's look like this. – Orace Apr 9 '18 at 11:39
1

You could consider using a mediator to decouple parameter event dependencies. This is sometimes known as an Event Aggregator. Your individual parameter classes are wired up to publish and subscribe to events directly through the EA.

The following example uses Reactive Extensions but you could implement a simple version yourself using callbacks for subscription and publishing.

public class EventAggregator
{
    private Subject<Event> _events;
    public IObservable<Event> Events { get { return _events; } }

    public void Publish(Event evt)
    {
        _events.OnNext(evt);
    }
}

public class Parameter<T> : IDisposable
{
    private readonly EventAggregator _ea;
    public Parameter(EventAggregator ea, string name, T initialValue)
    {
        this._ea = ea;
        this._name = name;
        this._value = initialValue;
        this._subscription = ea.Events.Subscribe(evt => HandleEvent(evt));
    }

    private readonly string _name;
    public string Name { get { return this._name; } }

    private T _value;
    public T Value
    {
        get { return this._value; }
        set
        {
            this._value = value;
            this._ea.Publish(new ValueChangedEvent(this._name, this._value);
        }
    }

    private void HandleEvent(Event evt)
    {
        // decide what to do with an event here - change availability of the parameter etc
    }

    public void Dispose()
    {
        this._subscription.Dispose();
    }
}

You create a single EventAggregator and pass it as a dependency to your Parameter objects:

var eventAggregator = new EventAggregator();
var length = new Parameter<int>(eventAggregator, "Length", 50);
var width = new Parameter<int>(eventAggregator, "Width", 50);

// this will fire an event to the event aggregator which can be handled by other parameters
width.Value = 20; 

// this unsubscribes from the event aggregator
length.Dispose(); 
0

There are tools to help you manage the complexity; see for example the Feature Model approach.

However, if I had to do it, I'd try to go for a constraint programming approach (feature trees are basically a simplified notation for constraints). Since you are using C#, I am going to point you to Prolog.NET. Note however that even though I already used Prolog, I don't know Prolog.Net specifically.

Note: I will edit this answer later with an example, if possible (I don't have much time now). By the way, I'd rather use one of your example, if you could provide some.

0

One more idea:

Keep all your parameters in a big map like:

class Params
{
   Map<String, Object> map;

   /* fancy helper methods... */
}

Add a set of rule objects:

interface Rule
{
  /* returns "OK" or an error message
   * or use exceptions or whatever you fancy */
  void validate(Params params); 

  /* returns the list of params that will trigger the rule */
  String[] triggers();
}

class FancyRule implements Rule
{
  String validate(Params params) {
     if( params.get("foo") <= params.get("bar") )
        return "foo should be greater than bar";
     else
        return "OK";
  }
  String[] triggers() {
     return ["foo", "bar"];
  }
}

As last step, you just need to bundle things together:

class Params
{
   Map<String, Object> map;

   void setParam(key,value) {
      map.put(key,value); // + keep a backup if desired

      // now validate everything depending on it
      for( rule in rules having key as trigger):
           if( rule.validate(this) != "OK" )
                // ...
   }
}
0

There are many different ways to approach the updating of such interconnected parameters; I have prior experience with two: For the purposes of this answer I will call them the "centralized" approach and the "decentralized" approach.

In the centralized approach, you have a single "UpdateEverything" function which you have to make sure to invoke every single time you do anything in your entire system which has even the slightest chance of requiring an update. The advantage of the centralized approach is that it is simple: all the code which implements the updating is in one place, so you can read through it to make sure it is correct. The disadvantages of the centralized approach are that a) the "UpdateEverything" function usually ends up being monstrously long, b) it does all of the updating every single time it gets invoked even though only a few things may have changed and therefore only a few things may really needed to be updated, and c) it tends to be invoked excessively, just so as to reduce the chances of missing an update, so overall, it represents a huge unnecessary overhead.

The decentralized approach is what you have already said you have started working on; it is a lot more involved and it actually requires building some framework before it can be used. In this approach, each parameter is capable of triggering a "changed" notification, and each parameter has knowledge of every single one of the parameters that it depends on, so that it can register with them to be notified when they change, and thus recompute its own value, possibly triggering its own "changed" notification in turn. The decentralized approach solves all of the problems of the centralized approach, but it is more elaborate and therefore more costly to implement.

A few points to note:

  1. You will need to introduce and primarily make use of some "ReadonlyParameter" interface, which has a "GetValue()" and a "RegisterForChangedEvent()", but no "SetValue()", because it makes no sense to set the value of a parameter which is built to recompute its own value based on the values of other parameters. "read-only" in this context does not mean that its value may never change, it just means that you may not change its value, though its value may still change due to other reasons, so it makes sense to register with it in order to observe any such changes.

  2. For those parameters that receive values from the outside, you may need to introduce a "StoringParameter" class which "stores" (contains) its own value, implementing the "ReadonlyParameter" interface and also offering a "SetValue()" method.

  3. A parameter that knows how to compute its own value will need to cache that value, so that a) it can respond to "GetValue()" without costly recalculations, and b) it only issues a "Changed" notification when its cached value changes, so as to eliminate unnecessary updates.

  4. A parameter that knows how to compute its own value will generally have a private RecomputeOwnValue() method which gets invoked a) whenever one of the parameters it depends on changes, b) once at the end of its own constructor, so as to compute its initial value. If there is a possibility that a parameter might need to stop depending on a particular parameter and instead start depending on another parameter, then you will need to also invoke RecomputeOwnValue() whenever a dependent parameter is added/removed.

  5. You can create an entire algebra of parameters for frequently used operations. For example, you can introduce logical AND, OR, XOR etc parameter classes, which know how to recompute their own values as the result of a logical operation on the values of two other boolean parameters that they depend on, you can introduce arithmetic operation parameter classes, doing addition, subtraction, multiplication, division etc., and so on.

  6. You can write simple helper functions that instantiate interconnected parameters in a way that is as convenient as writing expressions. So, you might have something like this:

    d = fob( a, fiddle( b, c ) ) );

Where fob() creates a FobParameter object, and fiddle() creates a FiddleParameter object, so the above expression means that parameter d is a new FobParameter() which happens to depend on two instances of ReadonlyParameter, for which we pass pre-existing parameter a and a new FiddleParameter(), which in turn happens to depend on another two instances of ReadonlyParameter, for which we pass pre-existing parameters b and c.

protected by gnat Jun 16 '15 at 11:46

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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