Design patterns (see relevant paragraph in the page Design summary)are elegant and effective solutions to common programming problems; they describe how objects communicate without knowing each other’s data and methods. The design patterns strategy allows offering to the user of the component different models (one or more algorithms) by encapsulating each of them in a class. Different algorithms, which are alternate options to do the same thing, are called strategies. When building a biophysical model component this allows in principle to offer alternate options to estimate a variable or, more in general, to model a process. This often needed feature in the implementation of biophysical models, if implemented using the design pattern Strategy, comes with two very welcomed effects from the software side: 1) it allows an easier maintenance of the component, by facilitating adding other algorithms, 2) it allows to add easily further algorithms from the client side, without the need of recompiling the component, but keeping the same interface and the same call.
A class implementing the interface inheriting from the IStrategy is a strategy of the UNIMI.Suitability component. Many of such classes are implemented in the component, but a client of the component may extend the modelling capabilities by adding models which use as inputs and outputs the ones in the Domain Classes (which can also be extended).
A commented example of such classes follows. The class below is currently implemented as an example, but it applies to all components, with small changes in the relevant part of the code. Note that the grey colored statements represent blocks of code. (#region -#endregion code blocks), and are discussed after the first code block.
Accessors to lists of parameters, inputs, outputs, and asociated strategies. This code block is the same for all strategies and of all components. |
#region Lists: Inputs, Outputs, Parameters, Associated Strategies private static readonly List<string> _inputs = new List<string>(); /// <summary>List of inputs within Suitability</summary> public static List<string> Inputs { get { return _inputs; } } private static readonly List<string> _outputs = new List<string>(); /// <summary>List of outputs within Suitability</summary> public static List<string> Outputs { get { return _outputs; } } private static readonly List<string> _parameters = new List<string>(); /// <summary>List of parameters (static properties of this class)</summary> public static List<string> Parameters { get { return _parameters; } } private static readonly List<string> _associatedStrategies = new List<string>(); /// <summary>List of associated strategies</summary> public static List<string> AssociatedStrategies { get { return _associatedStrategies; } } #endregion |
Implementation of the IStrategyComponent methods |
#region IStrategySuitability Members /// <summary> /// Set parameter(s) to the default value /// </summary> public void SetParametersDefaultValue() { _PercentageNeighbouringThresholdForDistrictEffect = _PercentageNeighbouringThresholdForDistrictEffectVarInfo.DefaultValue; _RelativePercentageIncreaseDueToNeighbouring = _RelativePercentageIncreaseDueToNeighbouringVarInfo.DefaultValue; _PercentageThresholdForCropPresence = _PercentageThresholdForCropPresenceVarInfo.DefaultValue; } /// <summary> /// Test to verify the postconditions /// </summary> /// <param name="c">Climate data</param> /// <param name="s">Soil data</param> /// <param name="wb">Water balance data</param> /// <param name="cr">Crop data</param> /// <param name="callID">Client ID for specific call</param> /// <returns>Result of post-conditions test</returns> public string TestPostConditions(Climate c, Soil s, WaterBalance wb, Crop cr, string callID) { try { return PostConditions(c, s, wb, cr, callID); } catch (Exception e) { TraceStrategies.TraceEvent(System.Diagnostics.TraceEventType.Error, 1001, "Strategy: " + _strategyName + " - Unhandled exception running post-conditions"); string msg = "Component Suitability, " + _strategyName + ": Unhandled exception running post-condition test. "; throw new Exception(msg, e); } } /// <summary> /// Test to verify the preconditions /// </summary> /// <param name="c">Climate data</param> /// <param name="s">Soil data</param> /// <param name="wb">Water balance data</param> /// <param name="cr">Crop data</param> /// <param name="callID">Client ID for specific call</param> /// <returns>Result of pre-conditions test</returns> public string TestPreConditions(Climate c, Soil s, WaterBalance wb, Crop cr, string callID) { try { return PreConditions(c, s, wb, cr, callID); } catch (Exception e) { TraceStrategies.TraceEvent(System.Diagnostics.TraceEventType.Error, 1002, "Strategy: " + _strategyName + " - Unhandled exception running pre-conditions"); string msg = "Component Suitability, " + _strategyName + ": Unhandled exception running pre-condition test. "; throw new Exception(msg, e); } } /// <summary> /// Calculate the output(s) of the selected model /// </summary> /// <param name="c">Climate data</param> /// <param name="s">Soil data</param> /// <param name="wb">Water balance data</param> /// <param name="cr">Crop data</param> public void Update(Climate c, Soil s, WaterBalance wb, Crop cr) { try { ModelEstimate(c, s, wb, cr); TraceStrategies.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 1005, "Strategy: " + _strategyName + " - Model executed"); } catch (Exception e) { TraceStrategies.TraceEvent(System.Diagnostics.TraceEventType.Error, 1003, "Strategy: " + _strategyName + " - Unhandled exception running model"); string msg = "Component Suitability, " + _strategyName + ": Pre-conditions violated. "; msg = msg + TestPreConditions(c, s, wb, cr, String.Empty); throw new Exception(msg, e); } } #endregion |
Example Title |
#region Model private void ModelEstimate(Climate c, Soil s, WaterBalance wb, Crop cr) { double _ThresholdHigh = _PercentageNeighbouringThresholdForDistrictEffect; double _ThresholdLow = 1 - _PercentageNeighbouringThresholdForDistrictEffect; double _RelativeIncrease = 1+_RelativePercentageIncreaseDueToNeighbouring; double _RelativeDecrease = 1 - _RelativePercentageIncreaseDueToNeighbouring; double presence = cr.PercentageOfCropPresence; if (cr.NeighbouringAreaPresence > _ThresholdHigh) { cr.PercentageOfCropPresence = presence*_RelativeIncrease; } else if (cr.NeighbouringAreaPresence < _ThresholdLow) { cr.PercentageOfCropPresence = presence*_RelativeDecrease; } if (cr.PercentageOfCropPresence < 0) { cr.PercentageOfCropPresence = 0; } if (cr.PercentageOfCropPresence > 1) { cr.PercentageOfCropPresence = 1; } if (cr.PercentageOfCropPresence > _PercentageThresholdForCropPresence) { cr.IsTheCropPresent = true; } } #endregion |
Pre- and Post-conditions |
#region Pre- post- conditions private string PreConditions(Climate c, Soil s, WaterBalance wb, Crop cr, string callID) { SetParametersInputsCurrentValue(c, s, wb, cr); PreconditionsData prc = new PreconditionsData(); Preconditions pre = new Preconditions(); prc.RangeBased.Add(_PercentageNeighbouringThresholdForDistrictEffectVarInfo); prc.RangeBased.Add(_RelativePercentageIncreaseDueToNeighbouringVarInfo); prc.RangeBased.Add(_PercentageThresholdForCropPresenceVarInfo); prc.RangeBased.Add(CropVarInfo.PercentageOfCropPresence); prc.RangeBased.Add(CropVarInfo.NeighbouringAreaPresence); return pre.VerifyPreconditions(prc, callID); } private string PostConditions(Climate c, Soil s, WaterBalance wb, Crop cr, string callID) { SetOutputsCurrentValue(c, s, wb, cr); PreconditionsData prc = new PreconditionsData(); Preconditions pre = new Preconditions(); prc.RangeBased.Add(CropVarInfo.PercentageOfCropPresence); return pre.VerifyPostconditions(prc, callID); } #endregion |
Set current value prior to the test of pre- post-conditions |
#region Set inputs and parameters current values private void SetParametersInputsCurrentValue(Climate c, Soil s, WaterBalance wb, Crop cr) { //Parameters _PercentageNeighbouringThresholdForDistrictEffectVarInfo.CurrentValue = _PercentageNeighbouringThresholdForDistrictEffect; _RelativePercentageIncreaseDueToNeighbouringVarInfo.CurrentValue = _RelativePercentageIncreaseDueToNeighbouring; _PercentageThresholdForCropPresenceVarInfo.CurrentValue = _PercentageThresholdForCropPresence; //inputs CropVarInfo.PercentageOfCropPresence.CurrentValue = cr.PercentageOfCropPresence; CropVarInfo.NeighbouringAreaPresence.CurrentValue = cr.NeighbouringAreaPresence; } private static void SetOutputsCurrentValue(Climate c, Soil s, WaterBalance wb, Crop cr) { //Outputs CropVarInfo.PercentageOfCropPresence.CurrentValue = cr.PercentageOfCropPresence; } #endregion |
Strategy class constructor |
#region Constructor methods private static void SetParameterDescription() { _PercentageNeighbouringThresholdForDistrictEffectVarInfo.Name = "PercentageNeighbouringThresholdForDistrictEffect"; _PercentageNeighbouringThresholdForDistrictEffectVarInfo.Description = "Percentage neighbouring threshold for district effect (above the threshold lead to an increase in percentage)"; _PercentageNeighbouringThresholdForDistrictEffectVarInfo.MaxValue = 1; _PercentageNeighbouringThresholdForDistrictEffectVarInfo.MinValue = 0; _PercentageNeighbouringThresholdForDistrictEffectVarInfo.DefaultValue = 0.75; _PercentageNeighbouringThresholdForDistrictEffectVarInfo.Units = "unitless"; _PercentageNeighbouringThresholdForDistrictEffectVarInfo.URL = ""; _RelativePercentageIncreaseDueToNeighbouringVarInfo.Name = "RelativePercentageIncreaseDueToNeighbouring"; _RelativePercentageIncreaseDueToNeighbouringVarInfo.Description = "RelativePercentageIncreaseDueToNeighbouring"; _RelativePercentageIncreaseDueToNeighbouringVarInfo.MaxValue = 1; _RelativePercentageIncreaseDueToNeighbouringVarInfo.MinValue = 0; _RelativePercentageIncreaseDueToNeighbouringVarInfo.DefaultValue = 0.1; _RelativePercentageIncreaseDueToNeighbouringVarInfo.Units = "unitless"; _RelativePercentageIncreaseDueToNeighbouringVarInfo.URL = ""; _PercentageThresholdForCropPresenceVarInfo.Name = "PercentageThresholdForCropPresence"; _PercentageThresholdForCropPresenceVarInfo.Description = "Percentage threshold for crop presence"; _PercentageThresholdForCropPresenceVarInfo.MaxValue = 1; _PercentageThresholdForCropPresenceVarInfo.MinValue = 0; _PercentageThresholdForCropPresenceVarInfo.DefaultValue = 0.1; _PercentageThresholdForCropPresenceVarInfo.Units = "unitless"; _PercentageThresholdForCropPresenceVarInfo.URL = ""; } private static void FillInOutParLists() { //inputs _inputs.Add(CropVarInfo.PercentageOfCropPresence.Name); _inputs.Add(CropVarInfo.NeighbouringAreaPresence.Name); //outputs _outputs.Add(CropVarInfo.PercentageOfCropPresence.Name); //parameters _parameters.Add(_PercentageNeighbouringThresholdForDistrictEffectVarInfo.Name); _parameters.Add(_RelativePercentageIncreaseDueToNeighbouringVarInfo.Name); _parameters.Add(_PercentageThresholdForCropPresenceVarInfo.Name); } #endregion |
IStrategy Members |
#region IStrategy Members /// <summary> /// Description of the model /// </summary> public string Description { get { return "Apply a correction to the percentage of presence estiamted for e certain crop based on the idea that crops distribution could be affected by the tendency to create production districts. - R. Confalonieri."; } } /// <summary> /// URL to access description of the model /// </summary> public string URL { get { return "http://"; } } #endregion |
Created with the Personal Edition of HelpNDoc: Free EPub and documentation generator